mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-12 03:46:22 +00:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f26ad8787 | ||
|
|
b08ad82518 | ||
|
|
5319f063d6 | ||
|
|
63f7ceecbe | ||
|
|
770420bdad | ||
|
|
4994a7d694 | ||
|
|
c55ee2a815 | ||
|
|
312651f68e | ||
|
|
654f411a5b | ||
|
|
68b862da49 | ||
|
|
8f2b13e864 | ||
|
|
bb564f96a3 | ||
|
|
a555f24ded | ||
|
|
10ae321bf3 | ||
|
|
6e8ff0c2b7 | ||
|
|
923bda07a2 | ||
|
|
4c34ee5eab |
@@ -25,7 +25,7 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }}
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ (github.ref_name == 'v2' && (inputs.version || inputs.bump) && 'release') || inputs.version || inputs.bump }}
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -33,7 +33,7 @@ permissions:
|
||||
packages: write
|
||||
|
||||
env:
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'v2' && 'dev') || '' }}
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'v2' && !inputs.bump && !inputs.version && 'dev') || '' }}
|
||||
|
||||
jobs:
|
||||
version:
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
fi
|
||||
|
||||
found=0
|
||||
for file in packages/cli/dist/cli-darwin-*/bin/opencode2; do
|
||||
for file in packages/cli/dist/cli-darwin-*/bin/opencode; do
|
||||
if [ ! -f "$file" ]; then
|
||||
continue
|
||||
fi
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: opencode-preview-cli
|
||||
name: opencode-preview-cli-macos
|
||||
path: packages/cli/dist/cli-*
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
needs: version
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
if: github.repository == 'anomalyco/opencode'
|
||||
if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version))
|
||||
steps:
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
needs:
|
||||
- version
|
||||
- build-node-app-archive
|
||||
if: github.repository == 'anomalyco/opencode'
|
||||
if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version))
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -276,10 +276,9 @@ jobs:
|
||||
|
||||
sign-cli-windows:
|
||||
needs:
|
||||
- build-cli
|
||||
- version
|
||||
- sign-cli-macos
|
||||
runs-on: blacksmith-4vcpu-windows-2025
|
||||
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2' && github.ref_name != 'beta'
|
||||
if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'v2' || github.ref_name == 'beta')
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -292,15 +291,8 @@ jobs:
|
||||
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: opencode-cli-windows
|
||||
path: packages/opencode/dist
|
||||
|
||||
- name: Setup git committer
|
||||
id: committer
|
||||
uses: ./.github/actions/setup-git-committer
|
||||
with:
|
||||
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
|
||||
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
|
||||
name: opencode-preview-cli-macos
|
||||
path: packages/cli/dist
|
||||
|
||||
- name: Azure login
|
||||
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
|
||||
@@ -315,9 +307,9 @@ jobs:
|
||||
signing-account-name: ${{ env.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
||||
certificate-profile-name: ${{ env.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}
|
||||
files: |
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe
|
||||
exclude-environment-credential: true
|
||||
exclude-workload-identity-credential: true
|
||||
exclude-managed-identity-credential: true
|
||||
@@ -333,9 +325,9 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
$files = @(
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe"
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe"
|
||||
)
|
||||
|
||||
foreach ($file in $files) {
|
||||
@@ -345,40 +337,17 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
- name: Repack Windows CLI archives
|
||||
working-directory: packages/opencode/dist
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive -Path "opencode-windows-arm64\bin\*" -DestinationPath "opencode-windows-arm64.zip" -Force
|
||||
Compress-Archive -Path "opencode-windows-x64\bin\*" -DestinationPath "opencode-windows-x64.zip" -Force
|
||||
Compress-Archive -Path "opencode-windows-x64-baseline\bin\*" -DestinationPath "opencode-windows-x64-baseline.zip" -Force
|
||||
|
||||
- name: Upload signed Windows CLI release assets
|
||||
if: needs.version.outputs.release != ''
|
||||
shell: pwsh
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
||||
run: |
|
||||
gh release upload "v${{ needs.version.outputs.version }}" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64.zip" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64.zip" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline.zip" `
|
||||
--clobber `
|
||||
--repo "${{ needs.version.outputs.repo }}"
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: opencode-cli-signed-windows
|
||||
path: |
|
||||
packages/opencode/dist/opencode-windows-arm64
|
||||
packages/opencode/dist/opencode-windows-x64
|
||||
packages/opencode/dist/opencode-windows-x64-baseline
|
||||
name: opencode-preview-cli
|
||||
path: packages/cli/dist/cli-*
|
||||
if-no-files-found: error
|
||||
|
||||
build-electron:
|
||||
needs:
|
||||
- version
|
||||
- sign-cli-macos
|
||||
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2'
|
||||
- sign-cli-windows
|
||||
if: github.repository == 'anomalyco/opencode' && (github.ref_name != 'v2' || needs.version.outputs.release != '')
|
||||
continue-on-error: false
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
## Live V2 TUI Testing
|
||||
|
||||
- Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode2` background server and live sessions.
|
||||
- Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode` background server and live sessions.
|
||||
- Pass a directory after the script when needed, for example `bun run dev:live /path/to/project`.
|
||||
- The script discovers the server with `opencode2 service status`, injects its private local credential from `opencode2 service get password`, and uses the `dev` TUI storage channel so tabs and other client-local state match the installed client.
|
||||
- The script discovers the server with `opencode service status`, injects its private local credential from `opencode service get password`, and uses the `dev` TUI storage channel so tabs and other client-local state match the installed client.
|
||||
- Prefer `dev:live` over plain `bun run dev` for this workflow. An implicit managed-service connection may replace the live server when the worktree client version differs; explicit `--server` warns and continues without replacing it.
|
||||
|
||||
## V2 TUI Stories
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"packages/ai": {
|
||||
"name": "@opencode/ai",
|
||||
"version": "1.17.20",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "3.1057.0",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -53,7 +53,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode/app",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode/cli",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"bin": {
|
||||
"opencode2": "./bin/opencode2.cjs",
|
||||
},
|
||||
@@ -175,7 +175,7 @@
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@opencode/client",
|
||||
"version": "1.17.13",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/protocol": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -201,7 +201,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode/codemode",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -215,7 +215,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode/console-app",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -251,7 +251,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode/console-core",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -278,7 +278,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode/console-function",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -295,7 +295,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode/console-mail",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -319,7 +319,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode/console-support",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -339,7 +339,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode/core",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
@@ -411,7 +411,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode/desktop",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"electron-context-menu": "4.1.2",
|
||||
@@ -463,7 +463,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode/enterprise",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/sdk": "1.18.21",
|
||||
@@ -500,7 +500,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode/function",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -516,7 +516,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode/http-recorder",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@effect/platform-node-shared": "4.0.0-rc.112",
|
||||
},
|
||||
@@ -535,7 +535,7 @@
|
||||
},
|
||||
"packages/httpapi-codegen": {
|
||||
"name": "@opencode/httpapi-codegen",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
"prettier": "3.6.2",
|
||||
@@ -548,7 +548,7 @@
|
||||
},
|
||||
"packages/latex": {
|
||||
"name": "@opencode/latex",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -562,7 +562,7 @@
|
||||
},
|
||||
"packages/merman": {
|
||||
"name": "@opencode/merman",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -577,7 +577,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode/plugin",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode/ai": "workspace:*",
|
||||
@@ -616,7 +616,7 @@
|
||||
},
|
||||
"packages/plugin-browser": {
|
||||
"name": "@opencode/plugin-browser",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -646,7 +646,7 @@
|
||||
},
|
||||
"packages/protocol": {
|
||||
"name": "@opencode/protocol",
|
||||
"version": "1.17.11",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/schema": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
@@ -661,7 +661,7 @@
|
||||
},
|
||||
"packages/schema": {
|
||||
"name": "@opencode/schema",
|
||||
"version": "1.17.11",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -685,7 +685,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@opencode/sdk",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -706,7 +706,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode/server",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@effect/platform-node-shared": "catalog:",
|
||||
@@ -728,7 +728,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode/session-ui",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode/client": "workspace:*",
|
||||
@@ -763,7 +763,7 @@
|
||||
},
|
||||
"packages/simulation": {
|
||||
"name": "@opencode/simulation",
|
||||
"version": "1.17.13",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/ai": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -783,7 +783,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode/stats-app",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -817,7 +817,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode/stats-core",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -836,7 +836,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode/stats-server",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -882,7 +882,7 @@
|
||||
},
|
||||
"packages/theme": {
|
||||
"name": "@opencode/theme",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opentui/core": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -896,7 +896,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode/tui",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -931,7 +931,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode/ui",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -966,7 +966,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode/util",
|
||||
"version": "1.18.3",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@effect/opentelemetry": "catalog:",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -999,7 +999,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode/web",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1040,7 +1040,7 @@
|
||||
},
|
||||
"services/update": {
|
||||
"name": "@opencode/update",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"jose": "6.0.11",
|
||||
"semver": "catalog:",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
APP=opencode2
|
||||
APP=opencode
|
||||
SOURCE_APP=opencode
|
||||
|
||||
MUTED='\033[0;2m'
|
||||
RED='\033[0;31m'
|
||||
@@ -22,7 +23,7 @@ Options:
|
||||
Examples:
|
||||
curl -fsSL https://opencode.ai/v2/install | bash
|
||||
curl -fsSL https://opencode.ai/v2/install | bash -s -- --version 0.0.0-beta-17236
|
||||
./install --binary /path/to/opencode2
|
||||
./install --binary /path/to/opencode
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -201,9 +202,9 @@ else
|
||||
|
||||
filename="cli-$target-$specific_version.tgz"
|
||||
url="https://registry.npmjs.org/$package_name/-/$filename"
|
||||
binary_name="$APP"
|
||||
binary_name="$SOURCE_APP"
|
||||
if [ "$os" = "windows" ]; then
|
||||
binary_name="$APP.exe"
|
||||
binary_name="$SOURCE_APP.exe"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -230,12 +231,7 @@ check_version() {
|
||||
installed_version="${installed_version##* }"
|
||||
installed_version="${installed_version#v}"
|
||||
|
||||
if [[ "$installed_version" != "$specific_version" ]]; then
|
||||
print_message info "${MUTED}Installed version: ${NC}$installed_version."
|
||||
else
|
||||
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
|
||||
exit 0
|
||||
fi
|
||||
print_message info "${MUTED}Installed version: ${NC}$installed_version."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -340,15 +336,46 @@ download_and_install() {
|
||||
fi
|
||||
|
||||
tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
|
||||
mv "$tmp_dir/package/bin/$binary_name" "$INSTALL_DIR"
|
||||
chmod 755 "${INSTALL_DIR}/$binary_name"
|
||||
local installed_binary="$APP"
|
||||
if [ "$os" = "windows" ]; then
|
||||
installed_binary="$APP.exe"
|
||||
fi
|
||||
mv "$tmp_dir/package/bin/$binary_name" "$INSTALL_DIR/$installed_binary"
|
||||
chmod 755 "$INSTALL_DIR/$installed_binary"
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
|
||||
install_from_binary() {
|
||||
print_message info "\n${MUTED}Installing ${NC}$APP ${MUTED}from: ${NC}$binary_path"
|
||||
cp "$binary_path" "${INSTALL_DIR}/$APP"
|
||||
chmod 755 "${INSTALL_DIR}/$APP"
|
||||
local installed_binary="$APP"
|
||||
case "$(uname -s)" in
|
||||
MINGW*|MSYS*|CYGWIN*) installed_binary="$APP.exe" ;;
|
||||
esac
|
||||
cp "$binary_path" "$INSTALL_DIR/$installed_binary"
|
||||
chmod 755 "$INSTALL_DIR/$installed_binary"
|
||||
}
|
||||
|
||||
install_legacy_shim() {
|
||||
local shim_os="${os:-}"
|
||||
if [[ -z "$shim_os" ]]; then
|
||||
case "$(uname -s)" in
|
||||
MINGW*|MSYS*|CYGWIN*) shim_os="windows" ;;
|
||||
esac
|
||||
fi
|
||||
rm -f "$INSTALL_DIR/opencode2" "$INSTALL_DIR/opencode2.exe" "$INSTALL_DIR/opencode2.cmd"
|
||||
if [[ "$shim_os" == "windows" ]]; then
|
||||
cat > "$INSTALL_DIR/opencode2.cmd" <<'EOF'
|
||||
@echo off
|
||||
"%~dp0opencode.exe" %*
|
||||
exit /b %errorlevel%
|
||||
EOF
|
||||
return
|
||||
fi
|
||||
cat > "$INSTALL_DIR/opencode2" <<'EOF'
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/opencode" "$@"
|
||||
EOF
|
||||
chmod 755 "$INSTALL_DIR/opencode2"
|
||||
}
|
||||
|
||||
if [ -n "$binary_path" ]; then
|
||||
@@ -357,6 +384,7 @@ else
|
||||
check_version
|
||||
download_and_install
|
||||
fi
|
||||
install_legacy_shim
|
||||
|
||||
|
||||
add_to_path() {
|
||||
@@ -453,7 +481,7 @@ echo -e ""
|
||||
echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
|
||||
echo -e ""
|
||||
echo -e "cd <project> ${MUTED}# Open directory${NC}"
|
||||
echo -e "opencode2 ${MUTED}# Run command${NC}"
|
||||
echo -e "opencode ${MUTED}# Run command${NC}"
|
||||
echo -e ""
|
||||
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/v2/docs"
|
||||
echo -e ""
|
||||
|
||||
+3
-3
@@ -2,15 +2,15 @@
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "opencode",
|
||||
"description": "AI-powered development tool",
|
||||
"version": "0.0.0",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.4.2",
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/cli src/index.ts",
|
||||
"dev:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode2 service get password)\" exec bun run dev \"$@\" --server \"$(opencode2 service status)\"' --",
|
||||
"dev:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev \"$@\" --server \"$(opencode service status)\"' --",
|
||||
"dev:vite": "bun run --cwd packages/cli --conditions=browser dev/vite.ts",
|
||||
"dev:vite:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode2 service get password)\" exec bun run dev:vite \"$@\" --server \"$(opencode2 service status)\"' --",
|
||||
"dev:vite:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev:vite \"$@\" --server \"$(opencode service status)\"' --",
|
||||
"dev:desktop": "bun --cwd packages/desktop dev",
|
||||
"dev:web": "bun --cwd packages/app dev",
|
||||
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
|
||||
|
||||
@@ -122,13 +122,14 @@ Keep provider facades small and explicit:
|
||||
|
||||
### Provider Package Entrypoints
|
||||
|
||||
Catalog-selected native providers use package-like export paths from `@opencode/ai`. They are internal entrypoints in one npm package, not separately published provider packages. Every entrypoint implements `ProviderPackage.Definition` and exposes `model(modelID, settings)`, where settings are serializable provider configuration plus common `headers`, `body`, and `limits` overlays.
|
||||
Catalog-selected native providers use package-like export paths from `@opencode/ai`. They are internal entrypoints in one npm package, not separately published provider packages. Every entrypoint implements `ProviderPackage.Definition` and exposes `model(modelID, settings)`, where settings are one flat serializable object: the connection keys the entrypoint declares (`apiKey`, `baseURL`, `region`, …), the common `headers` and `body` overlays, and the protocol's request options (`reasoningEffort`, `thinking`, …) side by side. Each entrypoint destructures its own connection keys and passes the rest to the route as `providerOptions`; there is no nested `providerOptions` at the entrypoint.
|
||||
|
||||
```ts
|
||||
import { model } from "@opencode/ai/providers/openai/responses"
|
||||
|
||||
const selected = model("gpt-5", {
|
||||
apiKey,
|
||||
reasoningEffort: "high",
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.17.20",
|
||||
"version": "2.0.0",
|
||||
"name": "@opencode/ai",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1708,7 +1708,10 @@ export const transport = <
|
||||
}
|
||||
|
||||
function requiredBetaHeaders(body: Pick<AnthropicMessagesBody, "messages" | "context_management" | "thinking">) {
|
||||
const betas: string[] = []
|
||||
// Always request interleaved thinking. The API accepts the header on any
|
||||
// model and ignores it where unsupported, while manual-thinking models need
|
||||
// it for thinking between tool calls.
|
||||
const betas: string[] = ["interleaved-thinking-2025-05-14"]
|
||||
const requestsCompaction = (body.context_management?.edits.length ?? 0) > 0
|
||||
const replaysCompaction = body.messages.some((message) =>
|
||||
message.content.some((block) => block.type === "compaction"),
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { LanguageModel, ProviderOptions } from "./schema/index.js"
|
||||
import type { CompactionOperations } from "./route/client.js"
|
||||
|
||||
/**
|
||||
* Flat, serializable settings for `model(modelID, settings)`. Each entrypoint declares the connection keys it
|
||||
* reads; every other key is a request option for the route's protocol.
|
||||
*/
|
||||
export interface Settings extends Readonly<Record<string, unknown>> {
|
||||
readonly baseURL?: string
|
||||
readonly headers?: Readonly<Record<string, string>>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Struct } from "effect"
|
||||
import type { ProviderPackage } from "../provider-package.js"
|
||||
import { AlibabaChat } from "../protocols/alibaba-chat.js"
|
||||
import { AlibabaMessages } from "../protocols/alibaba-messages.js"
|
||||
@@ -34,9 +35,9 @@ export type Config = Location &
|
||||
readonly providerOptions?: ChatOptionsInput | MessagesOptionsInput | ResponsesOptionsInput
|
||||
}
|
||||
export type Settings<Options = ChatOptionsInput> = Location &
|
||||
ProviderPackage.Settings & {
|
||||
ProviderPackage.Settings &
|
||||
Options & {
|
||||
readonly apiKey?: string
|
||||
readonly providerOptions?: Options
|
||||
}
|
||||
|
||||
const hosts = new Map<string, string>([
|
||||
@@ -120,7 +121,11 @@ export const responsesModel: ProviderPackage.Definition<
|
||||
|
||||
function fromSettings(input: Settings<Config["providerOptions"]>) {
|
||||
const { body, ...rest } = input
|
||||
return configure({ ...rest, http: body === undefined ? undefined : { body } })
|
||||
return configure({
|
||||
...rest,
|
||||
http: body === undefined ? undefined : { body },
|
||||
providerOptions: Struct.omit(rest, ["apiKey", "baseURL", "headers", "region", "workspaceID"]),
|
||||
})
|
||||
}
|
||||
|
||||
export const webSearch = () => hostedTool("web_search", "Search the web with Alibaba's hosted search tool.")
|
||||
|
||||
@@ -23,16 +23,16 @@ export type Config = RouteDefaultsInput & {
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly auth?: "bearer" | "sigv4"
|
||||
readonly baseURL?: string
|
||||
readonly credentials?: Credentials
|
||||
readonly profile?: string
|
||||
readonly region?: string
|
||||
readonly topP?: number
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly auth?: "bearer" | "sigv4"
|
||||
readonly baseURL?: string
|
||||
readonly credentials?: Credentials
|
||||
readonly profile?: string
|
||||
readonly region?: string
|
||||
readonly topP?: number
|
||||
}
|
||||
|
||||
const responsesRoute = Route.make({
|
||||
id: "bedrock-mantle-responses",
|
||||
@@ -108,18 +108,29 @@ export const configure = (input: Config = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
const fromSettings = (settings: Settings) =>
|
||||
const fromSettings = ({
|
||||
apiKey,
|
||||
auth,
|
||||
baseURL,
|
||||
body,
|
||||
credentials,
|
||||
headers,
|
||||
profile,
|
||||
region,
|
||||
topP,
|
||||
...providerOptions
|
||||
}: Settings) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
auth: settings.auth,
|
||||
baseURL: settings.baseURL,
|
||||
credentials: settings.credentials,
|
||||
generation: settings.topP === undefined ? undefined : { topP: settings.topP },
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
profile: settings.profile,
|
||||
providerOptions: settings.providerOptions,
|
||||
region: settings.region,
|
||||
apiKey,
|
||||
auth,
|
||||
baseURL,
|
||||
credentials,
|
||||
generation: topP === undefined ? undefined : { topP },
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
profile,
|
||||
providerOptions,
|
||||
region,
|
||||
})
|
||||
|
||||
export const chatModel: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
|
||||
@@ -19,13 +19,13 @@ export type Config = RouteDefaultsInput &
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
AnthropicMessages.ProviderOptionsInput &
|
||||
(
|
||||
| { readonly apiKey?: string; readonly authToken?: never }
|
||||
| { readonly apiKey?: never; readonly authToken?: string }
|
||||
) & {
|
||||
readonly baseURL: string
|
||||
readonly provider?: string
|
||||
readonly providerOptions?: AnthropicMessages.ProviderOptionsInput
|
||||
}
|
||||
|
||||
export const routes = [AnthropicMessages.route]
|
||||
@@ -63,22 +63,20 @@ export const provider = {
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, AnthropicMessages.ProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, authToken, baseURL, body, headers, provider, ...providerOptions },
|
||||
) => {
|
||||
// Read before the exclusivity check narrows a conflicting settings object to `never`.
|
||||
const provider = ProviderID.make(settings.provider ?? id)
|
||||
if (settings.apiKey !== undefined && settings.authToken !== undefined)
|
||||
if (apiKey !== undefined && authToken !== undefined)
|
||||
throw new ProviderConfigurationError({
|
||||
provider,
|
||||
provider: ProviderID.make(provider ?? id),
|
||||
message: "Anthropic-compatible apiKey cannot be combined with authToken",
|
||||
})
|
||||
return configure({
|
||||
...(settings.authToken === undefined ? { apiKey: settings.apiKey } : { auth: Auth.bearer(settings.authToken) }),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
provider: settings.provider,
|
||||
providerOptions: settings.providerOptions,
|
||||
...(authToken === undefined ? { apiKey: apiKey } : { auth: Auth.bearer(authToken) }),
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
provider,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ export type Config = RouteDefaultsInput &
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
AnthropicMessages.ProviderOptionsInput &
|
||||
(
|
||||
| { readonly apiKey?: string; readonly authToken?: never }
|
||||
| { readonly apiKey?: never; readonly authToken?: string }
|
||||
) & {
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: AnthropicMessages.ProviderOptionsInput
|
||||
}
|
||||
|
||||
const auth = (options: ProviderAuthOption<"optional">) => {
|
||||
@@ -54,18 +54,18 @@ export const configure = (input: Config = {}) => {
|
||||
export const provider = configure()
|
||||
export const model: ProviderPackage.Definition<Settings, AnthropicMessages.ProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, authToken, baseURL, body, headers, ...providerOptions },
|
||||
) => {
|
||||
if (settings.apiKey !== undefined && settings.authToken !== undefined)
|
||||
if (apiKey !== undefined && authToken !== undefined)
|
||||
throw new ProviderConfigurationError({
|
||||
provider: id,
|
||||
message: "Anthropic apiKey cannot be combined with authToken",
|
||||
})
|
||||
return configure({
|
||||
...(settings.authToken === undefined ? { apiKey: settings.apiKey } : { auth: Auth.bearer(settings.authToken) }),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
...(authToken === undefined ? { apiKey: apiKey } : { auth: Auth.bearer(authToken) }),
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ export type LanguageModelOptions = AzureURL &
|
||||
export type Config = LanguageModelOptions
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput &
|
||||
AzureURL & {
|
||||
readonly apiKey?: string
|
||||
readonly apiVersion?: string
|
||||
readonly queryParams?: Readonly<Record<string, string>>
|
||||
readonly useDeploymentBasedUrls?: boolean
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
const resourceBaseURL = (resourceName: string) => `https://${resourceName.trim()}.openai.azure.com/openai`
|
||||
@@ -151,18 +151,28 @@ export const provider = {
|
||||
configure,
|
||||
}
|
||||
|
||||
const config = (settings: Settings): Config => {
|
||||
const config = ({
|
||||
apiKey,
|
||||
apiVersion,
|
||||
baseURL,
|
||||
body,
|
||||
headers,
|
||||
queryParams,
|
||||
resourceName,
|
||||
useDeploymentBasedUrls,
|
||||
...providerOptions
|
||||
}: Settings): Config => {
|
||||
const common = {
|
||||
apiKey: settings.apiKey,
|
||||
apiVersion: settings.apiVersion,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
queryParams: settings.queryParams === undefined ? undefined : { ...settings.queryParams },
|
||||
useDeploymentBasedUrls: settings.useDeploymentBasedUrls,
|
||||
apiKey,
|
||||
apiVersion,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
queryParams: queryParams === undefined ? undefined : { ...queryParams },
|
||||
useDeploymentBasedUrls,
|
||||
}
|
||||
if (settings.baseURL !== undefined) return { ...common, baseURL: settings.baseURL }
|
||||
if (settings.resourceName !== undefined) return { ...common, resourceName: settings.resourceName }
|
||||
if (baseURL !== undefined) return { ...common, baseURL }
|
||||
if (resourceName !== undefined) return { ...common, resourceName }
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Azure requires resourceName or baseURL" })
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "baseten-chat",
|
||||
@@ -48,13 +48,16 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as Baseten from "./baseten.js"
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "cerebras-chat",
|
||||
@@ -52,11 +52,14 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
@@ -27,10 +27,10 @@ export type LanguageModelOptions = GatewayURL &
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput &
|
||||
GatewayURL & {
|
||||
readonly apiKey?: string
|
||||
readonly gatewayApiKey?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export const baseURL = (input: GatewayURL) => {
|
||||
@@ -89,14 +89,25 @@ export const configure = (input: LanguageModelOptions) => {
|
||||
|
||||
export const provider = { id, configure }
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
gatewayApiKey: settings.gatewayApiKey,
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) => {
|
||||
const {
|
||||
accountId: _,
|
||||
apiKey,
|
||||
baseURL: _url,
|
||||
body,
|
||||
gatewayApiKey,
|
||||
gatewayId: _id,
|
||||
headers,
|
||||
...providerOptions
|
||||
} = settings
|
||||
return configure({
|
||||
apiKey,
|
||||
gatewayApiKey,
|
||||
baseURL: baseURL(settings),
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
export * as CloudflareAIGateway from "./cloudflare-ai-gateway.js"
|
||||
|
||||
@@ -21,9 +21,9 @@ export type LanguageModelOptions = WorkersAIURL &
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput &
|
||||
WorkersAIURL & {
|
||||
readonly apiKey?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export const baseURL = (input: WorkersAIURL) => {
|
||||
@@ -63,13 +63,15 @@ export const configure = (input: LanguageModelOptions) => {
|
||||
|
||||
export const provider = { id, configure }
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) => {
|
||||
const { accountId: _, apiKey, baseURL: _url, body, headers, ...providerOptions } = settings
|
||||
return configure({
|
||||
apiKey,
|
||||
baseURL: baseURL(settings),
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
export * as CloudflareWorkersAI from "./cloudflare-workers-ai.js"
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "deepinfra-chat",
|
||||
@@ -55,11 +55,14 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "deepseek-chat",
|
||||
@@ -52,13 +52,16 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as DeepSeek from "./deepseek.js"
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "fireworks-chat",
|
||||
@@ -48,13 +48,16 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as Fireworks from "./fireworks.js"
|
||||
|
||||
@@ -16,14 +16,14 @@ export type Config = RouteDefaultsInput &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
}
|
||||
|
||||
const route = Route.make({
|
||||
id: "google-vertex-chat",
|
||||
@@ -74,16 +74,19 @@ export const provider = {
|
||||
configure,
|
||||
}
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) => {
|
||||
if (settings.apiKey !== undefined)
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions },
|
||||
) => {
|
||||
if (apiKey !== undefined)
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Chat does not support API keys" })
|
||||
return configure({
|
||||
accessToken: settings.accessToken,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
location: settings.location,
|
||||
project: settings.project,
|
||||
providerOptions: settings.providerOptions,
|
||||
accessToken,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
location,
|
||||
project,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
@@ -25,14 +25,14 @@ export type Config = RouteDefaultsInput &
|
||||
readonly providerOptions?: AnthropicMessages.ProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
readonly providerOptions?: AnthropicMessages.ProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
AnthropicMessages.ProviderOptionsInput & {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
}
|
||||
|
||||
const route = Route.make({
|
||||
id: "google-vertex-messages",
|
||||
@@ -105,17 +105,17 @@ export const provider = {
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, AnthropicMessages.ProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions },
|
||||
) => {
|
||||
if (settings.apiKey !== undefined)
|
||||
if (apiKey !== undefined)
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Messages does not support API keys" })
|
||||
return configure({
|
||||
accessToken: settings.accessToken,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
location: settings.location,
|
||||
project: settings.project,
|
||||
providerOptions: settings.providerOptions,
|
||||
accessToken,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
location,
|
||||
project,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ export type Config = RouteDefaultsInput &
|
||||
readonly providerOptions?: OpenResponsesProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
readonly providerOptions?: OpenResponsesProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenResponsesProviderOptionsInput & {
|
||||
readonly accessToken?: string
|
||||
readonly apiKey?: never
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
}
|
||||
|
||||
const route = Route.make({
|
||||
id: "google-vertex-responses",
|
||||
@@ -77,17 +77,17 @@ export const provider = {
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenResponsesProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions },
|
||||
) => {
|
||||
if (settings.apiKey !== undefined)
|
||||
if (apiKey !== undefined)
|
||||
throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Responses does not support API keys" })
|
||||
return configure({
|
||||
accessToken: settings.accessToken,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
location: settings.location,
|
||||
project: settings.project,
|
||||
providerOptions: settings.providerOptions,
|
||||
accessToken,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
location,
|
||||
project,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ export type Config = RouteDefaultsInput &
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
GeminiProviderOptionsInput &
|
||||
(
|
||||
| { readonly accessToken?: string; readonly apiKey?: never }
|
||||
| { readonly accessToken?: never; readonly apiKey?: string }
|
||||
@@ -33,7 +34,6 @@ export type Settings = ProviderPackage.Settings &
|
||||
readonly baseURL?: string
|
||||
readonly location?: string
|
||||
readonly project?: string
|
||||
readonly providerOptions?: GeminiProviderOptionsInput
|
||||
}
|
||||
|
||||
const fromRequest = Effect.fn("GoogleVertex.fromRequest")(function* (request: LLMRequest) {
|
||||
@@ -124,19 +124,22 @@ export const provider = {
|
||||
id,
|
||||
configure,
|
||||
}
|
||||
export const model: ProviderPackage.Definition<Settings, GeminiProviderOptionsInput>["model"] = (modelID, settings) => {
|
||||
if (settings.apiKey !== undefined && settings.accessToken !== undefined)
|
||||
export const model: ProviderPackage.Definition<Settings, GeminiProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ accessToken, apiKey, baseURL, body, headers, location, project, ...providerOptions },
|
||||
) => {
|
||||
if (apiKey !== undefined && accessToken !== undefined)
|
||||
throw new ProviderConfigurationError({
|
||||
provider: id,
|
||||
message: "Google Vertex apiKey cannot be combined with accessToken or auth",
|
||||
})
|
||||
return configure({
|
||||
...(settings.apiKey === undefined ? { accessToken: settings.accessToken } : { apiKey: settings.apiKey }),
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
location: settings.location,
|
||||
project: settings.project,
|
||||
providerOptions: settings.providerOptions,
|
||||
...(apiKey === undefined ? { accessToken: accessToken } : { apiKey: apiKey }),
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
location,
|
||||
project,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ export type Config = RouteDefaultsInput &
|
||||
readonly providerOptions?: Gemini.ProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: Gemini.ProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
Gemini.ProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const auth = (options: ProviderAuthOption<"optional">) => {
|
||||
if ("auth" in options && options.auth) return options.auth
|
||||
@@ -57,13 +57,16 @@ export const configure = (input: Config = {}) => {
|
||||
}
|
||||
|
||||
export const provider = configure()
|
||||
export const model: ProviderPackage.Definition<Settings, Gemini.ProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, Gemini.ProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export const image = provider.image
|
||||
|
||||
@@ -26,11 +26,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ProviderOptions
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: ProviderOptions
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
ProviderOptions & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const Options = Schema.Struct({
|
||||
includeReasoning: Schema.optional(Schema.Boolean),
|
||||
@@ -103,13 +103,16 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ProviderOptions>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, ProviderOptions>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as Groq from "./groq.js"
|
||||
|
||||
@@ -79,11 +79,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: ProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
ProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const responsesRoute = Route.make({
|
||||
id: "meta-responses",
|
||||
@@ -169,13 +169,13 @@ export const chatModel: ProviderPackage.Definition<Settings, OpenResponsesProvid
|
||||
export const messagesModel: ProviderPackage.Definition<Settings, MessagesOptionsInput>["model"] = (modelID, settings) =>
|
||||
fromSettings(settings).messages(modelID)
|
||||
|
||||
function fromSettings(settings: Settings) {
|
||||
function fromSettings({ apiKey, baseURL, body, headers, ...providerOptions }: Settings) {
|
||||
return configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ export type Config = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings<Options = MessagesOptionsInput> extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: Options
|
||||
}
|
||||
export type Settings<Options = MessagesOptionsInput> = ProviderPackage.Settings &
|
||||
Options & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const ChatOptions = Schema.Struct({
|
||||
thinking: Schema.optional(Schema.Struct({ type: Schema.String })),
|
||||
@@ -127,14 +127,14 @@ export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings<MessagesOptionsInput>, MessagesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export const messages = provider.messages
|
||||
|
||||
@@ -3,11 +3,14 @@ import { MiniMax } from "../minimax.js"
|
||||
|
||||
export type Settings = MiniMax.Settings<MiniMax.ChatOptionsInput>
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, MiniMax.ChatOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, MiniMax.ChatOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
MiniMax.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).chat(modelID)
|
||||
|
||||
@@ -5,12 +5,12 @@ export type Settings = MiniMax.Settings<MiniMax.ResponsesOptionsInput>
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, MiniMax.ResponsesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
MiniMax.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).responses(modelID)
|
||||
|
||||
@@ -14,11 +14,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ProviderOptions
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: ProviderOptions
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
ProviderOptions & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = MistralChat.route
|
||||
export const routes = [route]
|
||||
@@ -39,13 +39,16 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ProviderOptions>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, ProviderOptions>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as Mistral from "./mistral.js"
|
||||
|
||||
@@ -42,11 +42,11 @@ export type Config = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ChatOptionsInput | MessagesOptionsInput | ResponsesOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings<Options = ChatOptionsInput> extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: Options
|
||||
}
|
||||
export type Settings<Options = ChatOptionsInput> = ProviderPackage.Settings &
|
||||
Options & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const ChatOptions = Schema.Struct({
|
||||
reasoningEffort: Schema.optional(Schema.String),
|
||||
@@ -133,13 +133,16 @@ export const chat = provider.chat
|
||||
export const messages = provider.messages
|
||||
export const responses = provider.responses
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as Moonshot from "./moonshot.js"
|
||||
|
||||
@@ -5,12 +5,12 @@ export type Settings = Moonshot.Settings<Moonshot.MessagesOptionsInput>
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, Moonshot.MessagesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
Moonshot.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).messages(modelID)
|
||||
|
||||
@@ -5,12 +5,12 @@ export type Settings = Moonshot.Settings<Moonshot.ResponsesOptionsInput>
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, Moonshot.ResponsesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
Moonshot.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).responses(modelID)
|
||||
|
||||
@@ -16,12 +16,12 @@ export type Config = RouteDefaultsInput &
|
||||
readonly providerOptions?: OpenResponsesProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL: string
|
||||
readonly provider?: string
|
||||
readonly providerOptions?: OpenResponsesProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenResponsesProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL: string
|
||||
readonly provider?: string
|
||||
}
|
||||
|
||||
export const routes = [OpenAICompatibleResponses.route]
|
||||
|
||||
@@ -48,13 +48,13 @@ export const provider = {
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenResponsesProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, provider, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
provider: settings.provider,
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
provider,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
@@ -14,12 +14,12 @@ type GenericModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL: string
|
||||
readonly provider?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL: string
|
||||
readonly provider?: string
|
||||
}
|
||||
|
||||
export const routes = [OpenAICompatibleChat.route]
|
||||
|
||||
@@ -45,14 +45,17 @@ export const provider = {
|
||||
configure,
|
||||
}
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, provider, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
provider: settings.provider,
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
provider,
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as OpenAICompatible from "./openai-compatible.js"
|
||||
|
||||
@@ -57,14 +57,14 @@ export const imageGeneration = (options: ImageGenerationOptions = {}) =>
|
||||
},
|
||||
})
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly organization?: string
|
||||
readonly project?: string
|
||||
readonly queryParams?: Readonly<Record<string, string>>
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly organization?: string
|
||||
readonly project?: string
|
||||
readonly queryParams?: Readonly<Record<string, string>>
|
||||
}
|
||||
|
||||
const auth = (options: ProviderAuthOption<"optional">) => AuthOptions.bearer(options, "OPENAI_API_KEY")
|
||||
|
||||
@@ -116,19 +116,28 @@ export const configure = (input: Config = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
const config = (settings: Settings): Config => {
|
||||
const config = ({
|
||||
apiKey,
|
||||
baseURL,
|
||||
body,
|
||||
headers: given,
|
||||
organization,
|
||||
project,
|
||||
queryParams,
|
||||
...providerOptions
|
||||
}: Settings): Config => {
|
||||
const headers = {
|
||||
...(settings.organization === undefined ? {} : { "OpenAI-Organization": settings.organization }),
|
||||
...(settings.project === undefined ? {} : { "OpenAI-Project": settings.project }),
|
||||
...settings.headers,
|
||||
...(organization === undefined ? {} : { "OpenAI-Organization": organization }),
|
||||
...(project === undefined ? {} : { "OpenAI-Project": project }),
|
||||
...given,
|
||||
}
|
||||
return {
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: Object.keys(headers).length === 0 ? undefined : headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
queryParams: settings.queryParams === undefined ? undefined : { ...settings.queryParams },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
queryParams: queryParams === undefined ? undefined : { ...queryParams },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,11 +77,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenRouterProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenRouterProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenRouterProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const OpenRouterBody = Schema.StructWithRest(Schema.Struct(OpenAIChat.bodyFields), [
|
||||
Schema.Record(Schema.String, Schema.Any),
|
||||
@@ -191,12 +191,12 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
export const provider = configure()
|
||||
export const model: ProviderPackage.Definition<Settings, OpenRouterProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
@@ -15,11 +15,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
OpenAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export const route = Route.make({
|
||||
id: "togetherai-chat",
|
||||
@@ -52,11 +52,14 @@ export const configure = (input: LanguageModelOptions = {}) => {
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers === undefined ? undefined : { ...settings.headers },
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers: headers === undefined ? undefined : { ...headers },
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
@@ -20,11 +20,11 @@ export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: XAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: XAIProviderOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
XAIProviderOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export type { XAIImageOptions } from "../protocols/xai-images.js"
|
||||
|
||||
@@ -110,13 +110,13 @@ export const model: ProviderPackage.Definition<
|
||||
Settings,
|
||||
XAIProviderOptionsInput,
|
||||
typeof responsesRoute.compact
|
||||
>["model"] = (modelID, settings) =>
|
||||
>["model"] = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
export const responses = provider.responses
|
||||
export const chat = provider.chat
|
||||
|
||||
@@ -23,11 +23,11 @@ export type Config = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ChatOptionsInput | MessagesOptionsInput | ResponsesOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings<Options = ChatOptionsInput> extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: Options
|
||||
}
|
||||
export type Settings<Options = ChatOptionsInput> = ProviderPackage.Settings &
|
||||
Options & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
const chatRoute = Route.make({
|
||||
id: "zai-coding-chat",
|
||||
@@ -80,13 +80,16 @@ export const chat = provider.chat
|
||||
export const messages = provider.messages
|
||||
export const responses = provider.responses
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as ZAICodingPlan from "./zai-coding-plan.js"
|
||||
|
||||
@@ -5,12 +5,12 @@ export type Settings = ZAICodingPlan.Settings<ZAICodingPlan.MessagesOptionsInput
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ZAICodingPlan.MessagesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
ZAICodingPlan.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).messages(modelID)
|
||||
|
||||
@@ -5,12 +5,12 @@ export type Settings = ZAICodingPlan.Settings<ZAICodingPlan.ResponsesOptionsInpu
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ZAICodingPlan.ResponsesOptionsInput>["model"] = (
|
||||
modelID,
|
||||
settings,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
ZAICodingPlan.configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).responses(modelID)
|
||||
|
||||
@@ -17,11 +17,11 @@ export type Config = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
readonly providerOptions?: ChatOptionsInput
|
||||
}
|
||||
|
||||
export interface Settings extends ProviderPackage.Settings {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: ChatOptionsInput
|
||||
}
|
||||
export type Settings = ProviderPackage.Settings &
|
||||
ChatOptionsInput & {
|
||||
readonly apiKey?: string
|
||||
readonly baseURL?: string
|
||||
}
|
||||
|
||||
export type { ZAIImageOptions } from "../protocols/zai-images.js"
|
||||
|
||||
@@ -70,13 +70,16 @@ export const provider = configure()
|
||||
export const image = provider.image
|
||||
export const chat = provider.chat
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (modelID, settings) =>
|
||||
export const model: ProviderPackage.Definition<Settings, ChatOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey: settings.apiKey,
|
||||
baseURL: settings.baseURL,
|
||||
headers: settings.headers,
|
||||
http: settings.body === undefined ? undefined : { body: { ...settings.body } },
|
||||
providerOptions: settings.providerOptions,
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: body === undefined ? undefined : { body: { ...body } },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as ZAI from "./zai.js"
|
||||
|
||||
@@ -188,13 +188,13 @@ describe("provider package entrypoints", () => {
|
||||
baseURL: "https://provider.example.test/v1/",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { service_tier: "priority" },
|
||||
providerOptions: { reasoningEffort: "high" as const },
|
||||
reasoningEffort: "high" as const,
|
||||
}
|
||||
const deepinfra = DeepInfra.model("google/gemma-3-27b-it", settings)
|
||||
|
||||
expect(deepinfra.route.id).toBe("deepinfra-chat")
|
||||
expect(deepinfra.route.endpoint.baseURL).toBe("https://provider.example.test/v1/openai")
|
||||
expect(deepinfra.route.defaults.providerOptions).toEqual(settings.providerOptions)
|
||||
expect(deepinfra.route.defaults.providerOptions).toEqual({ reasoningEffort: "high" })
|
||||
expect(deepinfra.route.defaults.headers).toEqual(settings.headers)
|
||||
expect(deepinfra.route.defaults.http?.body).toEqual(settings.body)
|
||||
})
|
||||
@@ -210,7 +210,7 @@ describe("provider package entrypoints", () => {
|
||||
apiKey: "fixture",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { custom: true },
|
||||
providerOptions: { reasoningEffort: "high" },
|
||||
reasoningEffort: "high",
|
||||
})
|
||||
expect(selected.provider).toBe(provider.id)
|
||||
expect(selected.route.endpoint.baseURL).toBe(provider.baseURL({ accountId: "account" }))
|
||||
@@ -231,11 +231,11 @@ describe("provider package entrypoints", () => {
|
||||
}
|
||||
const openrouter = OpenRouter.model("anthropic/claude-sonnet-4", {
|
||||
...settings,
|
||||
providerOptions: { usage: true },
|
||||
usage: true,
|
||||
})
|
||||
const xai = XAI.model("grok-4", {
|
||||
...settings,
|
||||
providerOptions: { reasoningEffort: "high" },
|
||||
reasoningEffort: "high",
|
||||
})
|
||||
|
||||
for (const selected of [openrouter, xai]) {
|
||||
@@ -269,7 +269,8 @@ describe("provider package entrypoints", () => {
|
||||
provider: "example",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { service_tier: "priority" },
|
||||
providerOptions: { reasoningEffort: "low", store: true },
|
||||
reasoningEffort: "low",
|
||||
store: true,
|
||||
})
|
||||
|
||||
expect(String(selected.provider)).toBe("example")
|
||||
@@ -295,7 +296,7 @@ describe("provider package entrypoints", () => {
|
||||
provider: "example",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { metadata: { user_id: "user_1" } },
|
||||
providerOptions: { effort: "low" },
|
||||
effort: "low",
|
||||
})
|
||||
|
||||
expect(String(selected.provider)).toBe("example")
|
||||
@@ -315,7 +316,7 @@ describe("provider package entrypoints", () => {
|
||||
const Anthropic = await import("@opencode/ai/providers/anthropic")
|
||||
const selected = Anthropic.model("claude-sonnet-4-6", {
|
||||
apiKey: "fixture",
|
||||
providerOptions: { thinking: { type: "adaptive" } },
|
||||
thinking: { type: "adaptive" },
|
||||
})
|
||||
|
||||
expect(selected.route.defaults.providerOptions).toEqual({ thinking: { type: "adaptive" } })
|
||||
@@ -409,7 +410,7 @@ describe("provider package entrypoints", () => {
|
||||
baseURL: "https://generativelanguage.test/v1beta",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { safetySettings: [] },
|
||||
providerOptions: { thinkingConfig: { thinkingBudget: 1_024 } },
|
||||
thinkingConfig: { thinkingBudget: 1_024 },
|
||||
})
|
||||
|
||||
expect(selected.route.id).toBe("gemini")
|
||||
|
||||
@@ -69,7 +69,9 @@ for (const model of [
|
||||
dynamicResponse(({ request, text, respond }) =>
|
||||
Effect.sync(() => {
|
||||
const body = JSON.parse(text)
|
||||
expect(request.headers["anthropic-beta"]).toBe("existing-beta,compact-2026-01-12")
|
||||
expect(request.headers["anthropic-beta"]).toBe(
|
||||
"existing-beta,interleaved-thinking-2025-05-14,compact-2026-01-12",
|
||||
)
|
||||
if (body.messages.length === 1) {
|
||||
expect(body.context_management.edits).toEqual([
|
||||
{
|
||||
|
||||
@@ -32,7 +32,9 @@ for (const [id, enabled] of [
|
||||
enabled ? { type: "adaptive", block_binding: { prefix_mismatch_behavior: "drop_block" } } : undefined,
|
||||
)
|
||||
expect(prepared.request.headers["anthropic-beta"]).toBe(
|
||||
enabled ? "existing-beta,thinking-binding-controls-2026-08-01" : "existing-beta",
|
||||
enabled
|
||||
? "existing-beta,interleaved-thinking-2025-05-14,thinking-binding-controls-2026-08-01"
|
||||
: "existing-beta,interleaved-thinking-2025-05-14",
|
||||
)
|
||||
}),
|
||||
)
|
||||
@@ -53,7 +55,9 @@ it.effect("preserves explicit thinking settings and combines required beta heade
|
||||
const prepared = yield* AnthropicMessages.route.prepareTransport(compiled.body, request)
|
||||
expect(compiled.body.thinking).toEqual(thinking)
|
||||
expect(prepared.request.headers["anthropic-beta"]).toBe(
|
||||
thinking.type === "disabled" ? "compact-2026-01-12" : "compact-2026-01-12,thinking-binding-controls-2026-08-01",
|
||||
thinking.type === "disabled"
|
||||
? "interleaved-thinking-2025-05-14,compact-2026-01-12"
|
||||
: "interleaved-thinking-2025-05-14,compact-2026-01-12,thinking-binding-controls-2026-08-01",
|
||||
)
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -34,12 +34,10 @@ it.effect("Groq lowers its own options for custom catalog identities and endpoin
|
||||
baseURL: "https://gateway.example/v1",
|
||||
headers: { "x-client": "test" },
|
||||
body: { custom: "value" },
|
||||
providerOptions: {
|
||||
reasoningEffort: "default",
|
||||
parallelToolCalls: true,
|
||||
serviceTier: "flex",
|
||||
user: "test-user",
|
||||
},
|
||||
reasoningEffort: "default",
|
||||
parallelToolCalls: true,
|
||||
serviceTier: "flex",
|
||||
user: "test-user",
|
||||
}),
|
||||
{ provider: "custom-groq" },
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ it.effect("Meta package selectors preserve overrides and Chat token policy on cu
|
||||
baseURL: "https://gateway.example/v1",
|
||||
headers: { "x-client": "test" },
|
||||
body: { custom: "value" },
|
||||
providerOptions: { reasoningEffort: "future-effort" },
|
||||
reasoningEffort: "future-effort",
|
||||
})
|
||||
expect(model.route.endpoint.baseURL).toBe("https://gateway.example/v1")
|
||||
expect(model.route.defaults.headers).toEqual({ "x-client": "test" })
|
||||
|
||||
@@ -99,7 +99,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
const settings = {
|
||||
apiKey: "fixture",
|
||||
baseURL: "https://gateway.example/v1",
|
||||
providerOptions: { reasoningEffort: "high" },
|
||||
reasoningEffort: "high",
|
||||
}
|
||||
const selected = provider.configure(settings).model("test-model")
|
||||
expect(selected.provider).toBe(provider.id)
|
||||
@@ -143,7 +143,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
tools: [
|
||||
ToolDefinition.make({ name: "lookup", description: "Look up data", inputSchema: { type: "object" } }),
|
||||
],
|
||||
providerOptions: { store: true },
|
||||
store: true,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -168,7 +168,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
]),
|
||||
Message.user("Continue."),
|
||||
],
|
||||
providerOptions: { store: true },
|
||||
store: true,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -206,7 +206,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
baseURL: "https://gateway.example/v1",
|
||||
headers: { "x-application": "opencode" },
|
||||
body: { service_tier: "priority" },
|
||||
providerOptions: { reasoningEffort: "high" },
|
||||
reasoningEffort: "high",
|
||||
})
|
||||
|
||||
expect(selected.route.endpoint.baseURL).toBe("https://gateway.example/v1")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/app",
|
||||
"version": "1.18.15",
|
||||
"version": "2.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -285,7 +285,7 @@ export const DESKTOP_NATIVE_ENGLISH = {
|
||||
"desktop.updater.dialog.later": "Later",
|
||||
|
||||
"desktop.cli.installed.title": "CLI Installed",
|
||||
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode2' command.",
|
||||
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode' command.",
|
||||
"desktop.cli.failed.title": "Installation Failed",
|
||||
"desktop.cli.failed.message": "Failed to install CLI: {{error}}",
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM alpine AS base
|
||||
|
||||
ARG BUN_RUNTIME_TRANSPILER_CACHE_PATH=0
|
||||
ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=${BUN_RUNTIME_TRANSPILER_CACHE_PATH}
|
||||
RUN apk add --no-cache libgcc libstdc++ ripgrep
|
||||
|
||||
FROM base AS build-amd64
|
||||
COPY dist/cli-linux-x64-baseline-musl/bin/opencode /usr/local/bin/opencode
|
||||
|
||||
FROM base AS build-arm64
|
||||
COPY dist/cli-linux-arm64-musl/bin/opencode /usr/local/bin/opencode
|
||||
|
||||
ARG TARGETARCH
|
||||
FROM build-${TARGETARCH}
|
||||
RUN ln -s opencode /usr/local/bin/opencode2 && opencode --version && opencode2 --version
|
||||
ENTRYPOINT ["opencode"]
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const childProcess = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
|
||||
const forwardedSignals =
|
||||
process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGHUP"] : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
|
||||
|
||||
function run(target) {
|
||||
const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
|
||||
child.on("error", (error) => {
|
||||
console.error(error.message)
|
||||
process.exit(1)
|
||||
})
|
||||
const forwarders = {}
|
||||
for (const signal of forwardedSignals) {
|
||||
forwarders[signal] = () => {
|
||||
try {
|
||||
child.kill(signal)
|
||||
} catch {}
|
||||
}
|
||||
process.on(signal, forwarders[signal])
|
||||
}
|
||||
child.on("exit", (code, signal) => {
|
||||
for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal])
|
||||
if (signal) return process.kill(process.pid, signal)
|
||||
process.exit(typeof code === "number" ? code : 0)
|
||||
})
|
||||
}
|
||||
|
||||
const envPath = process.env.OPENCODE_BIN_PATH
|
||||
const scriptDir = path.dirname(fs.realpathSync(__filename))
|
||||
const command = path.basename(__filename).replace(/\.cjs$/, "")
|
||||
const nodeBuild = command === "opencode-node"
|
||||
const sourceCommand = nodeBuild ? "opencode2-node" : "opencode"
|
||||
const cached = path.join(scriptDir, `.${command}`)
|
||||
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform()
|
||||
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch()
|
||||
const base = `@opencode/cli${nodeBuild ? "-node" : ""}-` + platform + "-" + arch
|
||||
const binary = platform === "windows" ? `${sourceCommand}.exe` : sourceCommand
|
||||
|
||||
function supportsAvx2() {
|
||||
if (arch !== "x64") return false
|
||||
if (platform === "linux") {
|
||||
try {
|
||||
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (platform === "darwin") {
|
||||
try {
|
||||
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 })
|
||||
return result.status === 0 && (result.stdout || "").trim() === "1"
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (platform === "windows") {
|
||||
const command =
|
||||
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
||||
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
||||
try {
|
||||
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
||||
encoding: "utf8",
|
||||
timeout: 3000,
|
||||
windowsHide: true,
|
||||
})
|
||||
if (result.status !== 0) continue
|
||||
const output = (result.stdout || "").trim().toLowerCase()
|
||||
if (output === "true" || output === "1") return true
|
||||
if (output === "false" || output === "0") return false
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const names = (() => {
|
||||
if (nodeBuild) return [base]
|
||||
const baseline = arch === "x64" && !supportsAvx2()
|
||||
if (platform === "linux") {
|
||||
const musl = (() => {
|
||||
try {
|
||||
if (fs.existsSync("/etc/alpine-release")) return true
|
||||
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
||||
return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl")
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})()
|
||||
if (musl)
|
||||
return arch === "x64"
|
||||
? baseline
|
||||
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
||||
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
||||
: [`${base}-musl`, base]
|
||||
return arch === "x64"
|
||||
? baseline
|
||||
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
||||
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
||||
: [base, `${base}-musl`]
|
||||
}
|
||||
return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base]
|
||||
})()
|
||||
|
||||
function findBinary(startDir) {
|
||||
let current = startDir
|
||||
for (;;) {
|
||||
const modules = path.join(current, "node_modules")
|
||||
if (fs.existsSync(modules))
|
||||
for (const name of names) {
|
||||
const candidate = path.join(modules, name, "bin", binary)
|
||||
if (fs.existsSync(candidate)) return candidate
|
||||
}
|
||||
const parent = path.dirname(current)
|
||||
if (parent === current) return
|
||||
current = parent
|
||||
}
|
||||
}
|
||||
|
||||
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
|
||||
if (!resolved) {
|
||||
console.error(
|
||||
`It seems that your package manager failed to install the right ${command} CLI package. Try manually installing ` +
|
||||
names.map((name) => `"${name}"`).join(" or ") +
|
||||
" package",
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
run(resolved)
|
||||
@@ -1,134 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const childProcess = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
|
||||
const forwardedSignals =
|
||||
process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGHUP"] : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
|
||||
|
||||
function run(target) {
|
||||
const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
|
||||
child.on("error", (error) => {
|
||||
console.error(error.message)
|
||||
process.exit(1)
|
||||
})
|
||||
const forwarders = {}
|
||||
for (const signal of forwardedSignals) {
|
||||
forwarders[signal] = () => {
|
||||
try {
|
||||
child.kill(signal)
|
||||
} catch {}
|
||||
}
|
||||
process.on(signal, forwarders[signal])
|
||||
}
|
||||
child.on("exit", (code, signal) => {
|
||||
for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal])
|
||||
if (signal) return process.kill(process.pid, signal)
|
||||
process.exit(typeof code === "number" ? code : 0)
|
||||
})
|
||||
}
|
||||
|
||||
const envPath = process.env.OPENCODE_BIN_PATH
|
||||
const scriptDir = path.dirname(fs.realpathSync(__filename))
|
||||
const command = path.basename(__filename).replace(/\.cjs$/, "")
|
||||
const nodeBuild = command === "opencode2-node"
|
||||
const cached = path.join(scriptDir, `.${command}`)
|
||||
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform()
|
||||
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch()
|
||||
const base = `@opencode/cli${nodeBuild ? "-node" : ""}-` + platform + "-" + arch
|
||||
const binary = platform === "windows" ? `${command}.exe` : command
|
||||
|
||||
function supportsAvx2() {
|
||||
if (arch !== "x64") return false
|
||||
if (platform === "linux") {
|
||||
try {
|
||||
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (platform === "darwin") {
|
||||
try {
|
||||
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 })
|
||||
return result.status === 0 && (result.stdout || "").trim() === "1"
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (platform === "windows") {
|
||||
const command =
|
||||
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
||||
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
||||
try {
|
||||
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
||||
encoding: "utf8",
|
||||
timeout: 3000,
|
||||
windowsHide: true,
|
||||
})
|
||||
if (result.status !== 0) continue
|
||||
const output = (result.stdout || "").trim().toLowerCase()
|
||||
if (output === "true" || output === "1") return true
|
||||
if (output === "false" || output === "0") return false
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const names = (() => {
|
||||
if (nodeBuild) return [base]
|
||||
const baseline = arch === "x64" && !supportsAvx2()
|
||||
if (platform === "linux") {
|
||||
const musl = (() => {
|
||||
try {
|
||||
if (fs.existsSync("/etc/alpine-release")) return true
|
||||
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
||||
return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl")
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})()
|
||||
if (musl)
|
||||
return arch === "x64"
|
||||
? baseline
|
||||
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
||||
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
||||
: [`${base}-musl`, base]
|
||||
return arch === "x64"
|
||||
? baseline
|
||||
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
||||
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
||||
: [base, `${base}-musl`]
|
||||
}
|
||||
return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base]
|
||||
})()
|
||||
|
||||
function findBinary(startDir) {
|
||||
let current = startDir
|
||||
for (;;) {
|
||||
const modules = path.join(current, "node_modules")
|
||||
if (fs.existsSync(modules))
|
||||
for (const name of names) {
|
||||
const candidate = path.join(modules, name, "bin", binary)
|
||||
if (fs.existsSync(candidate)) return candidate
|
||||
}
|
||||
const parent = path.dirname(current)
|
||||
if (parent === current) return
|
||||
current = parent
|
||||
}
|
||||
}
|
||||
|
||||
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
|
||||
if (!resolved) {
|
||||
console.error(
|
||||
`It seems that your package manager failed to install the right ${command} CLI package. Try manually installing ` +
|
||||
names.map((name) => `"${name}"`).join(" or ") +
|
||||
" package",
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
run(resolved)
|
||||
require("./opencode.cjs")
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/cli",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode.cjs",
|
||||
"opencode2": "./bin/opencode2.cjs"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -12,7 +12,7 @@ import { verifyArtifact, verifySimulationGraph } from "./verify-artifact"
|
||||
import { resolveOpencodePty } from "./opencode-pty"
|
||||
|
||||
const dir = path.resolve(import.meta.dirname, "..")
|
||||
const binary = "opencode2"
|
||||
const binary = "opencode"
|
||||
const outdir = path.resolve(
|
||||
dir,
|
||||
process.argv.find((arg) => arg.startsWith("--outdir="))?.slice("--outdir=".length) ?? "dist",
|
||||
@@ -149,7 +149,7 @@ export default { path: file, version: ${JSON.stringify(opencodePty.version)}, sh
|
||||
},
|
||||
define: {
|
||||
OPENCODE_VERSION: `'${Script.version}'`,
|
||||
OPENCODE_CLI_NAME: `'${binary}'`,
|
||||
OPENCODE_CLI_NAME: "'opencode'",
|
||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||
OPENCODE_ARTIFACT: `'cli'`,
|
||||
OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "undefined",
|
||||
|
||||
@@ -12,10 +12,11 @@ const require = createRequire(import.meta.url)
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(directory, "package.json"), "utf8"))
|
||||
const command = Object.keys(packageJson.bin ?? {})[0]
|
||||
if (!command) throw new Error("OpenCode package does not declare a binary")
|
||||
const sourceCommand = packageJson.opencodeSourceBinary ?? command
|
||||
|
||||
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] ?? os.platform()
|
||||
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] ?? os.arch()
|
||||
const sourceBinary = platform === "windows" ? `${command}.exe` : command
|
||||
const sourceBinary = platform === "windows" ? `${sourceCommand}.exe` : sourceCommand
|
||||
const targetBinary = path.resolve(directory, packageJson.bin[command])
|
||||
const dependencies = packageJson.optionalDependencies ?? {}
|
||||
const base = Object.keys(dependencies).find((name) => name.endsWith(`-${platform}-${arch}`))
|
||||
|
||||
@@ -6,15 +6,10 @@ import path from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { UpdateArtifact } from "../../../script/update-artifact"
|
||||
|
||||
if (Script.channel !== "beta" && Script.channel !== "latest") {
|
||||
throw new Error("AUR publishing requires the beta or latest channel")
|
||||
}
|
||||
const beta = Script.channel === "beta"
|
||||
const name = beta ? "opencode-beta" : "opencode-bin"
|
||||
const command = beta ? "opencode2" : "opencode"
|
||||
if (!(beta ? /^\d+\.\d+\.\d+-beta[.-]\d+(?:\.\d+)?$/ : /^\d+\.\d+\.\d+$/).test(Script.version)) {
|
||||
throw new Error(`Expected a ${Script.channel} release version`)
|
||||
}
|
||||
if (Script.channel !== "beta") throw new Error("AUR publishing requires the beta channel")
|
||||
const name = "opencode-beta"
|
||||
const command = "opencode"
|
||||
if (!/^\d+\.\d+\.\d+-beta[.-]\d+(?:\.\d+)?$/.test(Script.version)) throw new Error("Expected a beta release version")
|
||||
|
||||
const dir = fileURLToPath(new URL("..", import.meta.url))
|
||||
const root = path.resolve(process.env.OPENCODE_CLI_DIST ?? path.join(dir, "dist"))
|
||||
@@ -22,6 +17,9 @@ const outdir = path.join(root, `aur-${name}`)
|
||||
const dryRun = process.argv.includes("--dry-run")
|
||||
const pkgver = Script.version.replaceAll("-", ".")
|
||||
const license = Bun.file(path.join(dir, "..", "..", "LICENSE"))
|
||||
const shim = `#!/bin/sh
|
||||
exec "$(dirname "$0")/opencode" "$@"
|
||||
`
|
||||
|
||||
await rm(outdir, { recursive: true, force: true })
|
||||
await mkdir(path.dirname(outdir), { recursive: true })
|
||||
@@ -48,6 +46,7 @@ const sources = await Promise.all(
|
||||
)
|
||||
|
||||
await Bun.write(path.join(outdir, "LICENSE"), license)
|
||||
await Bun.write(path.join(outdir, "opencode2"), shim)
|
||||
await Bun.write(
|
||||
path.join(outdir, "PKGBUILD"),
|
||||
[
|
||||
@@ -55,21 +54,22 @@ await Bun.write(
|
||||
`pkgname=${name}`,
|
||||
`pkgver=${pkgver}`,
|
||||
"pkgrel=1",
|
||||
`pkgdesc='OpenCode${beta ? " V2 beta" : ""} - the AI coding agent for the terminal'`,
|
||||
"pkgdesc='OpenCode beta - the AI coding agent for the terminal'",
|
||||
"url='https://github.com/anomalyco/opencode'",
|
||||
"arch=('x86_64' 'aarch64')",
|
||||
"license=('MIT')",
|
||||
"depends=('glibc' 'gcc-libs' 'ripgrep')",
|
||||
`provides=('${command}')`,
|
||||
`conflicts=('${command}')`,
|
||||
`provides=('${command}' 'opencode2')`,
|
||||
`conflicts=('${command}' 'opencode2')`,
|
||||
// Stripping a compiled Bun executable can damage its embedded application.
|
||||
"options=('!strip' '!debug')",
|
||||
"source=('LICENSE')",
|
||||
`sha256sums=('${new Bun.CryptoHasher("sha256").update(await license.arrayBuffer()).digest("hex")}')`,
|
||||
"source=('LICENSE' 'opencode2')",
|
||||
`sha256sums=('${new Bun.CryptoHasher("sha256").update(await license.arrayBuffer()).digest("hex")}' '${new Bun.CryptoHasher("sha256").update(shim).digest("hex")}')`,
|
||||
...sources,
|
||||
"",
|
||||
"package() {",
|
||||
` install -Dm755 "$srcdir/package/bin/opencode2" "$pkgdir/usr/bin/${command}"`,
|
||||
` install -Dm755 "$srcdir/package/bin/opencode" "$pkgdir/usr/bin/${command}"`,
|
||||
' install -Dm755 "$srcdir/opencode2" "$pkgdir/usr/bin/opencode2"',
|
||||
' install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"',
|
||||
"}",
|
||||
"",
|
||||
@@ -79,7 +79,7 @@ await Bun.write(path.join(outdir, ".SRCINFO"), await $`makepkg --printsrcinfo`.c
|
||||
console.log(`Prepared ${name} ${pkgver} in ${outdir}`)
|
||||
if (dryRun) process.exit(0)
|
||||
|
||||
await $`git add PKGBUILD .SRCINFO LICENSE`.cwd(outdir)
|
||||
await $`git add PKGBUILD .SRCINFO LICENSE opencode2`.cwd(outdir)
|
||||
if ((await $`git diff --cached --quiet`.cwd(outdir).nothrow()).exitCode !== 0) {
|
||||
await $`git commit -m ${`chore: update ${name} to ${pkgver}`}`.cwd(outdir)
|
||||
await $`git push origin master`.cwd(outdir)
|
||||
|
||||
@@ -29,6 +29,8 @@ async function publish(dir: string, name: string, version: string) {
|
||||
async function publishDistribution(input: {
|
||||
root: string
|
||||
name: string
|
||||
command: string
|
||||
legacyCommand?: string
|
||||
binary: string
|
||||
packagePrefix: string
|
||||
artifact: string
|
||||
@@ -48,7 +50,7 @@ async function publishDistribution(input: {
|
||||
|
||||
await $`mkdir -p ${input.root}/${input.name}/bin`
|
||||
await $`cp ./script/postinstall.mjs ${input.root}/${input.name}/postinstall.mjs`
|
||||
await Bun.file(`${input.root}/${input.name}/bin/${input.binary}.exe`).write(
|
||||
await Bun.file(`${input.root}/${input.name}/bin/${input.command}.exe`).write(
|
||||
[
|
||||
`echo "Error: ${input.name}'s postinstall script was not run." >&2`,
|
||||
'echo "" >&2',
|
||||
@@ -62,7 +64,11 @@ async function publishDistribution(input: {
|
||||
JSON.stringify(
|
||||
{
|
||||
name: input.name,
|
||||
bin: { [input.binary]: `./bin/${input.binary}.exe` },
|
||||
bin: {
|
||||
[input.command]: `./bin/${input.command}.exe`,
|
||||
...(input.legacyCommand ? { [input.legacyCommand]: `./bin/${input.command}.exe` } : {}),
|
||||
},
|
||||
...(input.command !== input.binary ? { opencodeSourceBinary: input.binary } : {}),
|
||||
scripts: { postinstall: "node ./postinstall.mjs" },
|
||||
version,
|
||||
license: pkg.license,
|
||||
@@ -120,21 +126,28 @@ async function publishDistribution(input: {
|
||||
await publishDistribution({
|
||||
root,
|
||||
name: pkg.name,
|
||||
binary: "opencode2",
|
||||
command: "opencode",
|
||||
legacyCommand: "opencode2",
|
||||
binary: "opencode",
|
||||
packagePrefix: "@opencode/cli-",
|
||||
artifact: "cli",
|
||||
})
|
||||
if (existsSync(path.join(root, "node"))) {
|
||||
if (Script.channel !== "latest" && existsSync(path.join(root, "node"))) {
|
||||
await publishDistribution({
|
||||
root: path.join(root, "node"),
|
||||
name: "@opencode/cli-node",
|
||||
command: "opencode2-node",
|
||||
binary: "opencode2-node",
|
||||
packagePrefix: "@opencode/cli-node-",
|
||||
artifact: "cli-node",
|
||||
})
|
||||
}
|
||||
|
||||
if ((Script.channel === "beta" || Script.channel === "latest") && Script.release) {
|
||||
if (Script.channel === "latest" && Script.release && !dryRun) {
|
||||
await $`docker buildx build --platform linux/amd64,linux/arm64 --tag ghcr.io/anomalyco/opencode:${Script.version} --push .`
|
||||
}
|
||||
|
||||
if (Script.channel === "beta" && Script.release) {
|
||||
await $`bun ./script/publish-aur.ts ${dryRun ? ["--dry-run"] : []}`.env({ ...process.env, OPENCODE_CLI_DIST: root })
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@ import path from "node:path"
|
||||
const nodeBuild = process.argv.includes("--node")
|
||||
const target = `cli${nodeBuild ? "-node" : ""}-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}`
|
||||
const directory = path.join(import.meta.dir, "..", "dist", ...(nodeBuild ? ["node"] : []), target, "bin")
|
||||
const binary = path.join(directory, `opencode2${nodeBuild ? "-node" : ""}${process.platform === "win32" ? ".exe" : ""}`)
|
||||
const binary = path.join(
|
||||
directory,
|
||||
`${nodeBuild ? "opencode2-node" : "opencode"}${process.platform === "win32" ? ".exe" : ""}`,
|
||||
)
|
||||
if (!(await Bun.file(binary).exists())) throw new Error(`Missing compiled CLI in ${directory}`)
|
||||
|
||||
const root = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-smoke-")))
|
||||
|
||||
@@ -36,7 +36,7 @@ const PermissionParams = {
|
||||
}
|
||||
|
||||
const Root = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
|
||||
description: "OpenCode 2.0 preview command line interface",
|
||||
description: "OpenCode command line interface",
|
||||
params: {
|
||||
...ServerParams,
|
||||
...PermissionParams,
|
||||
|
||||
@@ -105,7 +105,7 @@ const make = Effect.gen(function* () {
|
||||
global.home,
|
||||
".opencode",
|
||||
"bin",
|
||||
process.platform === "win32" ? "opencode2.exe" : "opencode2",
|
||||
process.platform === "win32" ? "opencode.exe" : "opencode",
|
||||
)
|
||||
if (path.resolve(process.execPath) === path.resolve(binary)) return "curl"
|
||||
if (!installedPackage) return
|
||||
@@ -186,7 +186,10 @@ const make = Effect.gen(function* () {
|
||||
"npm",
|
||||
"install",
|
||||
"--global",
|
||||
...(installedPackage && packageName !== installedPackage ? ["--force"] : []),
|
||||
...((OPENCODE_ARTIFACT === "cli" && !installedPackage?.endsWith("/cli-node")) ||
|
||||
(installedPackage && packageName !== installedPackage)
|
||||
? ["--force"]
|
||||
: []),
|
||||
target,
|
||||
],
|
||||
pnpm: ["pnpm", "add", "--global", `--allow-build=${packageName}`, target],
|
||||
|
||||
@@ -423,24 +423,24 @@ test("updates effective duplicate canonical keybinds", async () => {
|
||||
const file = path.join(directory.path, "cli.json")
|
||||
await Bun.write(
|
||||
file,
|
||||
`{"keybinds":{"session.delete":"first","session.delete":"last","permission.mode":"off","permission.mode":"on"}}`,
|
||||
`{"keybinds":{"session.delete":"first","session.delete":"last","opencode.settings":"off","opencode.settings":"on"}}`,
|
||||
)
|
||||
|
||||
const config = await run(
|
||||
directory.path,
|
||||
Effect.gen(function* () {
|
||||
const service = yield* Config.Service
|
||||
expect((yield* service.get()).keybinds).toEqual({ "session.delete": "last", "permission.mode": "on" })
|
||||
expect((yield* service.get()).keybinds).toEqual({ "session.delete": "last", "opencode.settings": "on" })
|
||||
return yield* service.update((draft) => {
|
||||
draft.keybinds = { ...draft.keybinds, "session.delete": "changed", "permission.mode": "changed" }
|
||||
draft.keybinds = { ...draft.keybinds, "session.delete": "changed", "opencode.settings": "changed" }
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(config.keybinds).toEqual({ "session.delete": "changed", "permission.mode": "changed" })
|
||||
expect(config.keybinds).toEqual({ "session.delete": "changed", "opencode.settings": "changed" })
|
||||
expect(parse(await Bun.file(file).text()).keybinds).toEqual({
|
||||
"session.delete": "changed",
|
||||
"permission.mode": "changed",
|
||||
"opencode.settings": "changed",
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ function fixture(
|
||||
}
|
||||
|
||||
const installs = [
|
||||
{ method: "npm", command: ["npm", "install", "--global", "@opencode/cli@2.3.4-beta.1"] },
|
||||
{ method: "npm", command: ["npm", "install", "--global", "--force", "@opencode/cli@2.3.4-beta.1"] },
|
||||
{
|
||||
method: "pnpm",
|
||||
command: ["pnpm", "add", "--global", "--allow-build=@opencode/cli", "@opencode/cli@2.3.4-beta.1"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/client",
|
||||
"version": "1.17.13",
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -9,7 +9,8 @@ standard-library surface that programs can use today, plus concrete gaps that ma
|
||||
- Intentional boundaries are not listed as compatibility work.
|
||||
|
||||
When behavior changes, update this file and the tests in the same change. The implementation and tests remain the
|
||||
ultimate source of truth.
|
||||
ultimate source of truth. Upstream test262 files run verbatim from `test/test262`; a failing file is listed in
|
||||
`test/test262/skipped.txt` and its gap is an unchecked item here (see `test/test262/README.md`).
|
||||
|
||||
## Source and execution model
|
||||
|
||||
@@ -25,6 +26,10 @@ ultimate source of truth.
|
||||
- [x] The global `search(...)` built-in: synchronous tool discovery that counts as an admitted tool call and is
|
||||
shadowable by program declarations like other globals.
|
||||
- [x] Cooperative timeout, an optional total tool-call limit, output bounding, and unrestricted tool-call concurrency.
|
||||
- [ ] Strict-mode early errors: duplicate parameter names, `yield` as an identifier, and a trailing comma after a
|
||||
rest parameter are accepted unless the program itself begins with `"use strict"`.
|
||||
- [ ] Valid JavaScript rejected by TypeScript transpilation before interpretation, such as `in` inside a destructuring
|
||||
default in a `for...of` head and Unicode-escaped keywords.
|
||||
|
||||
## Values and literals
|
||||
|
||||
@@ -64,6 +69,11 @@ ultimate source of truth.
|
||||
- [x] Array binding and assignment destructuring from strings, Maps, Sets, URLSearchParams, custom synchronous
|
||||
iterators, and synchronous generators, including stepwise elisions/rest and `IteratorClose` on early completion
|
||||
or binding/default failure.
|
||||
- [ ] Object destructuring from primitives follows ToObject (`const { length } = "abc"`, `const {} = 1`); non-object
|
||||
sources are rejected.
|
||||
- [ ] Destructuring a key that member access resolves through the owning built-in, such as
|
||||
`const { constructor } = error`, reads `undefined`.
|
||||
- [ ] Member expressions as `for...in` targets (`for (x.y in obj)`).
|
||||
|
||||
## Statements and control flow
|
||||
|
||||
@@ -111,6 +121,15 @@ ultimate source of truth.
|
||||
- [ ] User-defined constructor calls.
|
||||
- [ ] `Function.prototype.call`, `apply`, and `bind` for CodeMode functions.
|
||||
- [ ] Classes and private fields.
|
||||
- [x] Functions are objects: they hold own properties (`fn.count = 1`), enumerate them, and expose read-only `name`
|
||||
and `length`. Names follow JavaScript's NamedEvaluation: declarations, named expressions, bindings,
|
||||
assignments, object literal keys, and destructuring or parameter defaults.
|
||||
- [ ] `name` and `length` of built-in functions such as `Math.max` or `"a".includes`.
|
||||
- [ ] A named function expression's name is not bound inside its own body.
|
||||
- [ ] Redeclaring a function in the same scope is rejected; in JavaScript the last declaration wins.
|
||||
- [ ] A line terminator between `async function` and the function name.
|
||||
- [ ] Async generator functions evaluate parameter defaults and destructuring at the first `next()` rather than at the
|
||||
call, so their errors are not thrown synchronously.
|
||||
- [x] Synchronous and async generator declarations/expressions, `yield`, and `yield*`, including lazy bodies,
|
||||
`next(value)`, `return(value)`, `throw(value)`, exhaustion, promise adoption, async request ordering,
|
||||
`try`/`catch`/`finally`, and sync/async iterator symbols. Async `yield*` awaits values while adapting a sync
|
||||
@@ -154,6 +173,8 @@ ultimate source of truth.
|
||||
- [x] Plain, arithmetic, bitwise, and logical assignment operators.
|
||||
- [x] Property deletion on plain data objects and arrays, including computed and optional forms; deleting an array index
|
||||
creates a hole without changing its length.
|
||||
- [ ] Operators, `switch` discriminants, and coercion helpers such as `String` and `isNaN` applied to functions and
|
||||
namespaces; JavaScript coerces them, the interpreter rejects non-data operands.
|
||||
|
||||
## Promises and tools
|
||||
|
||||
@@ -225,7 +246,8 @@ ultimate source of truth.
|
||||
- [x] Circular references are rejected when created (`o.self = o`, `array.push(array)`), not at serialization as in JS.
|
||||
- [x] `Object.is` for supported data values.
|
||||
- [x] `Object.groupBy` over finite collections and custom synchronous iterators/generators, with string-key coercion
|
||||
and null-prototype results.
|
||||
and plain-object results.
|
||||
- [ ] `Object.prototype` methods on values: `toString`, `toLocaleString`, `valueOf`, and `hasOwnProperty`.
|
||||
|
||||
## Arrays
|
||||
|
||||
@@ -245,10 +267,18 @@ ultimate source of truth.
|
||||
- [x] `length`, numeric indexing, index assignment, spread, and `for...of`.
|
||||
- [x] The `thisArg` argument of `Array.from` is accepted and ignored, like JS arrows.
|
||||
- [x] `Array.prototype.toSpliced`.
|
||||
- [x] Canonical array/string index parsing: keys such as `"01"` remain non-index properties rather than aliasing index
|
||||
`1`; arbitrary array-property assignment remains unsupported.
|
||||
- [x] Canonical array/string index parsing: keys such as `"01"` are ordinary properties rather than aliases of index
|
||||
`1`.
|
||||
- [x] `Array.prototype.sort` preserves trailing holes, while `toSorted` densifies holes into `undefined` elements,
|
||||
like JavaScript.
|
||||
- [x] Assigning `length` to truncate or extend an array; invalid lengths throw `RangeError`.
|
||||
- [x] Non-index own properties on arrays (`arr.foo = 1`, `arr.constructor = null`). They are excluded from the JSON
|
||||
form, like `JSON.stringify`.
|
||||
- [ ] Argument coercion for `indexOf`, `lastIndexOf`, `includes`, `fill`, `flat`, `copyWithin`, and the `join`
|
||||
separator: JavaScript applies ToIntegerOrInfinity/ToString (including `valueOf`, strings, and `undefined`), the
|
||||
interpreter requires numbers and strings; `includes()`/`indexOf()` with no argument should search for
|
||||
`undefined`.
|
||||
- [ ] Iterator objects from `keys`, `values`, and `entries` with a live `next()`.
|
||||
|
||||
## Strings
|
||||
|
||||
@@ -332,7 +362,7 @@ ultimate source of truth.
|
||||
- [x] `test`, `exec`, and `toString`.
|
||||
- [x] Readable `source`, `flags`, `lastIndex`, `hasIndices`, `global`, `ignoreCase`, `multiline`, `sticky`, `unicode`,
|
||||
`unicodeSets`, and `dotAll`.
|
||||
- [x] Captures, named groups, match `.index`, and stateful global matching.
|
||||
- [x] Captures, named groups, match `.index` and `.input`, and stateful global matching.
|
||||
- [x] Integration with supported String methods, including function replacers.
|
||||
- [x] Writable `lastIndex`.
|
||||
- [x] Match `indices` metadata for the `d` flag, including named groups on `exec`, `match`, and `matchAll` results.
|
||||
@@ -391,3 +421,5 @@ ultimate source of truth.
|
||||
- [x] Caught errors do not distinguish user throws, interpreter failures, and tool failures; a program sees one
|
||||
Error-shaped value with `name` and `message` in `catch`, rejection handlers, and `Promise.allSettled` reasons.
|
||||
This is deliberate: the program should handle a failure the same way regardless of where it originated.
|
||||
- [ ] Failures raised by the interpreter itself carry the generic `Error` name where JavaScript throws a `TypeError`,
|
||||
`RangeError`, or `ReferenceError`, so `e instanceof TypeError` and `e.constructor === TypeError` are false.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/codemode",
|
||||
"version": "1.18.4",
|
||||
"version": "2.0.0",
|
||||
"description": "Effect-native confined code execution over schema-described tools",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copies the manifest's test262 directories from a local checkout into test/test262, verbatim.
|
||||
// Files needing unsupported flags, features, or harness includes, or whose code crosses one of the
|
||||
// interpreter's intentional boundaries, are not copied, so the vendored tree is exactly what
|
||||
// test/test262.test.ts runs.
|
||||
//
|
||||
// Usage: bun run script/sync-test262.ts /path/to/test262
|
||||
import path from "node:path"
|
||||
import { rm } from "node:fs/promises"
|
||||
|
||||
type Frontmatter = { flags?: Array<string>; features?: Array<string>; includes?: Array<string> }
|
||||
|
||||
const root = path.resolve(import.meta.dir, "../test/test262")
|
||||
const manifest = (await Bun.file(path.join(root, "manifest.json")).json()) as {
|
||||
revision: string
|
||||
directories: Array<string>
|
||||
harness: Array<string>
|
||||
flags: Array<string>
|
||||
features: Array<string>
|
||||
boundaries: Record<string, string>
|
||||
}
|
||||
const boundaries = Object.entries(manifest.boundaries).map(([name, pattern]) => [name, new RegExp(pattern)] as const)
|
||||
const checkout = process.argv[2]
|
||||
if (checkout === undefined) {
|
||||
console.error("usage: bun run script/sync-test262.ts /path/to/test262")
|
||||
process.exit(1)
|
||||
}
|
||||
const head = (await Bun.$`git -C ${checkout} rev-parse HEAD`.text()).trim()
|
||||
if (head !== manifest.revision) {
|
||||
console.error(`checkout is at ${head}; manifest pins ${manifest.revision}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const excluded = new Map<string, number>()
|
||||
let copied = 0
|
||||
for (const dir of manifest.directories) {
|
||||
await rm(path.join(root, dir), { recursive: true, force: true })
|
||||
const from = path.join(checkout, "test", dir)
|
||||
for await (const file of new Bun.Glob("**/*.js").scan({ cwd: from })) {
|
||||
if (file.endsWith("_FIXTURE.js")) continue
|
||||
const source = await Bun.file(path.join(from, file)).text()
|
||||
const start = source.indexOf("/*---")
|
||||
const end = source.indexOf("---*/", start)
|
||||
const meta = start === -1 ? {} : (Bun.YAML.parse(source.slice(start + 5, end)) as Frontmatter)
|
||||
const code = start === -1 ? source : source.slice(end + 5)
|
||||
const reason =
|
||||
meta.flags?.find((flag) => manifest.flags.includes(flag)) ??
|
||||
meta.features?.find((feature) => manifest.features.includes(feature)) ??
|
||||
meta.includes?.find((include) => !manifest.harness.includes(include)) ??
|
||||
boundaries.find(([, pattern]) => pattern.test(code))?.[0]
|
||||
if (reason !== undefined) {
|
||||
excluded.set(reason, (excluded.get(reason) ?? 0) + 1)
|
||||
continue
|
||||
}
|
||||
await Bun.write(path.join(root, dir, file), Bun.file(path.join(from, file)))
|
||||
copied++
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`copied ${copied} files`)
|
||||
for (const [reason, count] of [...excluded].sort((a, b) => b[1] - a[1])) {
|
||||
console.log(` excluded ${String(count).padStart(5)} ${reason}`)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Runs every vendored test262 file, including skipped ones, and groups failures by cause. Pass
|
||||
// --write to regenerate test/test262/skipped.txt from the current failures.
|
||||
//
|
||||
// Usage: bun run script/test262-report.ts [--write] [path-prefix]
|
||||
import path from "node:path"
|
||||
import { root, run, skipped } from "../test/test262/run.js"
|
||||
|
||||
const write = process.argv.includes("--write")
|
||||
const prefix = process.argv.slice(2).find((arg) => !arg.startsWith("--")) ?? ""
|
||||
const files = [...new Bun.Glob("**/*.js").scanSync({ cwd: root })].filter((file) => file.startsWith(prefix)).sort()
|
||||
|
||||
const failures: Array<{ file: string; reason: string }> = []
|
||||
const recovered: Array<string> = []
|
||||
for (const file of files) {
|
||||
const outcome = await run(file)
|
||||
if (outcome.status === "fail") failures.push({ file, reason: outcome.reason })
|
||||
if (outcome.status === "pass" && skipped.has(file)) recovered.push(file)
|
||||
}
|
||||
|
||||
// Collapse a reason to the part that identifies the cause rather than the test.
|
||||
const bucket = (reason: string) => {
|
||||
const syntax = reason.match(/Syntax '([A-Za-z]+)' is not supported/)
|
||||
if (syntax) return `unsupported syntax ${syntax[1]}`
|
||||
if (reason.startsWith("expected ")) return reason.replace(/ but got .*/, " but the program ran")
|
||||
return reason
|
||||
.replace(/^(\$DONE: |ExecutionFailure: |InvalidDataValue: |ParseError: |Uncaught: |Test262Error: |Error: )+/, "")
|
||||
.replace(/ \(line \d+, col \d+\)/, "")
|
||||
.replace(/^[\w$]+\.(\w+) is not a function/, ".$1 is not a function")
|
||||
.replace(/^[\w$]+ cannot be constructed/, "… cannot be constructed")
|
||||
.replace(/'[^']*'/g, "'…'")
|
||||
.slice(0, 100)
|
||||
}
|
||||
|
||||
const buckets = new Map<string, Array<string>>()
|
||||
for (const failure of failures) {
|
||||
const key = bucket(failure.reason)
|
||||
buckets.set(key, [...(buckets.get(key) ?? []), failure.file])
|
||||
}
|
||||
|
||||
console.log(`${files.length - failures.length} pass, ${failures.length} fail of ${files.length}\n`)
|
||||
for (const [key, list] of [...buckets].sort((a, b) => b[1].length - a[1].length)) {
|
||||
console.log(`${String(list.length).padStart(5)} ${key}`)
|
||||
for (const file of list.slice(0, 3)) console.log(` ${file}`)
|
||||
if (list.length > 3) console.log(` … ${list.length - 3} more`)
|
||||
}
|
||||
if (recovered.length > 0) {
|
||||
console.log(`\n${recovered.length} skipped files pass now; remove them from skipped.txt:`)
|
||||
for (const file of recovered) console.log(` ${file}`)
|
||||
}
|
||||
|
||||
if (write) {
|
||||
const lines = failures.map((failure) => `${failure.file} # ${bucket(failure.reason)}`)
|
||||
await Bun.write(path.join(root, "skipped.txt"), `${lines.join("\n")}\n`)
|
||||
console.log(`\nwrote ${lines.length} entries to skipped.txt`)
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
export * as Data from "./data.js"
|
||||
|
||||
import type { DiagnosticKind } from "./codemode.js"
|
||||
import {
|
||||
ownEntries,
|
||||
parseArrayIndex,
|
||||
ProgramArray,
|
||||
ProgramFunction,
|
||||
ProgramObject,
|
||||
set,
|
||||
} from "./interpreter/objects.js"
|
||||
import { Values } from "./values.js"
|
||||
|
||||
/** A null-prototype object owned by the program. */
|
||||
export type SafeObject = Record<string, unknown>
|
||||
|
||||
const MAX_VALUE_DEPTH = 32
|
||||
|
||||
export class ToolRuntimeError extends Error {
|
||||
@@ -23,15 +28,15 @@ export class ToolRuntimeError extends Error {
|
||||
}
|
||||
|
||||
/**
|
||||
* Brings a host-produced runtime value into the program: runtime values pass through, their host
|
||||
* counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects become
|
||||
* null-prototype copies. Arrays keep extra enumerable properties such as `index` and `groups`.
|
||||
* Brings a host-produced value into the program: program and runtime values pass through, their
|
||||
* host counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and host objects
|
||||
* and arrays are copied.
|
||||
*/
|
||||
export const toProgram = (value: unknown, label: string): unknown => copy(value, label, "program", 0, new Set())
|
||||
|
||||
/**
|
||||
* Brings host data into the program: Date and URL become strings, other host collections become
|
||||
* empty objects, and objects become null-prototype copies. Used for tool results and parsed JSON.
|
||||
* empty objects, and objects become program copies. Used for tool results and parsed JSON.
|
||||
*/
|
||||
export const fromData = (value: unknown, label: string): unknown => copy(value, label, "data", 0, new Set())
|
||||
|
||||
@@ -44,8 +49,7 @@ export const fromData = (value: unknown, label: string): unknown => copy(value,
|
||||
export const toData = (value: unknown, label: string, undefinedAs: "json" | "result" = "json"): unknown =>
|
||||
copy(value, label, undefinedAs, 0, new Set())
|
||||
|
||||
// "program" and "data" build program-owned null-prototype objects; "json" and "result" build
|
||||
// ordinary objects for the host.
|
||||
// "program" and "data" build program objects; "json" and "result" build ordinary objects for the host.
|
||||
type Mode = "program" | "data" | "json" | "result"
|
||||
|
||||
const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Set<object>): unknown => {
|
||||
@@ -64,9 +68,13 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se
|
||||
`${label} contains an un-awaited Promise; await tool calls (e.g. \`const result = await tools.ns.tool(...)\`) before using their results.`,
|
||||
)
|
||||
}
|
||||
if (value instanceof ProgramFunction && mode !== "program") {
|
||||
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`)
|
||||
}
|
||||
|
||||
const plain = mode === "program" || mode === "data"
|
||||
if (mode === "program") {
|
||||
if (Values.isValue(value)) return value
|
||||
if (value instanceof ProgramObject || Values.isValue(value)) return value
|
||||
if (value instanceof Date) return new Values.Date(value.getTime())
|
||||
if (value instanceof RegExp) return new Values.RegExp(value.source, value.flags)
|
||||
if (value instanceof Map) {
|
||||
@@ -85,7 +93,6 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se
|
||||
if (value instanceof URLSearchParams) return new Values.URLSearchParams(new URLSearchParams(value))
|
||||
}
|
||||
|
||||
const plain = mode === "program" || mode === "data"
|
||||
if (value instanceof Values.Date) return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null
|
||||
if (value instanceof Date) return Number.isFinite(value.getTime()) ? value.toISOString() : null
|
||||
if (value instanceof Values.URL) return value.url.href
|
||||
@@ -98,7 +105,7 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se
|
||||
value instanceof Set ||
|
||||
value instanceof URLSearchParams
|
||||
) {
|
||||
return plain ? (Object.create(null) as SafeObject) : {}
|
||||
return plain ? new ProgramObject() : {}
|
||||
}
|
||||
|
||||
if (seen.has(value)) {
|
||||
@@ -106,27 +113,48 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se
|
||||
}
|
||||
seen.add(value)
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
// Host output densifies holes to null like JSON; program copies keep them.
|
||||
const copied = plain
|
||||
? value.map((item) => copy(item, label, mode, depth + 1, seen))
|
||||
: Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null)
|
||||
if (mode === "program") {
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (Object.hasOwn(copied, key)) continue
|
||||
define(copied, key, copy(item, label, mode, depth + 1, seen))
|
||||
}
|
||||
if (value instanceof ProgramArray) {
|
||||
const copied = Array.from(value.items, (item) => copy(item, label, mode, depth + 1, seen) ?? null)
|
||||
seen.delete(value)
|
||||
return copied
|
||||
}
|
||||
if (value instanceof ProgramObject) {
|
||||
const copied: Record<string, unknown> = {}
|
||||
for (const [key, item] of ownEntries(value)) {
|
||||
const next = copy(item, label, mode, depth + 1, seen)
|
||||
if (next === undefined && mode === "json") continue
|
||||
define(copied, key, next)
|
||||
}
|
||||
seen.delete(value)
|
||||
return copied
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (plain) {
|
||||
const copied = new ProgramArray(value.map((item) => copy(item, label, mode, depth + 1, seen)))
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (parseArrayIndex(key) === undefined) set(copied, key, copy(item, label, mode, depth + 1, seen))
|
||||
}
|
||||
seen.delete(value)
|
||||
return copied
|
||||
}
|
||||
const copied = Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null)
|
||||
seen.delete(value)
|
||||
return copied
|
||||
}
|
||||
|
||||
const prototype = Object.getPrototypeOf(value)
|
||||
if (prototype !== Object.prototype && prototype !== null) {
|
||||
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`)
|
||||
}
|
||||
|
||||
const copied: SafeObject = plain ? (Object.create(null) as SafeObject) : {}
|
||||
if (plain) {
|
||||
const copied = new ProgramObject()
|
||||
for (const [key, item] of Object.entries(value)) set(copied, key, copy(item, label, mode, depth + 1, seen))
|
||||
seen.delete(value)
|
||||
return copied
|
||||
}
|
||||
const copied: Record<string, unknown> = {}
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
const next = copy(item, label, mode, depth + 1, seen)
|
||||
if (next === undefined && mode === "json") continue
|
||||
@@ -136,8 +164,8 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se
|
||||
return copied
|
||||
}
|
||||
|
||||
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object or
|
||||
// array never reaches the Object.prototype setter.
|
||||
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object
|
||||
// never reaches the Object.prototype setter.
|
||||
const define = (target: object, key: string, value: unknown): void => {
|
||||
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true })
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Effect } from "effect"
|
||||
import type { Diagnostic } from "../codemode.js"
|
||||
import { ToolError } from "../tool-error.js"
|
||||
import { type SafeObject, toData, ToolRuntimeError } from "../data.js"
|
||||
import { toData, ToolRuntimeError } from "../data.js"
|
||||
import { type AstNode, formatLocation, InterpreterRuntimeError, ProgramThrow, sourceLocation } from "./model.js"
|
||||
import { containsRuntimeReference } from "./references.js"
|
||||
import { type HostCall, HostFunction } from "./host.js"
|
||||
import { get, ProgramError, ProgramObject } from "./objects.js"
|
||||
import { type Runner } from "./runner.js"
|
||||
import {
|
||||
coerceToString,
|
||||
@@ -44,12 +45,8 @@ export const normalizeError = (error: unknown): Diagnostic => {
|
||||
message = "a non-data value"
|
||||
} else if (typeof value === "string") {
|
||||
message = value
|
||||
} else if (
|
||||
value !== null &&
|
||||
typeof value === "object" &&
|
||||
typeof (value as { message?: unknown }).message === "string"
|
||||
) {
|
||||
message = (value as { message: string }).message
|
||||
} else if (value instanceof ProgramObject && typeof get(value, "message") === "string") {
|
||||
message = get(value, "message") as string
|
||||
} else {
|
||||
try {
|
||||
message = JSON.stringify(toData(value, "Thrown value")) ?? String(value)
|
||||
@@ -87,14 +84,14 @@ export const caughtErrorValue = (thrown: unknown): unknown => {
|
||||
return createErrorValue(name, normalizeError(thrown).message)
|
||||
}
|
||||
|
||||
const constructErrorValue = (name: string, args: Array<unknown>): SafeObject =>
|
||||
const constructErrorValue = (name: string, args: Array<unknown>): ProgramError =>
|
||||
createErrorValue(name, args[0] === undefined ? "" : coerceToString(args[0]))
|
||||
|
||||
const constructAggregateErrorValue = <R>(
|
||||
runner: Runner<R>,
|
||||
args: Array<unknown>,
|
||||
node: AstNode,
|
||||
): Effect.Effect<SafeObject, unknown, R> =>
|
||||
): Effect.Effect<ProgramError, unknown, R> =>
|
||||
Effect.gen(function* () {
|
||||
const cursor = yield* runner.syncIterator(args[0], node)
|
||||
if (cursor === undefined) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { DataValue, Diagnostic, ResolvedExecutionLimits, Result } from "../
|
||||
import { toData } from "../data.js"
|
||||
import { ToolRuntime } from "../tool-runtime.js"
|
||||
import { normalizeError } from "./errors.js"
|
||||
import type { Host } from "./globals.js"
|
||||
import { InterpreterRuntimeError } from "./model.js"
|
||||
import { PromiseRuntime } from "./promises.js"
|
||||
import { Runtime } from "./runtime.js"
|
||||
@@ -16,6 +17,7 @@ export const executeProgram = <R>(
|
||||
prepared: ToolRuntime.Prepared<R>,
|
||||
limits: ResolvedExecutionLimits,
|
||||
hooks: ToolRuntime.ToolCallHooks<R>,
|
||||
extraGlobals?: (host: Host<R>) => ReadonlyArray<readonly [string, unknown]>,
|
||||
): Effect.Effect<Result, never, R> => {
|
||||
if (code.trim().length === 0) {
|
||||
return Effect.succeed({
|
||||
@@ -39,7 +41,14 @@ export const executeProgram = <R>(
|
||||
Effect.gen(function* () {
|
||||
const program = parseProgram(code)
|
||||
const promises = new PromiseRuntime<R>(scope)
|
||||
const value = yield* new Runtime<R>(tools.execute, tools.search, tools.keys, promises, logs).run(program)
|
||||
const value = yield* new Runtime<R>(
|
||||
tools.execute,
|
||||
tools.search,
|
||||
tools.keys,
|
||||
promises,
|
||||
logs,
|
||||
extraGlobals,
|
||||
).run(program)
|
||||
const result = toData(value, "Execution result", "result") as DataValue
|
||||
returned = { value: result, promises }
|
||||
const warnings = yield* promises.interrupt()
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Effect } from "effect"
|
||||
import { type SafeObject, toProgram } from "../data.js"
|
||||
import { toProgram } from "../data.js"
|
||||
import { dateSetterArgumentCount, invokeDateMethod } from "../stdlib/date.js"
|
||||
import { invokeNumberMethod } from "../stdlib/number.js"
|
||||
import { invokeRegExpMethod, matchToValue, toHostRegex } from "../stdlib/regexp.js"
|
||||
import { invokeURLMethod, uriArgument } from "../stdlib/url.js"
|
||||
import { coerceToNumber, coerceToString, errorBrandName } from "../stdlib/value.js"
|
||||
import { coerceToNumber, coerceToString } from "../stdlib/value.js"
|
||||
import { compareText } from "../tool-runtime.js"
|
||||
import { Values } from "../values.js"
|
||||
import { type AstNode, IntrinsicReference, InterpreterRuntimeError } from "./model.js"
|
||||
import { get, ProgramArray, ProgramObject, record } from "./objects.js"
|
||||
import { containsOpaqueReference, rejectCircularInsertion, typeofValue } from "./references.js"
|
||||
import { applyCollectionCallback, isSupportedCallback, type Runner, toPrimitive } from "./runner.js"
|
||||
|
||||
@@ -16,6 +17,14 @@ export const invokeIntrinsic = <R>(
|
||||
ref: IntrinsicReference,
|
||||
args: Array<unknown>,
|
||||
node: AstNode,
|
||||
): Effect.Effect<unknown, unknown, R> =>
|
||||
Effect.map(invoke(runner, ref, args, node), (result) => (Array.isArray(result) ? new ProgramArray(result) : result))
|
||||
|
||||
const invoke = <R>(
|
||||
runner: Runner<R>,
|
||||
ref: IntrinsicReference,
|
||||
args: Array<unknown>,
|
||||
node: AstNode,
|
||||
): Effect.Effect<unknown, unknown, R> => {
|
||||
if (typeof ref.receiver === "string") {
|
||||
if (ref.name === "replace" || ref.name === "replaceAll") {
|
||||
@@ -32,7 +41,7 @@ export const invokeIntrinsic = <R>(
|
||||
if (typeof ref.receiver === "number") {
|
||||
return Effect.succeed(invokeNumberMethod(ref.receiver, ref.name, args, node))
|
||||
}
|
||||
if (Array.isArray(ref.receiver)) {
|
||||
if (ref.receiver instanceof ProgramArray) {
|
||||
return invokeArrayMethod(runner, ref.receiver, ref.name, args, node)
|
||||
}
|
||||
if (ref.receiver instanceof Values.Date) {
|
||||
@@ -215,7 +224,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
|
||||
node,
|
||||
)
|
||||
}
|
||||
return Array.from(value.matchAll(pattern), matchToValue)
|
||||
return new ProgramArray(Array.from(value.matchAll(pattern), matchToValue))
|
||||
}
|
||||
case "search": {
|
||||
result = value.search(toHostRegex(args[0], name, node))
|
||||
@@ -288,13 +297,7 @@ const invokeStringReplacer = <R>(
|
||||
if (typeof match !== "string" || typeof offset !== "number") {
|
||||
throw new InterpreterRuntimeError(`String.${name} produced an invalid replacement match.`, node)
|
||||
}
|
||||
if (hasGroups) {
|
||||
const safeGroups: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, group] of Object.entries(groups)) {
|
||||
safeGroups[key] = group
|
||||
}
|
||||
callbackArgs[callbackArgs.length - 1] = safeGroups
|
||||
}
|
||||
if (hasGroups) callbackArgs[callbackArgs.length - 1] = record(groups as Record<string, unknown>)
|
||||
matches.push({ match, offset, args: callbackArgs })
|
||||
return match
|
||||
}
|
||||
@@ -320,14 +323,11 @@ const invokeStringReplacer = <R>(
|
||||
let end = 0
|
||||
for (const match of matches) {
|
||||
const replacement = yield* apply(match.args)
|
||||
// Error values are branded plain objects; toProgram would strip the brand before coercion.
|
||||
output.push(
|
||||
value.slice(end, match.offset),
|
||||
replacement instanceof Values.Promise
|
||||
? "[object Promise]"
|
||||
: errorBrandName(replacement)
|
||||
? coerceToString(replacement)
|
||||
: coerceToString(toProgram(replacement, `String.${name} replacer result`)),
|
||||
: coerceToString(toProgram(replacement, `String.${name} replacer result`)),
|
||||
)
|
||||
end = match.offset + match.match.length
|
||||
}
|
||||
@@ -365,7 +365,7 @@ const invokeMapMethod = <R>(
|
||||
case "values":
|
||||
return Effect.sync(() => Array.from(target.map.values()))
|
||||
case "entries":
|
||||
return Effect.sync(() => Array.from(target.map.entries(), ([key, item]): Array<unknown> => [key, item]))
|
||||
return Effect.sync(() => Array.from(target.map.entries(), ([key, item]) => new ProgramArray([key, item])))
|
||||
case "forEach": {
|
||||
const apply = applyCollectionCallback(runner, args[0], "Map.forEach", node)
|
||||
return Effect.gen(function* () {
|
||||
@@ -404,7 +404,7 @@ const invokeSetMethod = <R>(
|
||||
case "values":
|
||||
return Effect.sync(() => Array.from(target.set.values()))
|
||||
case "entries":
|
||||
return Effect.sync(() => Array.from(target.set.values(), (item): Array<unknown> => [item, item]))
|
||||
return Effect.sync(() => Array.from(target.set.values(), (item) => new ProgramArray([item, item])))
|
||||
case "forEach": {
|
||||
const apply = applyCollectionCallback(runner, args[0], "Set.forEach", node)
|
||||
return Effect.gen(function* () {
|
||||
@@ -518,28 +518,27 @@ const loadSetRecord = <R>(runner: Runner<R>, source: unknown, name: string, node
|
||||
keys: () => Effect.succeed(source.map.keys()),
|
||||
})
|
||||
}
|
||||
if (source === null || typeof source !== "object" || Values.isValue(source)) {
|
||||
if (!(source instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError")
|
||||
}
|
||||
const object = source as Record<string, unknown>
|
||||
return Effect.gen(function* () {
|
||||
const size = yield* coerceNumericArgument(runner, object.size, node)
|
||||
const size = yield* coerceNumericArgument(runner, get(source, "size"), node)
|
||||
if (Number.isNaN(size)) {
|
||||
throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as(
|
||||
"TypeError",
|
||||
)
|
||||
}
|
||||
if (!isSupportedCallback(object.has) || !isSupportedCallback(object.keys)) {
|
||||
const has = get(source, "has")
|
||||
const keys = get(source, "keys")
|
||||
if (!isSupportedCallback(has) || !isSupportedCallback(keys)) {
|
||||
throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError")
|
||||
}
|
||||
const has = object.has
|
||||
const keys = object.keys
|
||||
return {
|
||||
size: Math.max(Math.trunc(size), 0),
|
||||
has: (item: unknown) => Effect.map(runner.invokeCallable(has, [item], node), Boolean),
|
||||
keys: () =>
|
||||
Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => {
|
||||
if (Array.isArray(result)) return Effect.succeed(result)
|
||||
if (result instanceof ProgramArray) return Effect.succeed(result.items)
|
||||
throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError")
|
||||
}),
|
||||
}
|
||||
@@ -604,7 +603,7 @@ const invokeURLSearchParamsMethod = <R>(
|
||||
case "values":
|
||||
return Effect.sync(() => Array.from(target.params.values()))
|
||||
case "entries":
|
||||
return Effect.sync(() => Array.from(target.params.entries(), ([key, value]): Array<unknown> => [key, value]))
|
||||
return Effect.sync(() => Array.from(target.params.entries(), ([key, value]) => new ProgramArray([key, value])))
|
||||
case "toString":
|
||||
return Effect.sync(() => target.params.toString())
|
||||
case "forEach": {
|
||||
@@ -622,11 +621,12 @@ const invokeURLSearchParamsMethod = <R>(
|
||||
|
||||
const invokeArrayMethod = <R>(
|
||||
runner: Runner<R>,
|
||||
target: Array<unknown>,
|
||||
receiver: ProgramArray,
|
||||
name: string,
|
||||
args: Array<unknown>,
|
||||
node: AstNode,
|
||||
): Effect.Effect<unknown, unknown, R> => {
|
||||
const target = receiver.items
|
||||
const optNumber = (value: unknown, label: string): number | undefined => {
|
||||
if (value === undefined) return undefined
|
||||
if (typeof value !== "number")
|
||||
@@ -638,9 +638,8 @@ const invokeArrayMethod = <R>(
|
||||
if (args.length > 1 || (args.length === 1 && typeof args[0] !== "string")) {
|
||||
throw new InterpreterRuntimeError("Array.join expects zero arguments or one string separator.", node)
|
||||
}
|
||||
const input = toProgram(target, "Array.join input") as Array<unknown>
|
||||
return Effect.succeed(
|
||||
input.map((item) => coerceToString(item ?? "")).join(args.length === 0 ? "," : (args[0] as string)),
|
||||
target.map((item) => coerceToString(item ?? "")).join(args.length === 0 ? "," : (args[0] as string)),
|
||||
)
|
||||
}
|
||||
case "includes":
|
||||
@@ -660,11 +659,15 @@ const invokeArrayMethod = <R>(
|
||||
case "slice":
|
||||
return Effect.succeed(target.slice(optNumber(args[0], "start"), optNumber(args[1], "end")))
|
||||
case "concat":
|
||||
return Effect.succeed(target.concat(...args))
|
||||
case "flat":
|
||||
return Effect.succeed(target.flat(optNumber(args[0], "depth") ?? 1))
|
||||
return Effect.succeed(target.concat(...args.map((item) => (item instanceof ProgramArray ? item.items : item))))
|
||||
case "flat": {
|
||||
const flatten = (items: Array<unknown>, depth: number): Array<unknown> =>
|
||||
items.flatMap((item) => (item instanceof ProgramArray && depth > 0 ? flatten(item.items, depth - 1) : [item]))
|
||||
return Effect.succeed(flatten(target, optNumber(args[0], "depth") ?? 1))
|
||||
}
|
||||
case "reverse":
|
||||
return Effect.succeed(target.reverse())
|
||||
target.reverse()
|
||||
return Effect.succeed(receiver)
|
||||
case "sort": {
|
||||
const length = target.length
|
||||
const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(target, index)).filter((own) => !own).length
|
||||
@@ -676,7 +679,7 @@ const invokeArrayMethod = <R>(
|
||||
Array.from({ length: holeCount }, (_, index) => itemCount + index).forEach((index) => {
|
||||
Reflect.deleteProperty(target, index)
|
||||
})
|
||||
return target
|
||||
return receiver
|
||||
})
|
||||
}
|
||||
case "toSorted":
|
||||
@@ -695,12 +698,12 @@ const invokeArrayMethod = <R>(
|
||||
}
|
||||
case "push": {
|
||||
// Validate all insertions before mutating to avoid partial cyclic updates.
|
||||
for (const item of args) rejectCircularInsertion(target, item, "Array.push result", node)
|
||||
for (const item of args) rejectCircularInsertion(receiver, item, "Array.push result", node)
|
||||
target.push(...args)
|
||||
return Effect.succeed(target.length)
|
||||
}
|
||||
case "unshift": {
|
||||
for (const item of args) rejectCircularInsertion(target, item, "Array.unshift result", node)
|
||||
for (const item of args) rejectCircularInsertion(receiver, item, "Array.unshift result", node)
|
||||
target.unshift(...args)
|
||||
return Effect.succeed(target.length)
|
||||
}
|
||||
@@ -714,7 +717,7 @@ const invokeArrayMethod = <R>(
|
||||
if (args.length === 1) return Effect.succeed(target.splice(start))
|
||||
const deleteCount = optNumber(args[1], "delete count") ?? 0
|
||||
const inserted = args.slice(2)
|
||||
for (const item of inserted) rejectCircularInsertion(target, item, "Array.splice result", node)
|
||||
for (const item of inserted) rejectCircularInsertion(receiver, item, "Array.splice result", node)
|
||||
return Effect.succeed(target.splice(start, deleteCount, ...inserted))
|
||||
}
|
||||
case "toSpliced": {
|
||||
@@ -731,23 +734,23 @@ const invokeArrayMethod = <R>(
|
||||
return Effect.succeed(copied)
|
||||
}
|
||||
case "fill": {
|
||||
rejectCircularInsertion(target, args[0], "Array.fill result", node)
|
||||
return Effect.succeed(target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end")))
|
||||
rejectCircularInsertion(receiver, args[0], "Array.fill result", node)
|
||||
target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end"))
|
||||
return Effect.succeed(receiver)
|
||||
}
|
||||
case "copyWithin":
|
||||
return Effect.succeed(
|
||||
target.copyWithin(
|
||||
optNumber(args[0], "target index") ?? 0,
|
||||
optNumber(args[1], "start") ?? 0,
|
||||
optNumber(args[2], "end"),
|
||||
),
|
||||
target.copyWithin(
|
||||
optNumber(args[0], "target index") ?? 0,
|
||||
optNumber(args[1], "start") ?? 0,
|
||||
optNumber(args[2], "end"),
|
||||
)
|
||||
return Effect.succeed(receiver)
|
||||
case "keys":
|
||||
return Effect.succeed(Array.from(target.keys()))
|
||||
case "values":
|
||||
return Effect.succeed([...target])
|
||||
case "entries":
|
||||
return Effect.succeed(Array.from(target.entries(), ([index, item]): Array<unknown> => [index, item]))
|
||||
return Effect.succeed(Array.from(target.entries(), ([index, item]) => new ProgramArray([index, item])))
|
||||
}
|
||||
|
||||
const apply = applyCollectionCallback(runner, args[0], `Array.${name}`, node)
|
||||
@@ -760,7 +763,7 @@ const invokeArrayMethod = <R>(
|
||||
values.length = length
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
values[index] = yield* apply([target[index], index, target])
|
||||
values[index] = yield* apply([target[index], index, receiver])
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -768,8 +771,8 @@ const invokeArrayMethod = <R>(
|
||||
const values: Array<unknown> = []
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
const mapped = yield* apply([target[index], index, target])
|
||||
if (Array.isArray(mapped)) values.push(...mapped)
|
||||
const mapped = yield* apply([target[index], index, receiver])
|
||||
if (mapped instanceof ProgramArray) values.push(...mapped.items)
|
||||
else values.push(mapped)
|
||||
}
|
||||
return values
|
||||
@@ -779,36 +782,36 @@ const invokeArrayMethod = <R>(
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
const item = target[index]
|
||||
if (yield* apply([item, index, target])) values.push(item)
|
||||
if (yield* apply([item, index, receiver])) values.push(item)
|
||||
}
|
||||
return values
|
||||
}
|
||||
case "find":
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
const item = target[index]
|
||||
if (yield* apply([item, index, target])) return item
|
||||
if (yield* apply([item, index, receiver])) return item
|
||||
}
|
||||
return undefined
|
||||
case "findIndex":
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (yield* apply([target[index], index, target])) return index
|
||||
if (yield* apply([target[index], index, receiver])) return index
|
||||
}
|
||||
return -1
|
||||
case "some":
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
if (yield* apply([target[index], index, target])) return true
|
||||
if (yield* apply([target[index], index, receiver])) return true
|
||||
}
|
||||
return false
|
||||
case "every":
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
if (!(yield* apply([target[index], index, target]))) return false
|
||||
if (!(yield* apply([target[index], index, receiver]))) return false
|
||||
}
|
||||
return true
|
||||
case "forEach":
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
if (index in target) yield* apply([target[index], index, target])
|
||||
if (index in target) yield* apply([target[index], index, receiver])
|
||||
}
|
||||
return undefined
|
||||
case "reduce": {
|
||||
@@ -825,7 +828,7 @@ const invokeArrayMethod = <R>(
|
||||
}
|
||||
for (let index = start; index < length; index += 1) {
|
||||
if (!(index in target)) continue
|
||||
accumulator = yield* apply([accumulator, target[index], index, target])
|
||||
accumulator = yield* apply([accumulator, target[index], index, receiver])
|
||||
}
|
||||
return accumulator
|
||||
}
|
||||
@@ -843,19 +846,19 @@ const invokeArrayMethod = <R>(
|
||||
}
|
||||
for (let index = start; index >= 0; index -= 1) {
|
||||
if (!(index in target)) continue
|
||||
accumulator = yield* apply([accumulator, target[index], index, target])
|
||||
accumulator = yield* apply([accumulator, target[index], index, receiver])
|
||||
}
|
||||
return accumulator
|
||||
}
|
||||
case "findLast":
|
||||
for (let index = length - 1; index >= 0; index -= 1) {
|
||||
const item = target[index]
|
||||
if (yield* apply([item, index, target])) return item
|
||||
if (yield* apply([item, index, receiver])) return item
|
||||
}
|
||||
return undefined
|
||||
case "findLastIndex":
|
||||
for (let index = length - 1; index >= 0; index -= 1) {
|
||||
if (yield* apply([target[index], index, target])) return index
|
||||
if (yield* apply([target[index], index, receiver])) return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { BlockStatement, Expression, Node, Pattern } from "acorn"
|
||||
import type { Node } from "acorn"
|
||||
import type { Effect } from "effect"
|
||||
import type { DiagnosticKind } from "../codemode.js"
|
||||
import type { SafeObject } from "../data.js"
|
||||
import type { ProgramObject } from "./objects.js"
|
||||
import type { Values } from "../values.js"
|
||||
|
||||
/** Any parsed node; the interpreter narrows on `type` and reads `loc` for diagnostics. */
|
||||
@@ -20,20 +20,10 @@ export type StatementResult =
|
||||
| { kind: "continue"; label?: string }
|
||||
|
||||
export type MemberReference = {
|
||||
target: SafeObject | Array<unknown> | Values.RegExp | Values.URL
|
||||
target: ProgramObject | Values.RegExp | Values.URL
|
||||
key: PropertyKey
|
||||
}
|
||||
|
||||
export class CodeModeFunction {
|
||||
constructor(
|
||||
readonly parameters: ReadonlyArray<Pattern>,
|
||||
readonly body: BlockStatement | Expression,
|
||||
readonly capturedScopes: ReadonlyArray<Map<string, Binding>>,
|
||||
readonly async: boolean,
|
||||
readonly generator: boolean,
|
||||
) {}
|
||||
}
|
||||
|
||||
export type GeneratorRequestKind = "next" | "return" | "throw"
|
||||
|
||||
export class CodeModeGenerator {
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import type { BlockStatement, Expression, Pattern } from "acorn"
|
||||
import { AsyncIteratorSymbol, type Binding, IteratorSymbol } from "./model.js"
|
||||
|
||||
/** An object owned by the program: own properties plus a prototype link. */
|
||||
export class ProgramObject {
|
||||
readonly props = new Map<PropertyKey, unknown>()
|
||||
constructor(public proto: ProgramObject | null = null) {}
|
||||
}
|
||||
|
||||
export class ProgramArray extends ProgramObject {
|
||||
constructor(readonly items: Array<unknown> = []) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
export class ProgramError extends ProgramObject {
|
||||
constructor(readonly errorName: string) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
export class ProgramFunction extends ProgramObject {
|
||||
readonly length: number
|
||||
constructor(
|
||||
readonly name: string,
|
||||
readonly parameters: ReadonlyArray<Pattern>,
|
||||
readonly body: BlockStatement | Expression,
|
||||
readonly capturedScopes: ReadonlyArray<Map<string, Binding>>,
|
||||
readonly async: boolean,
|
||||
readonly generator: boolean,
|
||||
) {
|
||||
super()
|
||||
const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement")
|
||||
this.length = optional === -1 ? parameters.length : optional
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_ARRAY_LENGTH = 4_294_967_295
|
||||
|
||||
export const parseArrayIndex = (key: string | number): number | undefined => {
|
||||
const property = String(key)
|
||||
if (!/^(0|[1-9]\d*)$/.test(property)) return undefined
|
||||
const index = Number(property)
|
||||
return index < MAX_ARRAY_LENGTH ? index : undefined
|
||||
}
|
||||
|
||||
const canonical = (key: PropertyKey): string | symbol => (typeof key === "symbol" ? key : String(key))
|
||||
|
||||
const index = (target: ProgramObject, key: string | symbol): number | undefined =>
|
||||
target instanceof ProgramArray && typeof key === "string" ? parseArrayIndex(key) : undefined
|
||||
|
||||
// Non-enumerable built-in properties: array length, function name and length.
|
||||
const builtin = (target: ProgramObject, name: string | symbol): boolean =>
|
||||
(name === "length" && target instanceof ProgramArray) ||
|
||||
((name === "name" || name === "length") && target instanceof ProgramFunction)
|
||||
|
||||
export const hasOwn = (target: ProgramObject, key: PropertyKey): boolean => {
|
||||
const name = canonical(key)
|
||||
const at = index(target, name)
|
||||
if (at !== undefined) return at in (target as ProgramArray).items
|
||||
return builtin(target, name) || target.props.has(name)
|
||||
}
|
||||
|
||||
export const getOwn = (target: ProgramObject, key: PropertyKey): unknown => {
|
||||
const name = canonical(key)
|
||||
const at = index(target, name)
|
||||
if (at !== undefined) return (target as ProgramArray).items[at]
|
||||
if (target instanceof ProgramArray && name === "length") return target.items.length
|
||||
if (target instanceof ProgramFunction && name === "name") return target.name
|
||||
if (target instanceof ProgramFunction && name === "length") return target.length
|
||||
return target.props.get(name)
|
||||
}
|
||||
|
||||
export const get = (target: ProgramObject, key: PropertyKey): unknown => {
|
||||
for (let current: ProgramObject | null = target; current !== null; current = current.proto) {
|
||||
if (hasOwn(current, key)) return getOwn(current, key)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const has = (target: ProgramObject, key: PropertyKey): boolean => {
|
||||
for (let current: ProgramObject | null = target; current !== null; current = current.proto) {
|
||||
if (hasOwn(current, key)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const set = (target: ProgramObject, key: PropertyKey, value: unknown): boolean => {
|
||||
const name = canonical(key)
|
||||
const at = index(target, name)
|
||||
if (at !== undefined) {
|
||||
;(target as ProgramArray).items[at] = value
|
||||
return true
|
||||
}
|
||||
if (name === "length" && target instanceof ProgramArray) {
|
||||
const length = typeof value === "number" ? value : Number(value)
|
||||
if (!Number.isInteger(length) || length < 0 || length > 4_294_967_295) return false
|
||||
target.items.length = length
|
||||
return true
|
||||
}
|
||||
if (builtin(target, name)) return false
|
||||
target.props.set(name, value)
|
||||
return true
|
||||
}
|
||||
|
||||
export const remove = (target: ProgramObject, key: PropertyKey): boolean => {
|
||||
const name = canonical(key)
|
||||
const at = index(target, name)
|
||||
if (at !== undefined) return delete (target as ProgramArray).items[at]
|
||||
if (name === "length" && target instanceof ProgramArray) return false
|
||||
if (builtin(target, name)) return true
|
||||
target.props.delete(name)
|
||||
return true
|
||||
}
|
||||
|
||||
// JS order: array indexes, integer-like keys ascending, other strings, then symbols.
|
||||
export const ownKeys = (target: ProgramObject): Array<string | symbol> => {
|
||||
const strings = [...target.props.keys()].filter((key): key is string => typeof key === "string")
|
||||
const symbols = [...target.props.keys()].filter((key): key is symbol => typeof key === "symbol")
|
||||
return [
|
||||
...(target instanceof ProgramArray ? Object.keys(target.items) : []),
|
||||
...strings.filter((key) => parseArrayIndex(key) !== undefined).sort((a, b) => Number(a) - Number(b)),
|
||||
...strings.filter((key) => parseArrayIndex(key) === undefined),
|
||||
...symbols,
|
||||
]
|
||||
}
|
||||
|
||||
export const ownEntries = (target: ProgramObject): Array<[string, unknown]> =>
|
||||
ownKeys(target)
|
||||
.filter((key): key is string => typeof key === "string")
|
||||
.map((key) => [key, getOwn(target, key)])
|
||||
|
||||
export const record = (entries: Record<string, unknown>): ProgramObject => {
|
||||
const target = new ProgramObject()
|
||||
for (const [key, value] of Object.entries(entries)) set(target, key, value)
|
||||
return target
|
||||
}
|
||||
|
||||
export const assign = (target: ProgramObject, source: ProgramObject, skip?: ReadonlySet<PropertyKey>): void => {
|
||||
for (const key of ownKeys(source)) {
|
||||
if (skip?.has(key)) continue
|
||||
if (typeof key === "symbol" && key !== IteratorSymbol && key !== AsyncIteratorSymbol) continue
|
||||
set(target, key, getOwn(source, key))
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
import { Cause, Deferred, Effect, Exit, Fiber, Scope } from "effect"
|
||||
import type { Diagnostic } from "../codemode.js"
|
||||
import type { SafeObject } from "../data.js"
|
||||
import {
|
||||
type AstNode,
|
||||
CodeModeFunction,
|
||||
InterpreterRuntimeError,
|
||||
ProgramThrow,
|
||||
PromiseInstanceMethodReference,
|
||||
} from "./model.js"
|
||||
import { type AstNode, InterpreterRuntimeError, ProgramThrow, PromiseInstanceMethodReference } from "./model.js"
|
||||
import { get, ProgramArray, ProgramFunction, ProgramObject, record } from "./objects.js"
|
||||
import { HostFunction, requiresNew, sync } from "./host.js"
|
||||
import { caughtErrorValue, normalizeError } from "./errors.js"
|
||||
import { typeofValue } from "./references.js"
|
||||
@@ -111,8 +105,8 @@ export const resolvePromiseValue = <R>(
|
||||
): Effect.Effect<unknown, unknown, R> => {
|
||||
if (own?.promise !== undefined && value === own.promise) return Effect.fail(selfResolutionError(node))
|
||||
if (value instanceof Values.Promise) return runner.settlePromise(value)
|
||||
if (value === null || typeof value !== "object" || !Object.hasOwn(value, "then")) return Effect.succeed(value)
|
||||
const then = (value as SafeObject).then
|
||||
if (!(value instanceof ProgramObject)) return Effect.succeed(value)
|
||||
const then = get(value, "then")
|
||||
if (typeofValue(then) !== "function") return Effect.succeed(value)
|
||||
|
||||
return Effect.gen(function* () {
|
||||
@@ -174,10 +168,12 @@ const invokePromiseMethod = <R>(
|
||||
}
|
||||
|
||||
if (name === "all") {
|
||||
return yield* settleAfterTurn(
|
||||
Effect.all(
|
||||
items.map((item) => Effect.flatten(promises.await(item))),
|
||||
{ concurrency: "unbounded" },
|
||||
return new ProgramArray(
|
||||
yield* settleAfterTurn(
|
||||
Effect.all(
|
||||
items.map((item) => Effect.flatten(promises.await(item))),
|
||||
{ concurrency: "unbounded" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -186,19 +182,14 @@ const invokePromiseMethod = <R>(
|
||||
for (const item of items) {
|
||||
const exit = yield* promises.await(item)
|
||||
if (Exit.isSuccess(exit)) {
|
||||
outcomes.push(Object.assign(Object.create(null) as SafeObject, { status: "fulfilled", value: exit.value }))
|
||||
outcomes.push(record({ status: "fulfilled", value: exit.value }))
|
||||
continue
|
||||
}
|
||||
if (Cause.hasInterruptsOnly(exit.cause)) return yield* Effect.failCause(exit.cause)
|
||||
outcomes.push(
|
||||
Object.assign(Object.create(null) as SafeObject, {
|
||||
status: "rejected",
|
||||
reason: caughtErrorValue(Cause.squash(exit.cause)),
|
||||
}),
|
||||
)
|
||||
outcomes.push(record({ status: "rejected", reason: caughtErrorValue(Cause.squash(exit.cause)) }))
|
||||
}
|
||||
yield* Effect.yieldNow
|
||||
return outcomes
|
||||
return new ProgramArray(outcomes)
|
||||
}
|
||||
if (name === "race") {
|
||||
if (items.length === 0) {
|
||||
@@ -253,7 +244,7 @@ const constructPromise = <R>(
|
||||
executor: unknown,
|
||||
node: AstNode,
|
||||
): Effect.Effect<Values.Promise, unknown, R> => {
|
||||
if (!(executor instanceof CodeModeFunction)) {
|
||||
if (!(executor instanceof ProgramFunction)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
"new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).",
|
||||
node,
|
||||
|
||||
@@ -3,20 +3,18 @@ import { Values } from "../values.js"
|
||||
import { HostFunction, HostNamespace } from "./host.js"
|
||||
import {
|
||||
type AstNode,
|
||||
AsyncIteratorSymbol,
|
||||
CodeModeFunction,
|
||||
CodeModeGenerator,
|
||||
GeneratorMethodReference,
|
||||
InterpreterRuntimeError,
|
||||
IntrinsicReference,
|
||||
IteratorSymbol,
|
||||
PromiseInstanceMethodReference,
|
||||
} from "./model.js"
|
||||
import { getOwn, ownKeys, ProgramArray, ProgramFunction, ProgramObject } from "./objects.js"
|
||||
|
||||
export const isRuntimeReference = (value: unknown): boolean =>
|
||||
value instanceof HostFunction ||
|
||||
value instanceof HostNamespace ||
|
||||
value instanceof CodeModeFunction ||
|
||||
value instanceof ProgramFunction ||
|
||||
value instanceof CodeModeGenerator ||
|
||||
value instanceof GeneratorMethodReference ||
|
||||
value instanceof ToolReference ||
|
||||
@@ -26,11 +24,8 @@ export const isRuntimeReference = (value: unknown): boolean =>
|
||||
Values.isValue(value)
|
||||
|
||||
function* childValues(value: object): Generator {
|
||||
for (const key of Reflect.ownKeys(value)) {
|
||||
if (!Object.prototype.propertyIsEnumerable.call(value, key)) continue
|
||||
if (typeof key === "symbol" && key !== AsyncIteratorSymbol && key !== IteratorSymbol) continue
|
||||
yield Reflect.get(value, key)
|
||||
}
|
||||
if (!(value instanceof ProgramObject)) return
|
||||
for (const key of ownKeys(value)) yield getOwn(value, key)
|
||||
}
|
||||
|
||||
// Depth-first search over a value tree. `match` stops the walk; `skip` prunes a subtree without matching it.
|
||||
@@ -79,7 +74,7 @@ export const rejectCircularInsertion = (
|
||||
|
||||
export const describeValue = (value: unknown): string => {
|
||||
if (value === null) return "null"
|
||||
if (Array.isArray(value)) return "an array"
|
||||
if (value instanceof ProgramArray) return "an array"
|
||||
if (value instanceof Values.Promise) return "an un-awaited Promise"
|
||||
if (value instanceof ToolReference) return "a tool reference"
|
||||
if (value instanceof Values.Date) return "a Date"
|
||||
@@ -97,7 +92,7 @@ export const describeValue = (value: unknown): string => {
|
||||
export const typeofValue = (value: unknown): string => {
|
||||
if (
|
||||
value instanceof HostFunction ||
|
||||
value instanceof CodeModeFunction ||
|
||||
value instanceof ProgramFunction ||
|
||||
value instanceof GeneratorMethodReference ||
|
||||
value instanceof IntrinsicReference ||
|
||||
value instanceof PromiseInstanceMethodReference
|
||||
@@ -108,12 +103,3 @@ export const typeofValue = (value: unknown): string => {
|
||||
if (value instanceof ToolReference) return value.path.length > 0 ? "function" : "object"
|
||||
return typeof value
|
||||
}
|
||||
|
||||
const MAX_ARRAY_LENGTH = 4_294_967_295
|
||||
|
||||
export const parseArrayIndex = (key: string | number): number | undefined => {
|
||||
const property = String(key)
|
||||
if (!/^(0|[1-9]\d*)$/.test(property)) return undefined
|
||||
const index = Number(property)
|
||||
return index < MAX_ARRAY_LENGTH ? index : undefined
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Effect, Exit } from "effect"
|
||||
import { Values } from "../values.js"
|
||||
import { coerceToString } from "../stdlib/value.js"
|
||||
import { HostFunction } from "./host.js"
|
||||
import { type AstNode, CodeModeFunction, InterpreterRuntimeError, IntrinsicReference } from "./model.js"
|
||||
import { type AstNode, InterpreterRuntimeError, IntrinsicReference } from "./model.js"
|
||||
import { get, has, ProgramFunction, ProgramObject } from "./objects.js"
|
||||
import { typeofValue } from "./references.js"
|
||||
|
||||
export type IteratorCursor<R> = {
|
||||
@@ -12,7 +13,7 @@ export type IteratorCursor<R> = {
|
||||
|
||||
/** Everything a host function needs to call back into the program. */
|
||||
export type Runner<R> = {
|
||||
readonly invokeFunction: (fn: CodeModeFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
|
||||
readonly invokeFunction: (fn: ProgramFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
|
||||
readonly invokeCallable: (
|
||||
callable: unknown,
|
||||
args: Array<unknown>,
|
||||
@@ -42,13 +43,14 @@ export const toPrimitive = <R>(
|
||||
if (Values.isValue(value)) {
|
||||
return Effect.succeed(value instanceof Values.Date && hint === "number" ? value.time : coerceToString(value))
|
||||
}
|
||||
const object = value as Record<string, unknown>
|
||||
if (!(value instanceof ProgramObject)) return Effect.succeed(value)
|
||||
const order = hint === "number" ? ["valueOf", "toString"] : ["toString", "valueOf"]
|
||||
return Effect.gen(function* () {
|
||||
for (const method of order) {
|
||||
if (method === "toString" && !Object.hasOwn(object, "toString")) return coerceToString(value)
|
||||
if (!Object.hasOwn(object, method) || typeofValue(object[method]) !== "function") continue
|
||||
const result = yield* runner.invokeCallable(object[method], [], node)
|
||||
if (method === "toString" && !has(value, "toString")) return coerceToString(value)
|
||||
const callable = get(value, method)
|
||||
if (typeofValue(callable) !== "function") continue
|
||||
const result = yield* runner.invokeCallable(callable, [], node)
|
||||
if (result === null || (typeof result !== "object" && typeof result !== "function")) return result
|
||||
}
|
||||
throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError")
|
||||
@@ -59,10 +61,10 @@ export const toPrimitive = <R>(
|
||||
// Array.from mappers, and promise reactions all admit exactly these callables.
|
||||
// Admission means dispatchable, not necessarily invocable: new-requiring
|
||||
// constructors pass the gate and throw a TypeError on call, like JS.
|
||||
export type SupportedCallback = CodeModeFunction | HostFunction<unknown> | IntrinsicReference
|
||||
export type SupportedCallback = ProgramFunction | HostFunction<unknown> | IntrinsicReference
|
||||
|
||||
export const isSupportedCallback = (value: unknown): value is SupportedCallback =>
|
||||
value instanceof CodeModeFunction ||
|
||||
value instanceof ProgramFunction ||
|
||||
(value instanceof HostFunction && value.callback) ||
|
||||
value instanceof IntrinsicReference
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
ArrayExpression,
|
||||
ArrayPattern,
|
||||
AssignmentPattern,
|
||||
ArrowFunctionExpression,
|
||||
AssignmentExpression,
|
||||
AssignmentProperty,
|
||||
@@ -41,13 +42,12 @@ import type {
|
||||
YieldExpression,
|
||||
} from "acorn"
|
||||
import { Cause, Deferred, Effect, Exit } from "effect"
|
||||
import { ToolRuntimeError, type SafeObject, toProgram } from "../data.js"
|
||||
import { ToolRuntimeError, toProgram } from "../data.js"
|
||||
import { ToolReference } from "../tool-runtime.js"
|
||||
import {
|
||||
type AstNode,
|
||||
AsyncIteratorSymbol,
|
||||
type Binding,
|
||||
CodeModeFunction,
|
||||
CodeModeGenerator,
|
||||
ComputedValue,
|
||||
GeneratorMethodReference,
|
||||
@@ -55,9 +55,7 @@ import {
|
||||
type GeneratorRequestKind,
|
||||
IntrinsicReference,
|
||||
InterpreterRuntimeError,
|
||||
isRecord,
|
||||
IteratorSymbol,
|
||||
IteratorSymbols,
|
||||
type MemberReference,
|
||||
OptionalShortCircuit,
|
||||
PromiseInstanceMethodReference,
|
||||
@@ -66,16 +64,28 @@ import {
|
||||
unsupportedSyntax,
|
||||
} from "./model.js"
|
||||
import { caughtErrorValue } from "./errors.js"
|
||||
import { globals } from "./globals.js"
|
||||
import { globals, type Host } from "./globals.js"
|
||||
import { HostFunction, HostNamespace } from "./host.js"
|
||||
import { invokeIntrinsic } from "./methods.js"
|
||||
import {
|
||||
assign,
|
||||
get,
|
||||
has,
|
||||
ownKeys,
|
||||
parseArrayIndex,
|
||||
ProgramArray,
|
||||
ProgramFunction,
|
||||
ProgramObject,
|
||||
record,
|
||||
remove,
|
||||
set,
|
||||
} from "./objects.js"
|
||||
import { preserveConsumerError, type Runner } from "./runner.js"
|
||||
import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js"
|
||||
import {
|
||||
containsOpaqueReference,
|
||||
describeValue,
|
||||
isRuntimeReference,
|
||||
parseArrayIndex,
|
||||
rejectCircularInsertion,
|
||||
typeofValue,
|
||||
} from "./references.js"
|
||||
@@ -118,14 +128,11 @@ const calleeDescription = (callee: Expression | Super | undefined): string => {
|
||||
return "The called value"
|
||||
}
|
||||
|
||||
const hasOwn = (value: unknown, key: PropertyKey): boolean =>
|
||||
value !== null && typeof value === "object" && Object.hasOwn(value, key)
|
||||
|
||||
const constructorName = (value: unknown): string | undefined => {
|
||||
if (typeof value === "string") return "String"
|
||||
if (typeof value === "number") return "Number"
|
||||
if (typeof value === "boolean") return "Boolean"
|
||||
if (Array.isArray(value)) return "Array"
|
||||
if (value instanceof ProgramArray) return "Array"
|
||||
if (value instanceof Values.Date) return "Date"
|
||||
if (value instanceof Values.RegExp) return "RegExp"
|
||||
if (value instanceof Values.Map) return "Map"
|
||||
@@ -133,7 +140,7 @@ const constructorName = (value: unknown): string | undefined => {
|
||||
if (value instanceof Values.URL) return "URL"
|
||||
if (value instanceof Values.URLSearchParams) return "URLSearchParams"
|
||||
if (value instanceof Values.Promise) return "Promise"
|
||||
if (value === null || typeof value !== "object" || isRuntimeReference(value)) return undefined
|
||||
if (!(value instanceof ProgramObject) || value instanceof ProgramFunction) return undefined
|
||||
return errorBrandName(value) ?? "Object"
|
||||
}
|
||||
|
||||
@@ -230,7 +237,7 @@ const loopDeclaration = (left: VariableDeclaration | Pattern, statement: "for...
|
||||
}
|
||||
|
||||
type CustomIterator = {
|
||||
iterator: SafeObject | CodeModeGenerator
|
||||
iterator: ProgramObject | CodeModeGenerator
|
||||
next: unknown
|
||||
asynchronous: boolean
|
||||
}
|
||||
@@ -247,13 +254,6 @@ const isOpaqueMemberReference = (value: unknown): value is OpaqueMemberReference
|
||||
value instanceof IntrinsicReference ||
|
||||
value instanceof GeneratorMethodReference
|
||||
|
||||
const copyIteratorSymbols = (source: object, target: object, consumed?: ReadonlySet<PropertyKey>): void => {
|
||||
for (const symbol of IteratorSymbols) {
|
||||
if (!consumed?.has(symbol) && Object.hasOwn(source, symbol))
|
||||
Reflect.set(target, symbol, Reflect.get(source, symbol))
|
||||
}
|
||||
}
|
||||
|
||||
type GeneratorRequest = {
|
||||
kind: GeneratorRequestKind
|
||||
value: unknown
|
||||
@@ -285,6 +285,7 @@ export class Runtime<R> {
|
||||
readonly toolKeys: (path: ReadonlyArray<string>) => ReadonlyArray<string>,
|
||||
readonly promises: PromiseRuntime<R>,
|
||||
readonly logs: Array<string> = [],
|
||||
extraGlobals: (host: Host<R>) => ReadonlyArray<readonly [string, unknown]> = () => [],
|
||||
) {
|
||||
const globalScope = new Map<string, Binding>()
|
||||
// Calling back into the program never reads frame state, so any frame serves; the root is always alive.
|
||||
@@ -295,7 +296,7 @@ export class Runtime<R> {
|
||||
settlePromise: (promise) => this.root.settlePromise(promise),
|
||||
syncIterator: (value, node) => this.root.syncIterator(value, node),
|
||||
}
|
||||
this.builtins = new Map(globals(this))
|
||||
this.builtins = new Map([...globals(this), ...extraGlobals(this)])
|
||||
for (const [name, value] of this.builtins) globalScope.set(name, { mutable: false, value })
|
||||
}
|
||||
|
||||
@@ -434,8 +435,19 @@ class Frame<R> {
|
||||
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())))
|
||||
}
|
||||
|
||||
private createFunction(node: FunctionDeclaration | FunctionExpression | ArrowFunctionExpression): CodeModeFunction {
|
||||
return new CodeModeFunction(node.params, node.body, this.scopes.capture(), node.async, node.generator)
|
||||
private createFunction(
|
||||
node: FunctionDeclaration | FunctionExpression | ArrowFunctionExpression,
|
||||
name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? ""),
|
||||
): ProgramFunction {
|
||||
return new ProgramFunction(name, node.params, node.body, this.scopes.capture(), node.async, node.generator)
|
||||
}
|
||||
|
||||
// NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
|
||||
private evaluateNamed(node: Expression, name: string): Effect.Effect<unknown, unknown, R> {
|
||||
if (node.type === "ArrowFunctionExpression" || (node.type === "FunctionExpression" && !node.id)) {
|
||||
return Effect.sync(() => this.createFunction(node, name))
|
||||
}
|
||||
return this.evaluateExpression(node)
|
||||
}
|
||||
|
||||
private hoistFunctions(statements: ReadonlyArray<Statement | ModuleDeclaration>): void {
|
||||
@@ -722,22 +734,26 @@ class Frame<R> {
|
||||
}
|
||||
|
||||
syncIterator(value: unknown, node: AstNode) {
|
||||
const iterator = Array.isArray(value)
|
||||
? value[Symbol.iterator]()
|
||||
: typeof value === "string"
|
||||
? value[Symbol.iterator]()
|
||||
: value instanceof Values.Map
|
||||
? value.map.entries()
|
||||
: value instanceof Values.Set
|
||||
? value.set.values()
|
||||
: value instanceof Values.URLSearchParams
|
||||
? value.params.entries()
|
||||
: undefined
|
||||
const iterator =
|
||||
value instanceof ProgramArray
|
||||
? value.items[Symbol.iterator]()
|
||||
: typeof value === "string"
|
||||
? value[Symbol.iterator]()
|
||||
: value instanceof Values.Map
|
||||
? value.map.entries()
|
||||
: value instanceof Values.Set
|
||||
? value.set.values()
|
||||
: value instanceof Values.URLSearchParams
|
||||
? value.params.entries()
|
||||
: undefined
|
||||
if (iterator !== undefined) {
|
||||
return Effect.succeed({
|
||||
next: Effect.sync(() => {
|
||||
const step = iterator.next()
|
||||
return { done: Boolean(step.done), value: step.value }
|
||||
return {
|
||||
done: Boolean(step.done),
|
||||
value: Array.isArray(step.value) ? new ProgramArray(step.value) : step.value,
|
||||
}
|
||||
}),
|
||||
close: Effect.void,
|
||||
})
|
||||
@@ -762,9 +778,9 @@ class Frame<R> {
|
||||
asynchronous: value.asynchronous,
|
||||
})
|
||||
}
|
||||
if (!isRecord(value) || isRuntimeReference(value)) return Effect.undefined
|
||||
const asyncMethod = allowAsync ? Reflect.get(value, AsyncIteratorSymbol) : undefined
|
||||
const method = asyncMethod ?? Reflect.get(value, IteratorSymbol)
|
||||
if (!(value instanceof ProgramObject)) return Effect.undefined
|
||||
const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined
|
||||
const method = asyncMethod ?? get(value, IteratorSymbol)
|
||||
if (method === undefined || method === null) return Effect.undefined
|
||||
const self = this
|
||||
return Effect.map(
|
||||
@@ -776,7 +792,7 @@ class Frame<R> {
|
||||
next:
|
||||
object instanceof CodeModeGenerator
|
||||
? new GeneratorMethodReference(object, "next")
|
||||
: self.requireIteratorMethod(object.next, "Iterator next", node),
|
||||
: self.requireIteratorMethod(get(object, "next"), "Iterator next", node),
|
||||
asynchronous: asyncMethod !== undefined && asyncMethod !== null,
|
||||
}
|
||||
},
|
||||
@@ -792,7 +808,7 @@ class Frame<R> {
|
||||
"Iterator next() result",
|
||||
node,
|
||||
)
|
||||
return { done: Boolean(object.done), value: object.value }
|
||||
return { done: Boolean(get(object, "done")), value: get(object, "value") }
|
||||
}
|
||||
|
||||
const called = yield* Effect.exit(self.invokeCallable(iterator.next, [], node))
|
||||
@@ -803,7 +819,7 @@ class Frame<R> {
|
||||
const captured = yield* Effect.exit(
|
||||
Effect.sync(() => {
|
||||
const object = self.requireIteratorObject(called.value, "Iterator next() result", node)
|
||||
return { done: Boolean(object.done), value: object.value }
|
||||
return { done: Boolean(get(object, "done")), value: get(object, "value") }
|
||||
}),
|
||||
)
|
||||
if (!Exit.isSuccess(captured)) {
|
||||
@@ -823,7 +839,7 @@ class Frame<R> {
|
||||
const close =
|
||||
iterator.iterator instanceof CodeModeGenerator
|
||||
? new GeneratorMethodReference(iterator.iterator, "return")
|
||||
: iterator.iterator.return
|
||||
: get(iterator.iterator, "return")
|
||||
if (close === undefined || close === null) return iterator.asynchronous || !awaiting ? Effect.void : Effect.yieldNow
|
||||
const self = this
|
||||
return Effect.gen(function* () {
|
||||
@@ -843,7 +859,7 @@ class Frame<R> {
|
||||
return yield* Effect.failCause(called.cause)
|
||||
}
|
||||
const captured = yield* Effect.exit(
|
||||
Effect.sync(() => self.requireIteratorObject(called.value, "Iterator return() result", node).value),
|
||||
Effect.sync(() => get(self.requireIteratorObject(called.value, "Iterator return() result", node), "value")),
|
||||
)
|
||||
if (!Exit.isSuccess(captured)) {
|
||||
if (awaiting) yield* Effect.yieldNow
|
||||
@@ -853,12 +869,12 @@ class Frame<R> {
|
||||
})
|
||||
}
|
||||
|
||||
private requireIteratorObject(value: unknown, context: string, node: AstNode): SafeObject {
|
||||
if (isRecord(value) && !isRuntimeReference(value)) return value
|
||||
private requireIteratorObject(value: unknown, context: string, node: AstNode): ProgramObject {
|
||||
if (value instanceof ProgramObject) return value
|
||||
throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError")
|
||||
}
|
||||
|
||||
private requireIterator(value: unknown, node: AstNode): SafeObject | CodeModeGenerator {
|
||||
private requireIterator(value: unknown, node: AstNode): ProgramObject | CodeModeGenerator {
|
||||
return value instanceof CodeModeGenerator
|
||||
? value
|
||||
: this.requireIteratorObject(value, "Iterator method result", node)
|
||||
@@ -873,7 +889,7 @@ class Frame<R> {
|
||||
private enumerableKeys(value: unknown, node: AstNode): Array<string> {
|
||||
if (value instanceof ToolReference) return [...this.runtime.toolKeys(value.path)]
|
||||
if (value === null || value === undefined) return []
|
||||
return Object.keys(enumerableSource("for...in", value, node))
|
||||
return ownKeys(enumerableSource("for...in", value, node)).filter((key): key is string => typeof key === "string")
|
||||
}
|
||||
|
||||
private evaluateForInStatement(
|
||||
@@ -1019,11 +1035,14 @@ class Frame<R> {
|
||||
|
||||
const init = declaration.init
|
||||
// `var x` alone is a no-op: the binding was hoisted on function entry.
|
||||
const id = declaration.id
|
||||
const evaluate = (init: Expression) =>
|
||||
id.type === "Identifier" ? self.evaluateNamed(init, id.name) : self.evaluateExpression(init)
|
||||
if (kind === "var") {
|
||||
if (init) yield* self.assignPattern(declaration.id, yield* self.evaluateExpression(init), declaration)
|
||||
if (init) yield* self.assignPattern(id, yield* evaluate(init), declaration)
|
||||
continue
|
||||
}
|
||||
const value = init ? yield* self.evaluateExpression(init) : undefined
|
||||
const value = init ? yield* evaluate(init) : undefined
|
||||
yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true)
|
||||
}
|
||||
})
|
||||
@@ -1046,13 +1065,13 @@ class Frame<R> {
|
||||
}
|
||||
|
||||
if (pattern.type === "AssignmentPattern") {
|
||||
const resolved = value === undefined ? yield* self.evaluateExpression(pattern.right) : value
|
||||
const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value
|
||||
yield* self.declarePattern(pattern.left, resolved, mutable, node, initialize)
|
||||
return
|
||||
}
|
||||
|
||||
if (pattern.type === "ObjectPattern") {
|
||||
if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
|
||||
if (!(value instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`Object destructuring requires a data object or array value, received ${describeValue(value)}.`,
|
||||
pattern,
|
||||
@@ -1063,11 +1082,8 @@ class Frame<R> {
|
||||
const consumed = new Set<PropertyKey>()
|
||||
for (const property of pattern.properties) {
|
||||
if (property.type === "RestElement") {
|
||||
const rest: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, item] of Object.entries(value as SafeObject)) {
|
||||
if (!consumed.has(key)) rest[key] = item
|
||||
}
|
||||
copyIteratorSymbols(value, rest, consumed)
|
||||
const rest = new ProgramObject()
|
||||
assign(rest, value, consumed)
|
||||
yield* self.declarePattern(property.argument, rest, mutable, property, initialize)
|
||||
continue
|
||||
}
|
||||
@@ -1076,7 +1092,7 @@ class Frame<R> {
|
||||
consumed.add(typeof key === "symbol" ? key : String(key))
|
||||
yield* self.declarePattern(
|
||||
property.value,
|
||||
self.destructuringPropertyValue(value as SafeObject | Array<unknown>, key),
|
||||
self.destructuringPropertyValue(value, key),
|
||||
mutable,
|
||||
property,
|
||||
initialize,
|
||||
@@ -1109,13 +1125,13 @@ class Frame<R> {
|
||||
}
|
||||
|
||||
if (pattern.type === "AssignmentPattern") {
|
||||
const resolved = value === undefined ? yield* self.evaluateExpression(pattern.right) : value
|
||||
const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value
|
||||
yield* self.assignPattern(pattern.left, resolved, node)
|
||||
return
|
||||
}
|
||||
|
||||
if (pattern.type === "ObjectPattern") {
|
||||
if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
|
||||
if (!(value instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`Object destructuring requires a data object or array value, received ${describeValue(value)}.`,
|
||||
pattern,
|
||||
@@ -1123,21 +1139,17 @@ class Frame<R> {
|
||||
)
|
||||
}
|
||||
|
||||
const source = value as SafeObject | Array<unknown>
|
||||
const consumed = new Set<PropertyKey>()
|
||||
for (const property of pattern.properties) {
|
||||
if (property.type === "RestElement") {
|
||||
const rest: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, item] of Object.entries(source)) {
|
||||
if (!consumed.has(key)) rest[key] = item
|
||||
}
|
||||
copyIteratorSymbols(source, rest, consumed)
|
||||
const rest = new ProgramObject()
|
||||
assign(rest, value, consumed)
|
||||
yield* self.assignPattern(property.argument, rest, property)
|
||||
continue
|
||||
}
|
||||
const key = yield* self.destructuringPropertyKey(property)
|
||||
consumed.add(typeof key === "symbol" ? key : String(key))
|
||||
yield* self.assignPattern(property.value, self.destructuringPropertyValue(source, key), property)
|
||||
yield* self.assignPattern(property.value, self.destructuringPropertyValue(value, key), property)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1152,6 +1164,12 @@ class Frame<R> {
|
||||
})
|
||||
}
|
||||
|
||||
private evaluateDefault(pattern: AssignmentPattern): Effect.Effect<unknown, unknown, R> {
|
||||
return pattern.left.type === "Identifier"
|
||||
? this.evaluateNamed(pattern.right, pattern.left.name)
|
||||
: this.evaluateExpression(pattern.right)
|
||||
}
|
||||
|
||||
private destructureArrayPattern(
|
||||
pattern: ArrayPattern,
|
||||
value: unknown,
|
||||
@@ -1171,7 +1189,7 @@ class Frame<R> {
|
||||
if (element === null) continue
|
||||
yield* consume(
|
||||
element.type === "RestElement" ? element.argument : element,
|
||||
element.type === "RestElement" ? [] : undefined,
|
||||
element.type === "RestElement" ? new ProgramArray() : undefined,
|
||||
element,
|
||||
)
|
||||
if (element.type === "RestElement") return
|
||||
@@ -1188,7 +1206,7 @@ class Frame<R> {
|
||||
done = next.done
|
||||
if (!done) rest.push(next.value)
|
||||
}
|
||||
yield* consume(element.argument, rest, element)
|
||||
yield* consume(element.argument, new ProgramArray(rest), element)
|
||||
return
|
||||
}
|
||||
const consumed = consume(element, step.done ? undefined : step.value, pattern)
|
||||
@@ -1211,11 +1229,9 @@ class Frame<R> {
|
||||
throw unsupportedSyntax(keyNode.type, keyNode)
|
||||
}
|
||||
|
||||
private destructuringPropertyValue(source: SafeObject | Array<unknown>, key: PropertyKey): unknown {
|
||||
if (!Array.isArray(source)) return Reflect.get(source, key)
|
||||
if (key === "length") return source.length
|
||||
if (typeof key === "number") return source[key]
|
||||
if (Object.hasOwn(source, key)) return Reflect.get(source, key)
|
||||
private destructuringPropertyValue(source: ProgramObject, key: PropertyKey): unknown {
|
||||
if (!(source instanceof ProgramArray)) return get(source, key)
|
||||
if (has(source, key)) return get(source, key)
|
||||
if (typeof key === "string" && arrayMethods.has(key)) return new IntrinsicReference(source, key)
|
||||
return undefined
|
||||
}
|
||||
@@ -1293,7 +1309,7 @@ class Frame<R> {
|
||||
// otherwise; say `new` is unsupported for them and point at the plain call.
|
||||
const name = calleeDescription(node.callee)
|
||||
const message =
|
||||
callee instanceof CodeModeFunction
|
||||
callee instanceof ProgramFunction
|
||||
? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
|
||||
: callee instanceof HostFunction
|
||||
? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
|
||||
@@ -1321,6 +1337,9 @@ class Frame<R> {
|
||||
private applyBinaryOperator(operator: string, lhs: unknown, rhs: unknown, node: AstNode): unknown {
|
||||
if (operator === "===") return lhs === rhs
|
||||
if (operator === "!==") return lhs !== rhs
|
||||
if (operator === "in" && rhs instanceof ProgramObject && !containsOpaqueReference(lhs)) {
|
||||
return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : (lhs as PropertyKey))
|
||||
}
|
||||
if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
|
||||
throw new InterpreterRuntimeError("Binary operators require data values.", node, "InvalidDataValue")
|
||||
}
|
||||
@@ -1373,11 +1392,10 @@ class Frame<R> {
|
||||
case ">>>":
|
||||
return (l as number) >>> (r as number)
|
||||
case "in":
|
||||
if (rhs === null || typeof rhs !== "object") {
|
||||
if (!(rhs instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError("The 'in' operator requires a data object on the right-hand side.", node)
|
||||
}
|
||||
// Never expose properties inherited from host prototypes.
|
||||
return Object.hasOwn(rhs as object, coerceOperand(lhs) as PropertyKey)
|
||||
return has(rhs, coerceOperand(lhs) as PropertyKey)
|
||||
default:
|
||||
throw new InterpreterRuntimeError(`Unsupported binary operator '${operator}'.`, node)
|
||||
}
|
||||
@@ -1454,7 +1472,7 @@ class Frame<R> {
|
||||
const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result")
|
||||
return self.scopes.set(name, next, left)
|
||||
}
|
||||
const rightValue = yield* self.evaluateExpression(node.right)
|
||||
const rightValue = yield* self.evaluateNamed(node.right, name)
|
||||
return self.scopes.set(name, rightValue, left)
|
||||
}
|
||||
if (left.type === "MemberExpression") {
|
||||
@@ -1486,7 +1504,7 @@ class Frame<R> {
|
||||
return Effect.gen(function* () {
|
||||
const current = self.scopes.get(name, left)
|
||||
if (!shouldAssign(current)) return current
|
||||
const rightValue = yield* self.evaluateExpression(node.right)
|
||||
const rightValue = yield* self.evaluateNamed(node.right, name)
|
||||
return self.scopes.set(name, rightValue, left)
|
||||
})
|
||||
}
|
||||
@@ -1575,7 +1593,7 @@ class Frame<R> {
|
||||
}
|
||||
return yield* self.createToolCallPromise(callable.path, args)
|
||||
}
|
||||
if (callable instanceof CodeModeFunction) {
|
||||
if (callable instanceof ProgramFunction) {
|
||||
return yield* self.invokeFunction(callable, args)
|
||||
}
|
||||
if (callable instanceof GeneratorMethodReference) {
|
||||
@@ -1626,7 +1644,7 @@ class Frame<R> {
|
||||
})
|
||||
}
|
||||
|
||||
invokeFunction(fn: CodeModeFunction, args: Array<unknown>): Effect.Effect<unknown, unknown, R> {
|
||||
invokeFunction(fn: ProgramFunction, args: Array<unknown>): Effect.Effect<unknown, unknown, R> {
|
||||
const invocation = new Frame(this.runtime, new ScopeStack([...fn.capturedScopes, new Map()]))
|
||||
const run = Effect.gen(function* () {
|
||||
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
|
||||
@@ -1638,7 +1656,13 @@ class Frame<R> {
|
||||
}
|
||||
for (const [index, parameter] of fn.parameters.entries()) {
|
||||
if (parameter.type === "RestElement") {
|
||||
yield* invocation.declarePattern(parameter.argument, args.slice(index), true, parameter, true)
|
||||
yield* invocation.declarePattern(
|
||||
parameter.argument,
|
||||
new ProgramArray(args.slice(index)),
|
||||
true,
|
||||
parameter,
|
||||
true,
|
||||
)
|
||||
break
|
||||
}
|
||||
yield* invocation.declarePattern(parameter, args[index], true, parameter, true)
|
||||
@@ -1690,12 +1714,12 @@ class Frame<R> {
|
||||
}
|
||||
if (state.completed) {
|
||||
if (kind === "throw") return Effect.fail(new ProgramThrow(value))
|
||||
return Effect.succeed({ value: kind === "return" ? value : undefined, done: true })
|
||||
return Effect.succeed(record({ value: kind === "return" ? value : undefined, done: true }))
|
||||
}
|
||||
if (!state.started && kind !== "next") {
|
||||
state.completed = true
|
||||
if (kind === "throw") return Effect.fail(new ProgramThrow(value))
|
||||
return Effect.succeed({ value, done: true })
|
||||
return Effect.succeed(record({ value, done: true }))
|
||||
}
|
||||
|
||||
state.pending.push(request)
|
||||
@@ -1725,7 +1749,7 @@ class Frame<R> {
|
||||
if (active) {
|
||||
Deferred.doneUnsafe(
|
||||
active.response,
|
||||
Exit.isSuccess(exit) ? Exit.succeed({ value: exit.value, done: true }) : exit,
|
||||
Exit.isSuccess(exit) ? Exit.succeed(record({ value: exit.value, done: true })) : exit,
|
||||
)
|
||||
}
|
||||
yield* invocation.completeGeneratorRequests(state, asynchronous)
|
||||
@@ -1752,13 +1776,13 @@ class Frame<R> {
|
||||
const resolved = yield* Effect.exit(self.awaitValue(pending.value))
|
||||
Deferred.doneUnsafe(
|
||||
pending.response,
|
||||
Exit.isSuccess(resolved) ? Exit.succeed({ value: resolved.value, done: true }) : resolved,
|
||||
Exit.isSuccess(resolved) ? Exit.succeed(record({ value: resolved.value, done: true })) : resolved,
|
||||
)
|
||||
continue
|
||||
}
|
||||
Deferred.doneUnsafe(
|
||||
pending.response,
|
||||
Exit.succeed({ value: pending.kind === "return" ? pending.value : undefined, done: true }),
|
||||
Exit.succeed(record({ value: pending.kind === "return" ? pending.value : undefined, done: true })),
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -1803,7 +1827,7 @@ class Frame<R> {
|
||||
private suspendGenerator(value: unknown, node: AstNode): Effect.Effect<unknown, unknown, R> {
|
||||
const state = this.generatorState
|
||||
if (!state?.active) throw new InterpreterRuntimeError("Generator has no active request.", node)
|
||||
Deferred.doneUnsafe(state.active.response, Exit.succeed({ value, done: false }))
|
||||
Deferred.doneUnsafe(state.active.response, Exit.succeed(record({ value, done: false })))
|
||||
state.active = undefined
|
||||
return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
|
||||
state.active = request
|
||||
@@ -1819,7 +1843,7 @@ class Frame<R> {
|
||||
const self = this
|
||||
return Effect.gen(function* () {
|
||||
if (
|
||||
Array.isArray(value) ||
|
||||
value instanceof ProgramArray ||
|
||||
typeof value === "string" ||
|
||||
value instanceof Values.Map ||
|
||||
value instanceof Values.Set ||
|
||||
@@ -1860,7 +1884,7 @@ class Frame<R> {
|
||||
? iterator.next
|
||||
: iterator.iterator instanceof CodeModeGenerator
|
||||
? new GeneratorMethodReference(iterator.iterator, kind)
|
||||
: iterator.iterator[kind]
|
||||
: get(iterator.iterator, kind)
|
||||
if (method === undefined || method === null) {
|
||||
if (kind === "return") return yield* Effect.fail(new GeneratorReturn(input))
|
||||
yield* self.closeIterator(iterator, node, self.generatorAsync)
|
||||
@@ -1878,11 +1902,11 @@ class Frame<R> {
|
||||
`Iterator ${kind}() result`,
|
||||
node,
|
||||
)
|
||||
const done = Boolean(result.done)
|
||||
const done = Boolean(get(result, "done"))
|
||||
const resultValue: unknown =
|
||||
self.generatorAsync && !iterator.asynchronous
|
||||
? yield* self.awaitAsyncFromSyncValue(iterator, result.value, node, kind !== "return" && !done)
|
||||
: result.value
|
||||
? yield* self.awaitAsyncFromSyncValue(iterator, get(result, "value"), node, kind !== "return" && !done)
|
||||
: get(result, "value")
|
||||
if (done) {
|
||||
if (kind === "return") return yield* Effect.fail(new GeneratorReturn(resultValue))
|
||||
return resultValue
|
||||
@@ -1904,17 +1928,15 @@ class Frame<R> {
|
||||
})
|
||||
}
|
||||
|
||||
private evaluateObjectExpression(node: ObjectExpression): Effect.Effect<Record<string, unknown>, unknown, R> {
|
||||
const objectValue: Record<string, unknown> = Object.create(null) as Record<string, unknown>
|
||||
private evaluateObjectExpression(node: ObjectExpression): Effect.Effect<ProgramObject, unknown, R> {
|
||||
const objectValue = new ProgramObject()
|
||||
const self = this
|
||||
return Effect.gen(function* () {
|
||||
for (const property of node.properties) {
|
||||
if (property.type === "SpreadElement") {
|
||||
const spread = yield* self.evaluateExpression(property.argument)
|
||||
if (spread === null || spread === undefined) continue
|
||||
const from = enumerableSource("Object spread", spread, property)
|
||||
for (const [key, value] of Object.entries(from)) objectValue[key] = value
|
||||
if (typeof from === "object") copyIteratorSymbols(from, objectValue)
|
||||
assign(objectValue, enumerableSource("Object spread", spread, property))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1936,14 +1958,20 @@ class Frame<R> {
|
||||
throw new InterpreterRuntimeError("Unsupported object property key shape.", keyNode)
|
||||
}
|
||||
|
||||
Reflect.set(objectValue, key, yield* self.evaluateExpression(property.value))
|
||||
const name =
|
||||
key === IteratorSymbol
|
||||
? "[Symbol.iterator]"
|
||||
: key === AsyncIteratorSymbol
|
||||
? "[Symbol.asyncIterator]"
|
||||
: String(key)
|
||||
set(objectValue, key, yield* self.evaluateNamed(property.value, name))
|
||||
}
|
||||
|
||||
return objectValue
|
||||
})
|
||||
}
|
||||
|
||||
private evaluateArrayExpression(node: ArrayExpression): Effect.Effect<Array<unknown>, unknown, R> {
|
||||
private evaluateArrayExpression(node: ArrayExpression): Effect.Effect<ProgramArray, unknown, R> {
|
||||
const values: Array<unknown> = []
|
||||
|
||||
const self = this
|
||||
@@ -1968,7 +1996,7 @@ class Frame<R> {
|
||||
values.push(yield* self.evaluateExpression(element))
|
||||
}
|
||||
}
|
||||
return values
|
||||
return new ProgramArray(values)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2055,7 +2083,11 @@ class Frame<R> {
|
||||
}
|
||||
|
||||
// Values have no prototype chain, so `.constructor` resolves to the owning built-in directly.
|
||||
if (operation === "read" && key === "constructor" && !hasOwn(objectValue, key)) {
|
||||
if (
|
||||
operation === "read" &&
|
||||
key === "constructor" &&
|
||||
!(objectValue instanceof ProgramObject && has(objectValue, key))
|
||||
) {
|
||||
const name = constructorName(objectValue)
|
||||
if (name !== undefined) return new ComputedValue(self.runtime.builtins.get(name))
|
||||
}
|
||||
@@ -2136,6 +2168,8 @@ class Frame<R> {
|
||||
return new ComputedValue(undefined)
|
||||
}
|
||||
|
||||
if (objectValue instanceof ProgramObject) return { target: objectValue, key }
|
||||
|
||||
if (isRuntimeReference(objectValue)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`Cannot read properties of ${describeValue(objectValue)}; only data values expose properties.`,
|
||||
@@ -2143,42 +2177,27 @@ class Frame<R> {
|
||||
"InvalidDataValue",
|
||||
)
|
||||
}
|
||||
|
||||
if (typeof objectValue !== "object" || objectValue === null) {
|
||||
throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode)
|
||||
}
|
||||
|
||||
if (Array.isArray(objectValue)) {
|
||||
if (operation === "delete") return { target: objectValue, key }
|
||||
const index = typeof key === "symbol" ? undefined : parseArrayIndex(key)
|
||||
if (key !== "length" && !(typeof key === "string" && arrayMethods.has(key)) && index === undefined) {
|
||||
if (typeof key === "string" && Object.hasOwn(objectValue, key)) {
|
||||
return new ComputedValue((objectValue as Record<string, unknown> & Array<unknown>)[key])
|
||||
}
|
||||
return new ComputedValue(undefined)
|
||||
}
|
||||
return { target: objectValue, key: index ?? key }
|
||||
}
|
||||
|
||||
return { target: objectValue as SafeObject, key }
|
||||
throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode)
|
||||
})
|
||||
}
|
||||
|
||||
private readMember(node: MemberExpression): Effect.Effect<unknown, unknown, R> {
|
||||
const self = this
|
||||
return Effect.map(this.getMemberReference(node), (reference) => {
|
||||
if (reference === OptionalShortCircuit) return OptionalShortCircuit
|
||||
if (reference instanceof ComputedValue) return reference.value
|
||||
if (reference === undefined || isOpaqueMemberReference(reference)) return reference
|
||||
if (Array.isArray(reference.target)) {
|
||||
if (reference.key === "length") return reference.target.length
|
||||
if (typeof reference.key === "string") return new IntrinsicReference(reference.target, reference.key)
|
||||
return Reflect.get(reference.target, reference.key)
|
||||
const value = self.readReferenceValue(reference, reference.key)
|
||||
if (
|
||||
value === undefined &&
|
||||
reference.target instanceof ProgramArray &&
|
||||
typeof reference.key === "string" &&
|
||||
arrayMethods.has(reference.key) &&
|
||||
!has(reference.target, reference.key)
|
||||
) {
|
||||
return new IntrinsicReference(reference.target, reference.key)
|
||||
}
|
||||
if (reference.target instanceof Values.RegExp) return reference.target.lastIndex
|
||||
if (reference.target instanceof Values.URL) {
|
||||
return Reflect.get(reference.target.url, reference.key)
|
||||
}
|
||||
return Reflect.get(reference.target, reference.key)
|
||||
return value
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2204,7 +2223,7 @@ class Frame<R> {
|
||||
if (reference.target instanceof Values.RegExp) {
|
||||
return Reflect.deleteProperty(reference.target.regex, reference.key)
|
||||
}
|
||||
return Reflect.deleteProperty(reference.target, reference.key)
|
||||
return remove(reference.target, reference.key)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2224,12 +2243,6 @@ class Frame<R> {
|
||||
) {
|
||||
throw new InterpreterRuntimeError("Only data fields may be assigned.", node)
|
||||
}
|
||||
if (Array.isArray(reference.target)) {
|
||||
if (reference.key === "length") throw new InterpreterRuntimeError("Array length cannot be assigned.", node)
|
||||
if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
|
||||
throw new InterpreterRuntimeError("Array methods cannot be assigned.", node)
|
||||
}
|
||||
}
|
||||
const key = reference.key
|
||||
const { write, next, result } = yield* compute(self.readReferenceValue(reference, key))
|
||||
if (write) self.assignToReference(reference, key, next, node)
|
||||
@@ -2242,23 +2255,10 @@ class Frame<R> {
|
||||
return Reflect.get(reference.target.url, key)
|
||||
}
|
||||
if (reference.target instanceof Values.RegExp) return reference.target.lastIndex
|
||||
return Reflect.get(reference.target, key)
|
||||
return get(reference.target, key)
|
||||
}
|
||||
|
||||
private assignToReference(reference: MemberReference, key: PropertyKey, next: unknown, node: AstNode): void {
|
||||
if (Array.isArray(reference.target)) {
|
||||
const target = reference.target
|
||||
if (typeof key !== "number" || parseArrayIndex(key) === undefined) {
|
||||
throw new InterpreterRuntimeError(
|
||||
"Array assignment index must be a valid array index.",
|
||||
node,
|
||||
"InvalidDataValue",
|
||||
)
|
||||
}
|
||||
rejectCircularInsertion(target, next, "Array assignment result", node)
|
||||
target[key] = next
|
||||
return
|
||||
}
|
||||
if (reference.target instanceof Values.URL) {
|
||||
const property = key as string
|
||||
if (!urlWritableProperties.has(property)) {
|
||||
@@ -2277,9 +2277,16 @@ class Frame<R> {
|
||||
reference.target.lastIndex = next
|
||||
return
|
||||
}
|
||||
const target = reference.target as SafeObject
|
||||
rejectCircularInsertion(target, next, "Object assignment result", node)
|
||||
Reflect.set(target, key, next)
|
||||
const target = reference.target
|
||||
rejectCircularInsertion(
|
||||
target,
|
||||
next,
|
||||
target instanceof ProgramArray ? "Array assignment result" : "Object assignment result",
|
||||
node,
|
||||
)
|
||||
if (set(target, key, next)) return
|
||||
if (target instanceof ProgramArray) throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError")
|
||||
throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError")
|
||||
}
|
||||
|
||||
private toPropertyKey(value: unknown, node: AstNode): PropertyKey {
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import { Effect } from "effect"
|
||||
import { HostFunction, sync, syncCall } from "../interpreter/host.js"
|
||||
import { type AstNode, CodeModeGenerator, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { get, ProgramArray, ProgramObject } from "../interpreter/objects.js"
|
||||
import { describeValue } from "../interpreter/references.js"
|
||||
import { applyCollectionCallback, preserveConsumerError, type Runner } from "../interpreter/runner.js"
|
||||
|
||||
const constructArray = (args: Array<unknown>, node: AstNode): Array<unknown> => {
|
||||
if (args.length !== 1) return [...args]
|
||||
const constructArray = (args: Array<unknown>, node: AstNode): ProgramArray => {
|
||||
if (args.length !== 1) return new ProgramArray([...args])
|
||||
const first = args[0]
|
||||
if (typeof first !== "number") return [first]
|
||||
if (typeof first !== "number") return new ProgramArray([first])
|
||||
if (!Number.isInteger(first) || first < 0 || first > 4294967295) {
|
||||
throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError")
|
||||
}
|
||||
// Sparse like JS: Array(3) has holes, and combinator loops already skip them.
|
||||
return new Array(first)
|
||||
return new ProgramArray(new Array(first))
|
||||
}
|
||||
|
||||
const arrayLikeSource = (source: unknown, node: AstNode): { readonly length: number; readonly source: object } => {
|
||||
if (
|
||||
source !== null &&
|
||||
typeof source === "object" &&
|
||||
(Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
|
||||
typeof (source as { length?: unknown }).length === "number"
|
||||
) {
|
||||
const length = (source as { length: number }).length
|
||||
const arrayLikeSource = (
|
||||
source: unknown,
|
||||
node: AstNode,
|
||||
): { readonly length: number; readonly source: ProgramObject } => {
|
||||
if (source instanceof ProgramObject && typeof get(source, "length") === "number") {
|
||||
const length = get(source, "length") as number
|
||||
const normalized = Number.isNaN(length) || length <= 0 ? 0 : Math.trunc(length)
|
||||
if (normalized > 4_294_967_295) throw new RangeError("Invalid array length")
|
||||
return { length: normalized, source }
|
||||
@@ -49,16 +48,16 @@ const arrayFrom = <R>(runner: Runner<R>, args: Array<unknown>, node: AstNode): E
|
||||
const arrayLike = arrayLikeSource(source, node)
|
||||
const values: Array<unknown> = []
|
||||
for (let index = 0; index < arrayLike.length; index += 1) {
|
||||
const item = Reflect.get(arrayLike.source, index)
|
||||
const item = get(arrayLike.source, index)
|
||||
values.push(apply === undefined ? item : yield* apply([item, index]))
|
||||
}
|
||||
return values
|
||||
return new ProgramArray(values)
|
||||
}
|
||||
const values: Array<unknown> = []
|
||||
let index = 0
|
||||
while (true) {
|
||||
const step = yield* cursor.next
|
||||
if (step.done) return values
|
||||
if (step.done) return new ProgramArray(values)
|
||||
values.push(apply === undefined ? step.value : yield* preserveConsumerError(cursor, apply([step.value, index])))
|
||||
index += 1
|
||||
}
|
||||
@@ -71,10 +70,10 @@ export const arrayGlobal = <R>(runner: Runner<R>) =>
|
||||
name: "Array",
|
||||
call: syncCall(constructArray),
|
||||
construct: syncCall(constructArray),
|
||||
instanceOf: (value) => Array.isArray(value),
|
||||
instanceOf: (value) => value instanceof ProgramArray,
|
||||
members: {
|
||||
isArray: sync("Array.isArray", (args) => Array.isArray(args[0])),
|
||||
of: sync("Array.of", (args) => [...args]),
|
||||
isArray: sync("Array.isArray", (args) => args[0] instanceof ProgramArray),
|
||||
of: sync("Array.of", (args) => new ProgramArray([...args])),
|
||||
from: new HostFunction<R>({ name: "Array.from", call: (args, node) => arrayFrom(runner, args, node) }),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Effect } from "effect"
|
||||
import type { SafeObject } from "../data.js"
|
||||
import { HostFunction, requiresNew } from "../interpreter/host.js"
|
||||
import { type AstNode, InterpreterRuntimeError, isRecord } from "../interpreter/model.js"
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { getOwn, ProgramArray, ProgramObject, set } from "../interpreter/objects.js"
|
||||
import { describeValue, isRuntimeReference } from "../interpreter/references.js"
|
||||
import { applyCollectionCallback, preserveConsumerError, type Runner, toPrimitive } from "../interpreter/runner.js"
|
||||
import { Values } from "../values.js"
|
||||
@@ -108,13 +108,13 @@ export const groupBy = <R>(runner: Runner<R>, namespace: "Map" | "Object") =>
|
||||
const item = step.value
|
||||
const key = yield* preserveConsumerError(cursor, apply([item, index]))
|
||||
const group = result.map.get(key)
|
||||
if (group === undefined) result.map.set(key, [item])
|
||||
else (group as Array<unknown>).push(item)
|
||||
if (group === undefined) result.map.set(key, new ProgramArray([item]))
|
||||
else (group as ProgramArray).items.push(item)
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
const result: SafeObject = Object.create(null) as SafeObject
|
||||
const result = new ProgramObject()
|
||||
let index = 0
|
||||
while (true) {
|
||||
const step = yield* cursor.next
|
||||
@@ -124,9 +124,9 @@ export const groupBy = <R>(runner: Runner<R>, namespace: "Map" | "Object") =>
|
||||
cursor,
|
||||
Effect.flatMap(apply([item, index]), (value) => coerceGroupByPropertyKey(runner, value, node)),
|
||||
)
|
||||
const group = result[key]
|
||||
if (group === undefined) result[key] = [item]
|
||||
else (group as Array<unknown>).push(item)
|
||||
const group = getOwn(result, key)
|
||||
if (group === undefined) set(result, key, new ProgramArray([item]))
|
||||
else (group as ProgramArray).items.push(item)
|
||||
index += 1
|
||||
}
|
||||
})
|
||||
@@ -150,12 +150,12 @@ const constructMap = <R>(runner: Runner<R>, init: unknown, node: AstNode) => {
|
||||
yield* preserveConsumerError(
|
||||
cursor,
|
||||
Effect.sync(() => {
|
||||
if (!isRecord(step.value) || isRuntimeReference(step.value)) {
|
||||
if (!(step.value instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node).as(
|
||||
"TypeError",
|
||||
)
|
||||
}
|
||||
target.map.set(step.value[0], step.value[1])
|
||||
target.map.set(getOwn(step.value, 0), getOwn(step.value, 1))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { toData, toProgram } from "../data.js"
|
||||
import { HostNamespace, sync } from "../interpreter/host.js"
|
||||
import { get, ownEntries, ProgramArray, ProgramObject } from "../interpreter/objects.js"
|
||||
import { containsOpaqueReference, containsRuntimeReference, isRuntimeReference } from "../interpreter/references.js"
|
||||
import { Values } from "../values.js"
|
||||
import { coerceToString } from "./value.js"
|
||||
@@ -51,8 +52,8 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
|
||||
if (value instanceof Values.Map) {
|
||||
seen.add(value)
|
||||
try {
|
||||
const entries = Array.from(value.map.entries(), ([key, item]): Array<unknown> => [key, item])
|
||||
return `Map(${value.map.size}) ${formatConsoleValue(entries, seen, depth + 1)}`
|
||||
const entries = Array.from(value.map.entries(), ([key, item]) => new ProgramArray([key, item]))
|
||||
return `Map(${value.map.size}) ${formatConsoleValue(new ProgramArray(entries), seen, depth + 1)}`
|
||||
} finally {
|
||||
seen.delete(value)
|
||||
}
|
||||
@@ -60,7 +61,7 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
|
||||
if (value instanceof Values.Set) {
|
||||
seen.add(value)
|
||||
try {
|
||||
return `Set(${value.set.size}) ${formatConsoleValue(Array.from(value.set.values()), seen, depth + 1)}`
|
||||
return `Set(${value.set.size}) ${formatConsoleValue(new ProgramArray([...value.set.values()]), seen, depth + 1)}`
|
||||
} finally {
|
||||
seen.delete(value)
|
||||
}
|
||||
@@ -68,10 +69,11 @@ const formatConsoleValue = (value: unknown, seen: Set<object>, depth: number): s
|
||||
if (isRuntimeReference(value)) return "[opaque reference]"
|
||||
seen.add(value)
|
||||
try {
|
||||
if (Array.isArray(value)) {
|
||||
return `[${value.map((item) => formatConsoleValue(item, seen, depth + 1)).join(",")}]`
|
||||
if (value instanceof ProgramArray) {
|
||||
return `[${value.items.map((item) => formatConsoleValue(item, seen, depth + 1)).join(",")}]`
|
||||
}
|
||||
return `{${Object.entries(value)
|
||||
if (!(value instanceof ProgramObject)) return "[object Object]"
|
||||
return `{${ownEntries(value)
|
||||
.map(([key, item]) => `${JSON.stringify(key)}:${formatConsoleValue(item, seen, depth + 1)}`)
|
||||
.join(",")}}`
|
||||
} finally {
|
||||
@@ -104,20 +106,19 @@ const consoleTableRows = (
|
||||
data: unknown,
|
||||
columns: ReadonlyArray<string> | undefined,
|
||||
): Array<{ readonly index: string; readonly values: Record<string, unknown> }> => {
|
||||
if (Array.isArray(data)) {
|
||||
return data.map((item, index) => ({ index: String(index), values: consoleTableValues(item, columns) }))
|
||||
if (data instanceof ProgramArray) {
|
||||
return data.items.map((item, index) => ({ index: String(index), values: consoleTableValues(item, columns) }))
|
||||
}
|
||||
if (data !== null && typeof data === "object" && !Values.isValue(data)) {
|
||||
return Object.entries(data).map(([index, item]) => ({ index, values: consoleTableValues(item, columns) }))
|
||||
if (data instanceof ProgramObject) {
|
||||
return ownEntries(data).map(([index, item]) => ({ index, values: consoleTableValues(item, columns) }))
|
||||
}
|
||||
return [{ index: "0", values: { Value: data } }]
|
||||
}
|
||||
|
||||
const consoleTableValues = (value: unknown, columns: ReadonlyArray<string> | undefined): Record<string, unknown> => {
|
||||
if (value !== null && typeof value === "object" && !Array.isArray(value) && !Values.isValue(value)) {
|
||||
const source = value as Record<string, unknown>
|
||||
if (columns !== undefined) return Object.fromEntries(columns.map((column) => [column, source[column]]))
|
||||
return Object.fromEntries(Object.entries(source))
|
||||
if (value instanceof ProgramObject && !(value instanceof ProgramArray)) {
|
||||
if (columns !== undefined) return Object.fromEntries(columns.map((column) => [column, get(value, column)]))
|
||||
return Object.fromEntries(ownEntries(value))
|
||||
}
|
||||
return { Value: value }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { HostFunction, HostNamespace } from "../interpreter/host.js"
|
||||
import { applyCollectionCallback, type Runner } from "../interpreter/runner.js"
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { typeofValue } from "../interpreter/references.js"
|
||||
import { fromData, type SafeObject, toData, toProgram } from "../data.js"
|
||||
import { fromData, toData, toProgram } from "../data.js"
|
||||
import { get, ownKeys, ProgramArray, ProgramObject, record, remove, set } from "../interpreter/objects.js"
|
||||
import { Values } from "../values.js"
|
||||
|
||||
export const jsonGlobal = <R>(runner: Runner<R>) =>
|
||||
@@ -29,28 +30,19 @@ const parse = <R>(runner: Runner<R>, args: Array<unknown>, node: AstNode): Effec
|
||||
if (typeofValue(args[1]) !== "function") return Effect.succeed(parsed)
|
||||
|
||||
const apply = applyCollectionCallback(runner, args[1], "JSON.parse", node)
|
||||
const root: SafeObject = Object.create(null) as SafeObject
|
||||
root[""] = parsed
|
||||
const visit = (holder: SafeObject | Array<unknown>, key: string): Effect.Effect<unknown, unknown, R> =>
|
||||
const visit = (holder: ProgramObject, key: string): Effect.Effect<unknown, unknown, R> =>
|
||||
Effect.gen(function* () {
|
||||
const value = holder[key as keyof typeof holder]
|
||||
if (Array.isArray(value)) {
|
||||
const length = value.length
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
const revived = yield* visit(value, String(index))
|
||||
if (revived === undefined) Reflect.deleteProperty(value, index)
|
||||
else value[index] = revived
|
||||
}
|
||||
} else if (isPlainObject(value)) {
|
||||
for (const name of Object.keys(value)) {
|
||||
const revived = yield* visit(value, name)
|
||||
if (revived === undefined) Reflect.deleteProperty(value, name)
|
||||
else value[name] = revived
|
||||
const value = get(holder, key)
|
||||
if (value instanceof ProgramObject) {
|
||||
for (const name of ownKeys(value)) {
|
||||
const revived = yield* visit(value, name as string)
|
||||
if (revived === undefined) remove(value, name)
|
||||
else set(value, name, revived)
|
||||
}
|
||||
}
|
||||
return yield* apply([key, value])
|
||||
})
|
||||
return visit(root, "")
|
||||
return visit(record({ "": parsed }), "")
|
||||
}
|
||||
|
||||
const stringify = <R>(runner: Runner<R>, args: Array<unknown>, node: AstNode): Effect.Effect<unknown, unknown, R> => {
|
||||
@@ -59,44 +51,41 @@ const stringify = <R>(runner: Runner<R>, args: Array<unknown>, node: AstNode): E
|
||||
const replacer = args[1]
|
||||
|
||||
if (typeofValue(replacer) !== "function") {
|
||||
const properties = Array.isArray(replacer)
|
||||
? replacer
|
||||
.filter((item): item is string | number => typeof item === "string" || typeof item === "number")
|
||||
.map(String)
|
||||
: null
|
||||
const properties =
|
||||
replacer instanceof ProgramArray
|
||||
? replacer.items
|
||||
.filter((item): item is string | number => typeof item === "string" || typeof item === "number")
|
||||
.map(String)
|
||||
: null
|
||||
return Effect.succeed(JSON.stringify(toData(args[0], "JSON.stringify value"), properties, indent))
|
||||
}
|
||||
|
||||
// Validate up front; the replacer walk below reads the original value.
|
||||
toProgram(args[0], "JSON.stringify value")
|
||||
const apply = applyCollectionCallback(runner, replacer, "JSON.stringify", node)
|
||||
const root: SafeObject = Object.create(null) as SafeObject
|
||||
root[""] = args[0]
|
||||
const stack = new Set<object>()
|
||||
const visit = (holder: SafeObject | Array<unknown>, key: string): Effect.Effect<unknown, unknown, R> =>
|
||||
const visit = (holder: ProgramObject, key: string): Effect.Effect<unknown, unknown, R> =>
|
||||
Effect.gen(function* () {
|
||||
const value = yield* apply([key, toJSONValue(holder[key as keyof typeof holder])])
|
||||
const value = yield* apply([key, toJSONValue(get(holder, key))])
|
||||
if (value === undefined || typeofValue(value) === "function") return undefined
|
||||
toProgram(value, "JSON.stringify replacer result")
|
||||
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
||||
if (value === null || typeof value === "string" || typeof value === "boolean") return value
|
||||
if (Array.isArray(value)) {
|
||||
if (stack.has(value))
|
||||
throw new InterpreterRuntimeError("Converting circular structure to JSON.", node).as("TypeError")
|
||||
stack.add(value)
|
||||
if (!(value instanceof ProgramObject)) return {}
|
||||
if (stack.has(value))
|
||||
throw new InterpreterRuntimeError("Converting circular structure to JSON.", node).as("TypeError")
|
||||
stack.add(value)
|
||||
if (value instanceof ProgramArray) {
|
||||
const result: Array<unknown> = []
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
for (let index = 0; index < value.items.length; index += 1) {
|
||||
result.push((yield* visit(value, String(index))) ?? null)
|
||||
}
|
||||
stack.delete(value)
|
||||
return result
|
||||
}
|
||||
if (!isPlainObject(value)) return {}
|
||||
if (stack.has(value))
|
||||
throw new InterpreterRuntimeError("Converting circular structure to JSON.", node).as("TypeError")
|
||||
stack.add(value)
|
||||
const result: SafeObject = Object.create(null) as SafeObject
|
||||
for (const name of Object.keys(value)) {
|
||||
const result: Record<string, unknown> = Object.create(null)
|
||||
for (const name of ownKeys(value)) {
|
||||
if (typeof name !== "string") continue
|
||||
const item = yield* visit(value, name)
|
||||
if (item !== undefined) result[name] = item
|
||||
}
|
||||
@@ -104,7 +93,7 @@ const stringify = <R>(runner: Runner<R>, args: Array<unknown>, node: AstNode): E
|
||||
return result
|
||||
})
|
||||
|
||||
return Effect.map(visit(root, ""), (value) => JSON.stringify(value, null, indent))
|
||||
return Effect.map(visit(record({ "": args[0] }), ""), (value) => JSON.stringify(value, null, indent))
|
||||
}
|
||||
|
||||
const toJSONValue = (value: unknown): unknown => {
|
||||
@@ -114,6 +103,3 @@ const toJSONValue = (value: unknown): unknown => {
|
||||
if (value instanceof Values.URL) return value.url.href
|
||||
return value
|
||||
}
|
||||
|
||||
const isPlainObject = (value: unknown): value is SafeObject =>
|
||||
value !== null && typeof value === "object" && !Values.isValue(value)
|
||||
|
||||
@@ -2,11 +2,10 @@ import { Effect } from "effect"
|
||||
import { toProgram } from "../data.js"
|
||||
import { HostFunction, sync, syncCall } from "../interpreter/host.js"
|
||||
import { type AstNode, AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "../interpreter/model.js"
|
||||
import { getOwn, hasOwn, ownEntries, ownKeys, ProgramArray, ProgramObject, set } from "../interpreter/objects.js"
|
||||
import {
|
||||
containsOpaqueReference,
|
||||
describeValue,
|
||||
isRuntimeReference,
|
||||
parseArrayIndex,
|
||||
rejectCircularInsertion,
|
||||
typeofValue,
|
||||
} from "../interpreter/references.js"
|
||||
@@ -16,9 +15,8 @@ import { Values } from "../values.js"
|
||||
import { groupBy } from "./collections.js"
|
||||
import { coerceToString } from "./value.js"
|
||||
|
||||
// ToObject for enumeration. Strings return themselves: the host's Object.keys/entries/hasOwn index a
|
||||
// primitive string directly. Numbers, booleans, wrappers, and functions have no own enumerable keys.
|
||||
export const enumerableSource = (label: string, value: unknown, node: AstNode): Record<string, unknown> => {
|
||||
// ToObject for enumeration.
|
||||
export const enumerableSource = (label: string, value: unknown, node: AstNode): ProgramObject => {
|
||||
if (value === null || value === undefined) {
|
||||
throw new InterpreterRuntimeError(`${label} cannot convert ${describeValue(value)} to an object.`, node).as(
|
||||
"TypeError",
|
||||
@@ -38,58 +36,41 @@ export const enumerableSource = (label: string, value: unknown, node: AstNode):
|
||||
"InvalidDataValue",
|
||||
)
|
||||
}
|
||||
if (typeof value === "string") return value as unknown as Record<string, unknown>
|
||||
if (typeof value !== "object" || Values.isValue(value) || isRuntimeReference(value)) return {}
|
||||
return value as Record<string, unknown>
|
||||
if (typeof value === "string") return new ProgramArray([...value])
|
||||
if (value instanceof ProgramObject) return value
|
||||
return new ProgramObject()
|
||||
}
|
||||
|
||||
export const objectAssign = (args: Array<unknown>, node: AstNode): unknown => {
|
||||
const target = args[0]
|
||||
// JS would box a primitive target; wrappers and primitives cannot hold fields here.
|
||||
if (target === null || typeof target !== "object" || Values.isValue(target) || isRuntimeReference(target)) {
|
||||
if (!(target instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`Object.assign expects a data object or array target, received ${describeValue(target)}.`,
|
||||
node,
|
||||
).as("TypeError")
|
||||
}
|
||||
const out = target as Record<string, unknown>
|
||||
const seen = new Set<object>()
|
||||
const guardedSet = (key: PropertyKey, item: unknown): void => {
|
||||
// Arrays hold only indexed elements, as with direct assignment; Reflect.set would otherwise
|
||||
// reach Array's length and Object.prototype's __proto__ setter.
|
||||
if (Array.isArray(out) && (typeof key === "symbol" || parseArrayIndex(key) === undefined)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
`Object.assign cannot assign '${String(key)}' to an array: only array indexes may be assigned.`,
|
||||
node,
|
||||
).as("TypeError")
|
||||
}
|
||||
rejectCircularInsertion(out, item, "Object.assign result", node, seen)
|
||||
if (!Reflect.set(out, key, item))
|
||||
throw new InterpreterRuntimeError(`Object.assign could not assign property '${String(key)}'.`, node).as(
|
||||
"TypeError",
|
||||
)
|
||||
}
|
||||
for (const source of args.slice(1)) {
|
||||
if (source === null || source === undefined) continue
|
||||
const from = enumerableSource("Object.assign(...)", source, node)
|
||||
if (typeof from !== "object") {
|
||||
for (const [key, item] of Object.entries(from)) guardedSet(key, item)
|
||||
continue
|
||||
}
|
||||
for (const key of Reflect.ownKeys(from)) {
|
||||
for (const key of ownKeys(from)) {
|
||||
if (typeof key === "symbol" && key !== AsyncIteratorSymbol && key !== IteratorSymbol) continue
|
||||
if (Object.prototype.propertyIsEnumerable.call(from, key)) guardedSet(key, Reflect.get(from, key))
|
||||
rejectCircularInsertion(target, getOwn(from, key), "Object.assign result", node, seen)
|
||||
if (!set(target, key, getOwn(from, key))) {
|
||||
throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError")
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
return target
|
||||
}
|
||||
|
||||
const objectFromEntries = <R>(
|
||||
runner: Runner<R>,
|
||||
source: unknown,
|
||||
node: AstNode,
|
||||
): Effect.Effect<Record<string, unknown>, unknown, R> => {
|
||||
const out: Record<string, unknown> = Object.create(null)
|
||||
): Effect.Effect<ProgramObject, unknown, R> => {
|
||||
const out = new ProgramObject()
|
||||
return Effect.gen(function* () {
|
||||
const cursor = yield* runner.syncIterator(source, node)
|
||||
if (cursor === undefined) {
|
||||
@@ -103,21 +84,12 @@ const objectFromEntries = <R>(
|
||||
yield* preserveConsumerError(
|
||||
cursor,
|
||||
Effect.sync(() => {
|
||||
if (
|
||||
step.value === null ||
|
||||
typeof step.value !== "object" ||
|
||||
Values.isValue(step.value) ||
|
||||
containsOpaqueReference(step.value)
|
||||
) {
|
||||
if (!(step.value instanceof ProgramObject) || containsOpaqueReference(step.value)) {
|
||||
throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node).as(
|
||||
"TypeError",
|
||||
)
|
||||
}
|
||||
const entry = step.value as Record<string, unknown>
|
||||
toProgram(entry[0], "Object.fromEntries key")
|
||||
toProgram(entry[1], "Object.fromEntries value")
|
||||
const key = coerceToString(entry[0])
|
||||
out[key] = entry[1]
|
||||
set(out, coerceToString(getOwn(step.value, 0)), getOwn(step.value, 1))
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -126,7 +98,7 @@ const objectFromEntries = <R>(
|
||||
|
||||
const constructObject = (args: Array<unknown>, node: AstNode): unknown => {
|
||||
const first = args[0]
|
||||
if (first === null || first === undefined) return Object.create(null)
|
||||
if (first === null || first === undefined) return new ProgramObject()
|
||||
if (typeof first === "object") return first
|
||||
throw new InterpreterRuntimeError(
|
||||
`Object(${typeof first}) wrapper objects are not supported; use the primitive value directly.`,
|
||||
@@ -147,18 +119,24 @@ export const objectGlobal = <R>(runner: Runner<R>, toolKeys: (path: ReadonlyArra
|
||||
toProgram(
|
||||
args[0] instanceof ToolReference
|
||||
? [...toolKeys(args[0].path)]
|
||||
: Object.keys(enumerableSource("Object.keys(...)", args[0], node)),
|
||||
: ownKeys(enumerableSource("Object.keys(...)", args[0], node)).filter((key) => typeof key === "string"),
|
||||
"Object.keys result",
|
||||
),
|
||||
),
|
||||
values: sync("Object.values", (args, node) =>
|
||||
Object.values(enumerableSource("Object.values(...)", args[0], node)),
|
||||
values: sync(
|
||||
"Object.values",
|
||||
(args, node) =>
|
||||
new ProgramArray(ownEntries(enumerableSource("Object.values(...)", args[0], node)).map((entry) => entry[1])),
|
||||
),
|
||||
entries: sync("Object.entries", (args, node) =>
|
||||
Object.entries(enumerableSource("Object.entries(...)", args[0], node)).map(([key, item]) => [key, item]),
|
||||
entries: sync(
|
||||
"Object.entries",
|
||||
(args, node) =>
|
||||
new ProgramArray(
|
||||
ownEntries(enumerableSource("Object.entries(...)", args[0], node)).map((entry) => new ProgramArray(entry)),
|
||||
),
|
||||
),
|
||||
hasOwn: sync("Object.hasOwn", (args, node) =>
|
||||
Object.hasOwn(
|
||||
hasOwn(
|
||||
enumerableSource("Object.hasOwn(...)", args[0], node),
|
||||
args[1] === AsyncIteratorSymbol || args[1] === IteratorSymbol ? args[1] : String(args[1]),
|
||||
),
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
import { sync, syncCall } from "../interpreter/host.js"
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import type { SafeObject } from "../data.js"
|
||||
import { ProgramArray, record, set } from "../interpreter/objects.js"
|
||||
import { Values } from "../values.js"
|
||||
import { coerceToNumber, coerceToString } from "./value.js"
|
||||
|
||||
type MatchValue = Array<unknown> & {
|
||||
index?: number
|
||||
groups?: SafeObject
|
||||
indices?: IndicesValue
|
||||
}
|
||||
|
||||
type IndicesValue = Array<unknown> & {
|
||||
groups?: SafeObject
|
||||
}
|
||||
|
||||
export const regexpMethods = new Set(["test", "exec", "toString"])
|
||||
|
||||
export const regexpProperties = new Set([
|
||||
@@ -56,17 +46,12 @@ export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFl
|
||||
)
|
||||
}
|
||||
|
||||
export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
|
||||
const result: MatchValue = Array.from(match, (group) => group)
|
||||
if (match.index !== undefined) result.index = match.index
|
||||
if (match.groups) {
|
||||
const groups: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, group] of Object.entries(match.groups)) {
|
||||
groups[key] = group
|
||||
}
|
||||
result.groups = groups
|
||||
}
|
||||
if (match.indices) result.indices = indicesToValue(match.indices)
|
||||
export const matchToValue = (match: RegExpMatchArray): ProgramArray => {
|
||||
const result = new ProgramArray(Array.from(match, (group) => group))
|
||||
if (match.index !== undefined) set(result, "index", match.index)
|
||||
if (match.input !== undefined) set(result, "input", match.input)
|
||||
if (match.groups) set(result, "groups", record(match.groups))
|
||||
if (match.indices) set(result, "indices", indicesToValue(match.indices))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -143,16 +128,16 @@ const toLength = (value: unknown): number => {
|
||||
return Math.min(Math.floor(number), Number.MAX_SAFE_INTEGER)
|
||||
}
|
||||
|
||||
const indicesToValue = (indices: RegExpIndicesArray): IndicesValue => {
|
||||
const result: IndicesValue = Array.from(indices, (range) => (range === undefined ? undefined : [...range]))
|
||||
if (indices.groups) {
|
||||
const groups: SafeObject = Object.create(null) as SafeObject
|
||||
for (const [key, range] of Object.entries(indices.groups)) {
|
||||
groups[key] = range === undefined ? undefined : [...range]
|
||||
}
|
||||
result.groups = groups
|
||||
return result
|
||||
}
|
||||
result.groups = undefined
|
||||
const indicesToValue = (indices: RegExpIndicesArray): ProgramArray => {
|
||||
const range = (pair: [number, number] | undefined) => (pair === undefined ? undefined : new ProgramArray([...pair]))
|
||||
const result = new ProgramArray(Array.from(indices, range))
|
||||
const groups = indices.groups
|
||||
set(
|
||||
result,
|
||||
"groups",
|
||||
groups === undefined
|
||||
? undefined
|
||||
: record(Object.fromEntries(Object.entries(groups).map(([key, pair]) => [key, range(pair)]))),
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Effect } from "effect"
|
||||
import { toProgram } from "../data.js"
|
||||
import { HostFunction, requiresNew, sync, syncCall } from "../interpreter/host.js"
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { ownEntries, ProgramObject } from "../interpreter/objects.js"
|
||||
import { isRuntimeReference } from "../interpreter/references.js"
|
||||
import { preserveConsumerError, type Runner } from "../interpreter/runner.js"
|
||||
import { Values } from "../values.js"
|
||||
@@ -183,15 +184,14 @@ const constructURLSearchParams = <R>(
|
||||
).as("TypeError")
|
||||
}
|
||||
if (Values.isValue(init)) return new Values.URLSearchParams(new URLSearchParams())
|
||||
const data = toProgram(init, "new URLSearchParams input")
|
||||
if (data === null || typeof data !== "object") {
|
||||
if (!(init instanceof ProgramObject)) {
|
||||
throw new InterpreterRuntimeError(
|
||||
"new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.",
|
||||
node,
|
||||
).as("TypeError")
|
||||
}
|
||||
return new Values.URLSearchParams(
|
||||
new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))),
|
||||
new URLSearchParams(Object.fromEntries(ownEntries(init).map(([key, value]) => [key, coerceToString(value)]))),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type HostFunction, sync, type SyncOptions } from "../interpreter/host.js"
|
||||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { type SafeObject, toProgram } from "../data.js"
|
||||
import { toProgram } from "../data.js"
|
||||
import { get, ProgramArray, ProgramError, set } from "../interpreter/objects.js"
|
||||
import { Values } from "../values.js"
|
||||
|
||||
export const errorConstructors = new Set([
|
||||
@@ -16,21 +17,21 @@ export const errorConstructors = new Set([
|
||||
|
||||
export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="])
|
||||
|
||||
const ErrorBrand: unique symbol = Symbol("codemode.error")
|
||||
|
||||
export const createErrorValue = (name: string, message: string): SafeObject => {
|
||||
const value = Object.assign(Object.create(null) as SafeObject, { name, message })
|
||||
Object.defineProperty(value, ErrorBrand, { value: name })
|
||||
export const createErrorValue = (name: string, message: string): ProgramError => {
|
||||
const value = new ProgramError(name)
|
||||
set(value, "name", name)
|
||||
set(value, "message", message)
|
||||
return value
|
||||
}
|
||||
|
||||
export const createAggregateErrorValue = (errors: Array<unknown>, message: string): SafeObject =>
|
||||
Object.assign(createErrorValue("AggregateError", message), { errors })
|
||||
export const createAggregateErrorValue = (errors: Array<unknown>, message: string): ProgramError => {
|
||||
const value = createErrorValue("AggregateError", message)
|
||||
set(value, "errors", new ProgramArray(errors))
|
||||
return value
|
||||
}
|
||||
|
||||
export const errorBrandName = (value: unknown): string | undefined =>
|
||||
value !== null && typeof value === "object"
|
||||
? ((value as Record<PropertyKey, unknown>)[ErrorBrand] as string | undefined)
|
||||
: undefined
|
||||
value instanceof ProgramError ? value.errorName : undefined
|
||||
|
||||
export const coerceToString = (value: unknown): string => {
|
||||
if (value === null) return "null"
|
||||
@@ -42,29 +43,27 @@ export const coerceToString = (value: unknown): string => {
|
||||
if (value instanceof Values.Set) return "[object Set]"
|
||||
if (value instanceof Values.URL) return value.url.href
|
||||
if (value instanceof Values.URLSearchParams) return value.params.toString()
|
||||
if (errorBrandName(value) !== undefined) {
|
||||
if (value instanceof ProgramError) {
|
||||
// Match Error.prototype.toString: "name: message", or just one when the other is empty.
|
||||
const error = value as { name?: unknown; message?: unknown }
|
||||
const name = typeof error.name === "string" ? error.name : "Error"
|
||||
const message = typeof error.message === "string" ? error.message : ""
|
||||
if (message === "") return name
|
||||
if (name === "") return message
|
||||
return `${name}: ${message}`
|
||||
const name = get(value, "name")
|
||||
const message = get(value, "message")
|
||||
const shownName = typeof name === "string" ? name : "Error"
|
||||
const shownMessage = typeof message === "string" ? message : ""
|
||||
if (shownMessage === "") return shownName
|
||||
if (shownName === "") return shownMessage
|
||||
return `${shownName}: ${shownMessage}`
|
||||
}
|
||||
if (typeof value === "object") {
|
||||
return Array.isArray(value)
|
||||
? value.map((item) => (item === null || item === undefined ? "" : coerceToString(item))).join(",")
|
||||
: "[object Object]"
|
||||
if (value instanceof ProgramArray) {
|
||||
return value.items.map((item) => (item === null || item === undefined ? "" : coerceToString(item))).join(",")
|
||||
}
|
||||
if (typeof value === "object") return "[object Object]"
|
||||
return String(value)
|
||||
}
|
||||
|
||||
export const coerceToNumber = (value: unknown): number => {
|
||||
if (value instanceof Values.Date) return value.time
|
||||
if (Values.isValue(value)) return Number.NaN
|
||||
// Arrays coerce through our own string coercion: host Number(array) joins with host
|
||||
// ToPrimitive, which throws on the null-prototype objects the interpreter produces.
|
||||
if (Array.isArray(value)) return Number(coerceToString(value))
|
||||
if (value instanceof ProgramArray) return Number(coerceToString(value))
|
||||
return value !== null && typeof value === "object" ? Number.NaN : Number(value)
|
||||
}
|
||||
|
||||
@@ -78,8 +77,6 @@ const coerce = (name: Coercion, args: Array<unknown>, node: AstNode): unknown =>
|
||||
if (name === "String") return ""
|
||||
}
|
||||
const raw = args[0]
|
||||
// Error values are plain SafeObjects; the toProgram path below would strip their brand.
|
||||
if (name === "String" && errorBrandName(raw) !== undefined) return coerceToString(raw)
|
||||
if (Values.isValue(raw)) {
|
||||
if (name === "Boolean") return true
|
||||
if (name === "Number") return coerceToNumber(raw)
|
||||
|
||||
@@ -98,7 +98,7 @@ describe("Object.keys over arrays", () => {
|
||||
)
|
||||
expect((await error(`const { a } = new Map(); return a`)).message).toContain("received a Map.")
|
||||
expect((await error(`return Array.from(7)`)).message).toContain("received a number.")
|
||||
expect((await error(`return (() => 1).x`)).message).toContain("Cannot read properties of a function")
|
||||
expect(await value(`return [(() => 1).x, Object.keys(() => 1)]`)).toEqual([null, []])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -167,17 +167,14 @@ describe("Object.assign Test262 parity", () => {
|
||||
).toEqual([true, [1, 8, 9], true, [1, 8, 3], true, 5, true, 0])
|
||||
})
|
||||
|
||||
test("array targets accept only array indexes (deviation from target-Array.js)", async () => {
|
||||
test("test/built-ins/Object/assign/target-Array.js", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const target = [7]
|
||||
const out = []
|
||||
for (const source of [{ length: 0 }, { x: 1 }, { "1.5": 1 }, { "-0": 1 }, { ["__proto__"]: null }]) {
|
||||
try { Object.assign(target, source) } catch (error) { out.push(error.name) }
|
||||
}
|
||||
return [out, [...target], target.length, Object.keys(target)]
|
||||
for (const source of [{ x: 1 }, { "1.5": 1 }, { "-0": 1 }, { 1: 8 }, { length: 1 }]) Object.assign(target, source)
|
||||
return [[...target], target.length, Object.keys(target), target.x]
|
||||
`),
|
||||
).toEqual([["TypeError", "TypeError", "TypeError", "TypeError", "TypeError"], [7], 1, ["0"]])
|
||||
).toEqual([[7], 1, ["0", "x", "1.5", "-0"], 1])
|
||||
})
|
||||
|
||||
test("test/built-ins/Object/assign/Target-{Null,Undefined}.js", async () => {
|
||||
|
||||
@@ -81,27 +81,26 @@ describe("H3: array property access reads as undefined (not a throw)", () => {
|
||||
).toEqual(["b", "b", null, null, "a", null])
|
||||
})
|
||||
|
||||
test("noncanonical keys cannot mutate or delete an aliased element", async () => {
|
||||
test("noncanonical keys are ordinary properties that never alias an element", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const values = ["a", "b"]
|
||||
let writes = 0
|
||||
try { values["01"] = ++writes } catch {}
|
||||
values["01"] = "c"
|
||||
const before = [values["01"], values[1], values.length]
|
||||
const removed = delete values["01"]
|
||||
return [writes, removed, values]
|
||||
return [before, removed, values["01"], values]
|
||||
`),
|
||||
).toEqual([0, true, ["a", "b"]])
|
||||
).toEqual([["c", "b", 2], true, null, ["a", "b"]])
|
||||
})
|
||||
|
||||
test("the maximum array length is not accepted as an array index", async () => {
|
||||
test("the maximum array length is a property, not an index", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const values = []
|
||||
let writes = 0
|
||||
try { values["4294967295"] = ++writes } catch {}
|
||||
return [writes, values.length]
|
||||
values["4294967295"] = 1
|
||||
return [values["4294967295"], values.length]
|
||||
`),
|
||||
).toEqual([0, 0])
|
||||
).toEqual([1, 0])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -246,16 +245,14 @@ describe("property deletion", () => {
|
||||
expect(await value(`const values = [1, 2]; return [delete values.length, values.length]`)).toEqual([false, 2])
|
||||
})
|
||||
|
||||
test("does not broaden unsupported array property assignment", async () => {
|
||||
test("arrays accept named properties like JS, and they stay out of the JSON form", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const values = []
|
||||
let rightHandSideRuns = 0
|
||||
function next() { rightHandSideRuns++; return 1 }
|
||||
try { values.field = next() } catch {}
|
||||
return rightHandSideRuns
|
||||
const values = [1]
|
||||
values.field = 2
|
||||
return [values.field, Object.keys(values), values]
|
||||
`),
|
||||
).toBe(0)
|
||||
).toEqual([2, ["0", "field"], [1]])
|
||||
})
|
||||
|
||||
test("optional deletion short-circuits without evaluating the key", async () => {
|
||||
@@ -916,3 +913,48 @@ describe("coercion parity: unknown static members read as undefined", () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("functions are objects", () => {
|
||||
test("name follows NamedEvaluation and length counts required parameters", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
function decl(a, b = 1, ...rest) {}
|
||||
const arrow = () => {}
|
||||
const named = function inner() {}
|
||||
let assigned
|
||||
assigned = (a, b) => {}
|
||||
const { fromDefault = () => {} } = {}
|
||||
const [fromArray = function () {}] = []
|
||||
const obj = { method() {}, key: () => {}, [Symbol.iterator]: () => {} }
|
||||
const passthrough = (0, () => {})
|
||||
return [
|
||||
[decl.name, decl.length],
|
||||
[arrow.name, named.name, assigned.name, assigned.length],
|
||||
[fromDefault.name, fromArray.name],
|
||||
[obj.method.name, obj.key.name, obj[Symbol.iterator].name],
|
||||
passthrough.name,
|
||||
]
|
||||
`),
|
||||
).toEqual([
|
||||
["decl", 1],
|
||||
["arrow", "inner", "assigned", 2],
|
||||
["fromDefault", "fromArray"],
|
||||
["method", "key", "[Symbol.iterator]"],
|
||||
"",
|
||||
])
|
||||
})
|
||||
|
||||
test("functions hold own properties; name and length are read-only", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const fn = () => 1
|
||||
fn.count = 2
|
||||
fn.count += 1
|
||||
let renamed = false
|
||||
try { fn.name = "other" } catch (error) { renamed = error instanceof TypeError }
|
||||
const { name, count } = fn
|
||||
return [fn.count, Object.keys(fn), "count" in fn, "name" in fn, name, count, renamed, delete fn.count, fn.count]
|
||||
`),
|
||||
).toEqual([3, ["count"], true, true, "fn", 3, true, true, null])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { CodeMode, Tool } from "../src/index.js"
|
||||
import { AsyncIteratorSymbol, IteratorSymbol } from "../src/interpreter/model.js"
|
||||
import { objectAssign } from "../src/stdlib/object.js"
|
||||
|
||||
// Standard-library value types: Date, RegExp, Map, Set. Programs use them as ordinary JS;
|
||||
// intra-CodeMode checkpoints (Object.* helpers, spread, coercion inputs) preserve the live
|
||||
@@ -770,13 +768,18 @@ describe("stdlib integration", () => {
|
||||
],
|
||||
])
|
||||
expect(await value(`const match = /a/.exec("ba"); return [Object.values(match), Object.entries(match)]`)).toEqual([
|
||||
["a", 1],
|
||||
["a", 1, "ba"],
|
||||
[
|
||||
["0", "a"],
|
||||
["index", 1],
|
||||
["input", "ba"],
|
||||
],
|
||||
])
|
||||
expect(await value(`return Object.keys(Object.values({ match: /a/.exec("ba") })[0])`)).toEqual(["0", "index"])
|
||||
expect(await value(`return Object.keys(Object.values({ match: /a/.exec("ba") })[0])`)).toEqual([
|
||||
"0",
|
||||
"index",
|
||||
"input",
|
||||
])
|
||||
})
|
||||
|
||||
test("Object.fromEntries accepts every supported entry collection", async () => {
|
||||
@@ -849,86 +852,6 @@ describe("stdlib integration", () => {
|
||||
expect(await value(`try { Object.assign(null, { a: 1 }); return false } catch { return true }`)).toBe(true)
|
||||
})
|
||||
|
||||
test("Object.assign ignores non-enumerable supported symbols without reading them", () => {
|
||||
const target = {}
|
||||
const reads: Array<boolean> = []
|
||||
const source = Object.defineProperty({}, IteratorSymbol, {
|
||||
get() {
|
||||
reads.push(true)
|
||||
return target
|
||||
},
|
||||
})
|
||||
expect(objectAssign([target, source], { type: "CallExpression", start: 0, end: 0 })).toBe(target)
|
||||
expect(reads).toEqual([])
|
||||
expect(Object.hasOwn(target, IteratorSymbol)).toBe(false)
|
||||
})
|
||||
|
||||
test("Object.assign ignores nested non-enumerable supported symbols during cycle checks", () => {
|
||||
const target = {}
|
||||
const reads: Array<boolean> = []
|
||||
const nested = Object.defineProperty({}, IteratorSymbol, {
|
||||
get() {
|
||||
reads.push(true)
|
||||
return target
|
||||
},
|
||||
})
|
||||
expect(objectAssign([target, { nested }], { type: "CallExpression", start: 0, end: 0 })).toBe(target)
|
||||
expect(reads).toEqual([])
|
||||
expect(target).toEqual({ nested })
|
||||
})
|
||||
|
||||
test("Object.assign rejects cycles through supported symbols on nested arrays", () => {
|
||||
const target = {}
|
||||
const nested = Object.defineProperty([], IteratorSymbol, { enumerable: true, value: target })
|
||||
expect(() => objectAssign([target, { nested }], { type: "CallExpression", start: 0, end: 0 })).toThrow(
|
||||
"Object.assign result contains a circular value.",
|
||||
)
|
||||
expect(Object.hasOwn(target, "nested")).toBe(false)
|
||||
})
|
||||
|
||||
test("Object.assign cycle checks traverse sparse keys lazily", () => {
|
||||
const target = {}
|
||||
const reads: Array<boolean> = []
|
||||
const nested = Object.defineProperties([], {
|
||||
4294967294: { enumerable: true, value: target },
|
||||
later: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
reads.push(true)
|
||||
return null
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(() => objectAssign([target, { nested }], { type: "CallExpression", start: 0, end: 0 })).toThrow(
|
||||
"Object.assign result contains a circular value.",
|
||||
)
|
||||
expect(reads).toEqual([])
|
||||
})
|
||||
|
||||
test("Object.assign stops after a supported symbol write fails", () => {
|
||||
const previous = () => ({ done: true })
|
||||
const target = Object.defineProperty({}, IteratorSymbol, { value: previous })
|
||||
const reads: Array<boolean> = []
|
||||
const source = Object.defineProperties(
|
||||
{},
|
||||
{
|
||||
[IteratorSymbol]: { enumerable: true, value: () => ({ done: false }) },
|
||||
[AsyncIteratorSymbol]: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
reads.push(true)
|
||||
return () => ({ done: true })
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
expect(() => objectAssign([target, source], { type: "CallExpression", start: 0, end: 0 })).toThrow(
|
||||
"Object.assign could not assign property",
|
||||
)
|
||||
expect(Reflect.get(target, IteratorSymbol)).toBe(previous)
|
||||
expect(reads).toEqual([])
|
||||
})
|
||||
|
||||
test("Object.assign rejects direct and nested cycles", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
@@ -1000,27 +923,6 @@ describe("stdlib integration", () => {
|
||||
).toEqual([true, true, true, 2])
|
||||
})
|
||||
|
||||
test("Object.assign traverses shared aliases once", () => {
|
||||
const reads: Array<boolean> = []
|
||||
const shared = Object.defineProperty({}, "value", {
|
||||
enumerable: true,
|
||||
get() {
|
||||
reads.push(true)
|
||||
return 1
|
||||
},
|
||||
})
|
||||
const target = {}
|
||||
expect(
|
||||
objectAssign([target, { left: shared, right: shared }], {
|
||||
type: "CallExpression",
|
||||
start: 0,
|
||||
end: 0,
|
||||
}),
|
||||
).toBe(target)
|
||||
expect(target).toEqual({ left: shared, right: shared })
|
||||
expect(reads).toEqual([true])
|
||||
})
|
||||
|
||||
test("assignment resolves and reads its left side before evaluating the right side", async () => {
|
||||
expect(await value(`let x = 1; x += (x = 5); return x`)).toBe(6)
|
||||
expect(await value(`let i = 0; const values = [9]; values[i++] = i; return [values, i]`)).toEqual([[1], 1])
|
||||
@@ -1199,7 +1101,7 @@ describe("CodeMode values at intra-CodeMode checkpoints", () => {
|
||||
const diagnostic = await error(`return Array.from(Promise.resolve([1]))`)
|
||||
expect(diagnostic.kind).toBe("InvalidDataValue")
|
||||
expect(diagnostic.message).toContain("await")
|
||||
expect((await error(`return Array.from(() => 1)`)).kind).toBe("InvalidDataValue")
|
||||
expect(await value(`return Array.from((a, b) => 1)`)).toEqual([null, null])
|
||||
})
|
||||
|
||||
test("regexes stay callable through Object.values", async () => {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Runs the test262 files vendored under test/test262 (see test/test262/README.md) verbatim.
|
||||
* Files listed in test/test262/skipped.txt fail on a known interpreter gap and are skipped;
|
||||
* `bun run script/test262-report.ts` shows current gaps and which skipped files pass again.
|
||||
* Licensed under test/LICENSE.test262.
|
||||
*/
|
||||
import { test } from "bun:test"
|
||||
import { root, run, skipped } from "./test262/run.js"
|
||||
|
||||
for (const file of [...new Bun.Glob("**/*.js").scanSync({ cwd: root })].sort()) {
|
||||
const define = skipped.has(file) ? test.skip : test
|
||||
define(
|
||||
file,
|
||||
async () => {
|
||||
const outcome = await run(file)
|
||||
if (outcome.status === "fail") throw new Error(outcome.reason)
|
||||
},
|
||||
10_000,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Upstream test262 files are populated locally by script/sync-test262.ts and not committed yet.
|
||||
built-ins/
|
||||
language/
|
||||
@@ -0,0 +1,41 @@
|
||||
# test262
|
||||
|
||||
Runs upstream [test262](https://github.com/tc39/test262) files verbatim through `test/test262.test.ts`. The files
|
||||
themselves are not committed yet: populate them from a local checkout with
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tc39/test262 ~/development/test262
|
||||
bun run script/sync-test262.ts ~/development/test262
|
||||
```
|
||||
|
||||
Without them the runner registers no tests, so CI is unaffected. Licensed under `test/LICENSE.test262`.
|
||||
|
||||
## Layout
|
||||
|
||||
- `manifest.json` — the pinned upstream revision, which upstream directories are copied, and what is left out.
|
||||
- `built-ins/`, `language/` — the copied files, mirroring upstream `test/`; gitignored.
|
||||
- `skipped.txt` — vendored files that fail on a known interpreter gap, one `path # reason` per line. They are
|
||||
skipped, and each gap is listed as unchecked in `interpreter-support.md`.
|
||||
- `run.ts` — runs one file: prepends `"use strict"`, provides the harness (`assert`, `Test262Error`,
|
||||
`compareArray`, `$DONE`, `$DONOTEVALUATE`) as host globals, and interprets the file's frontmatter (`negative`,
|
||||
`flags: [async]`).
|
||||
|
||||
## What is not vendored
|
||||
|
||||
`script/sync-test262.ts` skips a file when its frontmatter declares a `flags`, `features`, or `includes` value the
|
||||
manifest marks unsupported, or when its code matches one of the manifest's `boundaries` patterns. The sync checks the
|
||||
checkout is at the pinned revision, so every machine runs the same 3993 files. Boundaries are
|
||||
intentional limits of the interpreter, not compatibility work: classes, `this`, `arguments`, prototype objects,
|
||||
property descriptors, accessors, boxed primitives, sloppy mode, `eval`, `Symbol()`, and the `$262` host API. If one
|
||||
of those decisions changes, delete its entry and re-sync; the tests are upstream, not lost.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
bun run script/sync-test262.ts /path/to/test262 # re-copy at the pinned revision; edit manifest.json to change scope
|
||||
bun run script/test262-report.ts [--write] [dir] # run everything, group failures by cause; --write regenerates skipped.txt
|
||||
bun test test/test262.test.ts # what CI runs
|
||||
```
|
||||
|
||||
When a fix makes skipped files pass, the report lists them so they can be removed from `skipped.txt`, and the
|
||||
matching gap in `interpreter-support.md` is checked in the same change.
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"revision": "250f204f23a9249ff204be2baec29600faae7b75",
|
||||
"directories": ["built-ins/Array/prototype", "language/statements"],
|
||||
"harness": ["assert.js", "sta.js", "compareArray.js", "doneprintHandle.js"],
|
||||
"flags": ["module", "raw", "noStrict"],
|
||||
"boundaries": {
|
||||
"class": "\\bclass\\s*[A-Za-z_${]",
|
||||
"this": "\\bthis\\b",
|
||||
"arguments": "\\barguments\\b",
|
||||
"call/apply/bind": "\\.(call|apply|bind)\\s*\\(",
|
||||
"prototype objects": "\\.prototype\\b",
|
||||
"accessor properties": "\\b(get|set)\\s+[\\w$\\[][^\\n(]*\\(",
|
||||
"property descriptors": "Object\\.(defineProperty|defineProperties|getOwnPropertyDescriptors?|getOwnPropertyNames|create|getPrototypeOf|setPrototypeOf|freeze|seal|preventExtensions|isFrozen|isSealed|isExtensible)\\b",
|
||||
"boxed primitives": "\\bnew\\s+(String|Number|Boolean)\\s*\\(",
|
||||
"sloppy mode": "\\bwith\\s*\\(",
|
||||
"eval": "\\b(eval|Function)\\b",
|
||||
"new.target": "\\bnew\\.target\\b",
|
||||
"globalThis": "\\bglobalThis\\b",
|
||||
"Symbol": "\\bSymbol\\b(?!\\.(iterator|asyncIterator)\\b)",
|
||||
"$262 host API": "\\$262"
|
||||
},
|
||||
"features": [
|
||||
"BigInt",
|
||||
"Proxy",
|
||||
"proxy-missing-checks",
|
||||
"Reflect",
|
||||
"Reflect.construct",
|
||||
"Reflect.set",
|
||||
"Reflect.setPrototypeOf",
|
||||
"Symbol",
|
||||
"Symbol.hasInstance",
|
||||
"Symbol.isConcatSpreadable",
|
||||
"Symbol.match",
|
||||
"Symbol.matchAll",
|
||||
"Symbol.prototype.description",
|
||||
"Symbol.replace",
|
||||
"Symbol.search",
|
||||
"Symbol.species",
|
||||
"Symbol.split",
|
||||
"Symbol.toPrimitive",
|
||||
"Symbol.toStringTag",
|
||||
"Symbol.unscopables",
|
||||
"symbols-as-weakmap-keys",
|
||||
"class",
|
||||
"class-fields-private",
|
||||
"class-fields-private-in",
|
||||
"class-fields-public",
|
||||
"class-methods-private",
|
||||
"class-static-block",
|
||||
"class-static-fields-private",
|
||||
"class-static-fields-public",
|
||||
"class-static-methods-private",
|
||||
"super",
|
||||
"new.target",
|
||||
"decorators",
|
||||
"TypedArray",
|
||||
"TypedArray.prototype.at",
|
||||
"ArrayBuffer",
|
||||
"SharedArrayBuffer",
|
||||
"DataView",
|
||||
"Atomics",
|
||||
"Atomics.pause",
|
||||
"Atomics.waitAsync",
|
||||
"resizable-arraybuffer",
|
||||
"arraybuffer-transfer",
|
||||
"immutable-arraybuffer",
|
||||
"Float16Array",
|
||||
"Float32Array",
|
||||
"Float64Array",
|
||||
"Int8Array",
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Uint8Array",
|
||||
"Uint8ClampedArray",
|
||||
"Uint16Array",
|
||||
"Uint32Array",
|
||||
"uint8array-base64",
|
||||
"WeakMap",
|
||||
"WeakSet",
|
||||
"WeakRef",
|
||||
"FinalizationRegistry",
|
||||
"Intl-enumeration",
|
||||
"Temporal",
|
||||
"ShadowRealm",
|
||||
"cross-realm",
|
||||
"dynamic-import",
|
||||
"import.meta",
|
||||
"import-attributes",
|
||||
"import-defer",
|
||||
"export-defer",
|
||||
"source-phase-imports",
|
||||
"source-phase-imports-module-source",
|
||||
"import-text",
|
||||
"import-bytes",
|
||||
"json-modules",
|
||||
"top-level-await",
|
||||
"explicit-resource-management",
|
||||
"tail-call-optimization",
|
||||
"caller",
|
||||
"IsHTMLDDA",
|
||||
"host-gc-required",
|
||||
"legacy-regexp",
|
||||
"__proto__",
|
||||
"__getter__",
|
||||
"__setter__"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// Runs one vendored test262 file verbatim. The harness (assert, Test262Error, compareArray, $DONE,
|
||||
// $DONOTEVALUATE) is host-provided because programs cannot attach properties to functions, which
|
||||
// test262's own assert.js relies on.
|
||||
import path from "node:path"
|
||||
import { Cause, Effect } from "effect"
|
||||
import { caughtErrorValue } from "../../src/interpreter/errors.js"
|
||||
import { executeProgram } from "../../src/interpreter/execute.js"
|
||||
import type { Host } from "../../src/interpreter/globals.js"
|
||||
import { HostFunction } from "../../src/interpreter/host.js"
|
||||
import { ProgramThrow } from "../../src/interpreter/model.js"
|
||||
import { get, ProgramArray, ProgramObject } from "../../src/interpreter/objects.js"
|
||||
import { createErrorValue, errorBrandName } from "../../src/stdlib/value.js"
|
||||
import { ToolRuntime } from "../../src/tool-runtime.js"
|
||||
|
||||
export const root = import.meta.dir
|
||||
|
||||
/** Files that fail on a known gap, mapped to the gap; see skipped.txt. */
|
||||
export const skipped = new Map(
|
||||
(await Bun.file(path.join(root, "skipped.txt")).text())
|
||||
.split("\n")
|
||||
.filter((line) => line.includes("#"))
|
||||
.map((line) => [line.slice(0, line.indexOf("#")).trim(), line.slice(line.indexOf("#") + 1).trim()]),
|
||||
)
|
||||
|
||||
export type Outcome = { readonly status: "pass" } | { readonly status: "fail"; readonly reason: string }
|
||||
|
||||
type Frontmatter = {
|
||||
flags?: Array<string>
|
||||
negative?: { phase: "parse" | "resolution" | "runtime"; type: string }
|
||||
}
|
||||
|
||||
const limits = { timeoutMs: 5000, maxToolCalls: undefined, maxOutputBytes: undefined }
|
||||
const prepared = ToolRuntime.prepare<never>({})
|
||||
|
||||
export const run = async (file: string): Promise<Outcome> => {
|
||||
const source = await Bun.file(path.join(root, file)).text()
|
||||
const start = source.indexOf("/*---")
|
||||
const meta = Bun.YAML.parse(source.slice(start + 5, source.indexOf("---*/", start))) as Frontmatter
|
||||
let done: { error: unknown } | undefined
|
||||
// Sloppy-only tests are never vendored, so every file runs as the strict half of test262's two-mode run.
|
||||
// A host drains the job queue after an async test's script ends; the program end interrupts un-awaited
|
||||
// work instead, so drain explicitly.
|
||||
const drain = meta.flags?.includes("async") ? "\nfor (let i = 0; i < 100; i++) await null" : ""
|
||||
const result = await Effect.runPromise(
|
||||
executeProgram(`"use strict";\n${source}${drain}`, prepared, limits, {}, (host) =>
|
||||
harness(host, (error) => {
|
||||
done ??= { error }
|
||||
}),
|
||||
),
|
||||
)
|
||||
if (meta.negative !== undefined) {
|
||||
// Runtime negatives only check that execution failed: diagnostics do not carry the error name.
|
||||
if (result.ok) return { status: "fail", reason: `expected ${meta.negative.type} but completed` }
|
||||
if (meta.negative.phase === "runtime" || result.error.kind === "ParseError") return { status: "pass" }
|
||||
return {
|
||||
status: "fail",
|
||||
reason: `expected ${meta.negative.type} but got ${result.error.kind}: ${result.error.message}`,
|
||||
}
|
||||
}
|
||||
if (!result.ok) return { status: "fail", reason: `${result.error.kind}: ${result.error.message}` }
|
||||
if (!meta.flags?.includes("async")) return { status: "pass" }
|
||||
if (done === undefined) return { status: "fail", reason: "$DONE was never called" }
|
||||
if (done.error !== undefined) return { status: "fail", reason: `$DONE: ${show(done.error)}` }
|
||||
return { status: "pass" }
|
||||
}
|
||||
|
||||
const harness = <R>(host: Host<R>, onDone: (error: unknown) => void): ReadonlyArray<readonly [string, unknown]> => {
|
||||
const fail = (message: string) => Effect.fail(new ProgramThrow(createErrorValue("Test262Error", message)))
|
||||
const prefix = (message: unknown) => (message === undefined ? "" : `${String(message)} `)
|
||||
const compare = (a: unknown, b: unknown) =>
|
||||
a instanceof ProgramArray &&
|
||||
b instanceof ProgramArray &&
|
||||
a.items.length === b.items.length &&
|
||||
a.items.every((value, i) => Object.is(value, b.items[i]))
|
||||
const test262Error = new HostFunction<R>({
|
||||
name: "Test262Error",
|
||||
call: (args) => Effect.succeed(createErrorValue("Test262Error", args[0] === undefined ? "" : String(args[0]))),
|
||||
construct: (args) => Effect.succeed(createErrorValue("Test262Error", args[0] === undefined ? "" : String(args[0]))),
|
||||
instanceOf: (value) => errorBrandName(value) === "Test262Error",
|
||||
members: { thrower: new HostFunction<R>({ name: "Test262Error.thrower", call: (args) => fail(String(args[0])) }) },
|
||||
})
|
||||
const compareArray = new HostFunction<R>({
|
||||
name: "compareArray",
|
||||
call: (args) => Effect.succeed(compare(args[0], args[1])),
|
||||
members: {
|
||||
format: new HostFunction<R>({ name: "compareArray.format", call: (args) => Effect.succeed(show(args[0])) }),
|
||||
},
|
||||
})
|
||||
const assert = new HostFunction<R>({
|
||||
name: "assert",
|
||||
call: (args) =>
|
||||
args[0] === true
|
||||
? Effect.void
|
||||
: fail(args[1] === undefined ? `Expected true but got ${show(args[0])}` : String(args[1])),
|
||||
members: {
|
||||
sameValue: new HostFunction<R>({
|
||||
name: "assert.sameValue",
|
||||
call: (args) =>
|
||||
Object.is(args[0], args[1])
|
||||
? Effect.void
|
||||
: fail(`${prefix(args[2])}Expected SameValue(«${show(args[0])}», «${show(args[1])}») to be true`),
|
||||
}),
|
||||
notSameValue: new HostFunction<R>({
|
||||
name: "assert.notSameValue",
|
||||
call: (args) =>
|
||||
Object.is(args[0], args[1])
|
||||
? fail(`${prefix(args[2])}Expected SameValue(«${show(args[0])}», «${show(args[1])}») to be false`)
|
||||
: Effect.void,
|
||||
}),
|
||||
compareArray: new HostFunction<R>({
|
||||
name: "assert.compareArray",
|
||||
call: (args) =>
|
||||
compare(args[0], args[1])
|
||||
? Effect.void
|
||||
: fail(
|
||||
`Actual ${show(args[0])} and expected ${show(args[1])} should have the same contents. ${prefix(args[2])}`,
|
||||
),
|
||||
}),
|
||||
throws: new HostFunction<R>({
|
||||
name: "assert.throws",
|
||||
call: (args, node) => {
|
||||
const expected = args[0] instanceof HostFunction ? args[0].name : show(args[0])
|
||||
return host.runner.invokeCallable(args[1], [], node).pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onFailure: (cause) => {
|
||||
if (cause.reasons.some(Cause.isInterruptReason)) return Effect.failCause(cause)
|
||||
const thrown = caughtErrorValue(Cause.squash(cause))
|
||||
if (thrown === null || typeof thrown !== "object") {
|
||||
return fail(`${prefix(args[2])}Thrown value was not an object!`)
|
||||
}
|
||||
const actual = errorBrandName(thrown)
|
||||
if (actual === expected) return Effect.void
|
||||
return fail(`${prefix(args[2])}Expected a ${expected} but got a ${actual ?? "non-error object"}`)
|
||||
},
|
||||
onSuccess: () =>
|
||||
fail(`${prefix(args[2])}Expected a ${expected} to be thrown but no exception was thrown at all`),
|
||||
}),
|
||||
)
|
||||
},
|
||||
}),
|
||||
},
|
||||
})
|
||||
return [
|
||||
["assert", assert],
|
||||
["compareArray", compareArray],
|
||||
["Test262Error", test262Error],
|
||||
["$DONE", new HostFunction<R>({ name: "$DONE", call: (args) => Effect.sync(() => onDone(args[0])) })],
|
||||
[
|
||||
"$DONOTEVALUATE",
|
||||
new HostFunction<R>({
|
||||
name: "$DONOTEVALUATE",
|
||||
call: () => Effect.fail(new ProgramThrow("Test262: This statement should not be evaluated.")),
|
||||
}),
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
const show = (value: unknown): string => {
|
||||
if (typeof value === "string") return JSON.stringify(value)
|
||||
if (Object.is(value, -0)) return "-0"
|
||||
if (value instanceof ProgramArray) return `[${value.items.map(show).join(", ")}]`
|
||||
if (value instanceof HostFunction) return value.name
|
||||
if (!(value instanceof ProgramObject)) return String(value)
|
||||
const message = get(value, "message")
|
||||
return typeof message === "string" ? `${errorBrandName(value) ?? "object"}: ${message}` : "object"
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
built-ins/Array/prototype/concat/create-ctor-non-object.js # a.concat() throws a TypeError exception Expected a TypeError to be thrown but no exception was throw
|
||||
built-ins/Array/prototype/copyWithin/coerced-values-end.js # Array.copyWithin expects end to be a number.
|
||||
built-ins/Array/prototype/copyWithin/coerced-values-start-change-target.js # Array.copyWithin expects start to be a number.
|
||||
built-ins/Array/prototype/copyWithin/coerced-values-start.js # Array.copyWithin expects start to be a number.
|
||||
built-ins/Array/prototype/copyWithin/coerced-values-target.js # Array.copyWithin expects target index to be a number.
|
||||
built-ins/Array/prototype/copyWithin/fill-holes.js # .hasOwnProperty is not a function.
|
||||
built-ins/Array/prototype/copyWithin/return-abrupt-from-end.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/copyWithin/return-abrupt-from-start.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/copyWithin/return-abrupt-from-target.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/entries/iteration-mutable.js # .next is not a function.
|
||||
built-ins/Array/prototype/entries/iteration.js # .next is not a function.
|
||||
built-ins/Array/prototype/every/15.4.4.16-7-7.js # .hasOwnProperty is not a function.
|
||||
built-ins/Array/prototype/every/15.4.4.16-8-13.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/fill/coerced-indexes.js # Array.fill expects start to be a number.
|
||||
built-ins/Array/prototype/fill/return-abrupt-from-end.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/fill/return-abrupt-from-start.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/filter/15.4.4.20-10-4.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/filter/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all
|
||||
built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js # Array.flat expects depth to be a number.
|
||||
built-ins/Array/prototype/forEach/15.4.4.18-8-12.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/includes/length-zero-returns-false.js # Array.includes expects a value and optional start index.
|
||||
built-ins/Array/prototype/includes/no-arg.js # Array.includes expects a value and optional start index.
|
||||
built-ins/Array/prototype/includes/return-abrupt-tointeger-fromindex.js # Expected a Test262Error but got a Error
|
||||
built-ins/Array/prototype/includes/tointeger-fromindex.js # Array.includes expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-1.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-15.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-16.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-17.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-18.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-19.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-20.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-21.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-22.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-23.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-24.js # Expected a TypeError but got a Error
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-3.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-5-5.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-9-7.js # Array assignment result contains a circular value.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-9-8.js # .toString is not a function.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-9-a-3.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/15.4.4.14-9-a-6.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js # Array.indexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/join/S15.4.4.5_A1.2_T2.js # Array.join expects zero arguments or one string separator.
|
||||
built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js # Array.join expects zero arguments or one string separator.
|
||||
built-ins/Array/prototype/join/S15.4.4.5_A3.1_T2.js # Array.join expects zero arguments or one string separator.
|
||||
built-ins/Array/prototype/join/S15.4.4.5_A3.2_T2.js # x.join() must return "*" Expected SameValue(«"[object Object]"», «"*"») to be true
|
||||
built-ins/Array/prototype/keys/iteration-mutable.js # .next is not a function.
|
||||
built-ins/Array/prototype/keys/iteration.js # .next is not a function.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-1.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-15.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-16.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-17.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-18.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-19.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-20.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-21.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-22.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-23.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-24.js # Expected a TypeError but got a Error
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-3.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-4.js # a.lastIndexOf(2,undefined) Expected SameValue(«1», «-1») to be true
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-5.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-7.js # Array assignment result contains a circular value.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-8.js # .toString is not a function.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-a-3.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/15.4.4.15-8-a-6.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js # Array.lastIndexOf expects start index to be a number.
|
||||
built-ins/Array/prototype/map/15.4.4.19-8-7.js # .toString is not a function.
|
||||
built-ins/Array/prototype/map/15.4.4.19-9-4.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/map/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all
|
||||
built-ins/Array/prototype/push/S15.4.4.7_A3.js # The value of x[4294967295] is expected to be "x" Expected SameValue(«undefined», «"x"») to be true
|
||||
built-ins/Array/prototype/reduce/15.4.4.21-10-8.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/reduce/15.4.4.21-9-7.js # .hasOwnProperty is not a function.
|
||||
built-ins/Array/prototype/reduceRight/15.4.4.22-10-8.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/reduceRight/15.4.4.22-9-7.js # .hasOwnProperty is not a function.
|
||||
built-ins/Array/prototype/slice/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all
|
||||
built-ins/Array/prototype/some/15.4.4.17-8-13.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
|
||||
built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T3.js # #1: Check ToString operator
|
||||
built-ins/Array/prototype/sort/S15.4.4.11_A2.2_T3.js # #1: Check ToString operator
|
||||
built-ins/Array/prototype/sort/bug_596_1.js # #1: [object, object].sort(); counter < 2. Actual: 0
|
||||
built-ins/Array/prototype/sort/bug_596_2.js # .hasOwnProperty is not a function.
|
||||
built-ins/Array/prototype/splice/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all
|
||||
built-ins/Array/prototype/toLocaleString/S15.4.4.3_A1_T1.js # .toLocaleString is not a function.
|
||||
built-ins/Array/prototype/toString/S15.4.4.2_A1_T1.js # .toString is not a function.
|
||||
built-ins/Array/prototype/toString/S15.4.4.2_A1_T2.js # .toString is not a function.
|
||||
built-ins/Array/prototype/toString/S15.4.4.2_A1_T3.js # .toString is not a function.
|
||||
built-ins/Array/prototype/toString/S15.4.4.2_A1_T4.js # .toString is not a function.
|
||||
built-ins/Array/prototype/values/iteration-mutable.js # .next is not a function.
|
||||
built-ins/Array/prototype/values/iteration.js # .next is not a function.
|
||||
language/statements/async-function/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran
|
||||
language/statements/async-generator/dflt-params-abrupt.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dflt-params-ref-later.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dflt-params-ref-self.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/async-generator/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran
|
||||
language/statements/async-generator/return-undefined-implicit-and-explicit.js # Actual ["tick 1", "tick 2", "g1 ret", "g2 ret", "g3 ret", "g4 ret"] and expected ["tick 1", "g1 ret"
|
||||
language/statements/const/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/const/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/const/dstr/obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/const/dstr/obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/const/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/const/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-init-in.js # Failed to parse TypeScript: '…' expected.
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-put-const.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-put-unresolvable-strict.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-decl-dstr-obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean.
|
||||
language/statements/for-await-of/async-func-decl-dstr-obj-empty-num.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-await-of/async-func-decl-dstr-obj-empty-string.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-await-of/async-func-decl-dstr-obj-rest-number.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-await-of/async-func-decl-dstr-obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-init-in.js # Failed to parse TypeScript: '…' expected.
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-rtrn-close-null.js # "Promise incorrectly fulfilled."
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-const.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-unresolvable-strict.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-thrw-close-skip.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-decl-dstr-obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean.
|
||||
language/statements/for-await-of/async-gen-decl-dstr-obj-empty-num.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-await-of/async-gen-decl-dstr-obj-empty-string.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-await-of/async-gen-decl-dstr-obj-rest-number.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-await-of/async-gen-decl-dstr-obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true
|
||||
language/statements/for-in/12.6.4-1.js # .hasOwnProperty is not a function.
|
||||
language/statements/for-in/head-lhs-member.js # Unsupported for...in binding.
|
||||
language/statements/for-of/Array.prototype.Symbol.iterator.js # The called value is not a function.
|
||||
language/statements/for-of/dstr/array-elem-init-in.js # Failed to parse TypeScript: '…' expected.
|
||||
language/statements/for-of/dstr/array-elem-iter-rtrn-close-err.js # Iterator next must be a function.
|
||||
language/statements/for-of/dstr/array-elem-iter-rtrn-close-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/for-of/dstr/array-elem-iter-thrw-close-err.js # Expected SameValue(«1», «0») to be true
|
||||
language/statements/for-of/dstr/array-elem-nested-obj-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/array-elem-nested-obj-undefined-hole.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/array-elem-nested-obj-undefined-own.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/array-elem-nested-obj-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-list-rtrn-close-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-list-rtrn-close-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-list-thrw-close-err.js # Expected SameValue(«1», «0») to be true
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-rest-rtrn-close-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-rest-rtrn-close-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/for-of/dstr/array-elem-trlg-iter-rest-thrw-close-err.js # Expected SameValue(«11», «1») to be true
|
||||
language/statements/for-of/dstr/array-rest-iter-rtrn-close-err.js # Iterator next must be a function.
|
||||
language/statements/for-of/dstr/array-rest-iter-rtrn-close-null.js # Iterator next must be a function.
|
||||
language/statements/for-of/dstr/array-rest-iter-thrw-close-err.js # Expected SameValue(«11», «0») to be true
|
||||
language/statements/for-of/dstr/array-rest-lref-err.js # Expected SameValue(«1», «0») to be true
|
||||
language/statements/for-of/dstr/const-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/const-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/const-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/const-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/const-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/const-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/let-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean.
|
||||
language/statements/for-of/dstr/obj-empty-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-empty-num.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-of/dstr/obj-empty-string.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-of/dstr/obj-empty-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-prop-name-evaluation-error.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-prop-nested-obj-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-prop-nested-obj-undefined-own.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-prop-nested-obj-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-rest-number.js # Object destructuring requires a data object or array value, received a number.
|
||||
language/statements/for-of/dstr/obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string.
|
||||
language/statements/for-of/dstr/obj-rest-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/obj-rest-val-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/dstr/var-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for-of/throw-from-catch.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
|
||||
language/statements/for-of/throw-from-finally.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
|
||||
language/statements/for-of/throw.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
|
||||
language/statements/for/dstr/const-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/const-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/const-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/const-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/const-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/const-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/let-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/for/dstr/var-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/function/13.2-19-b-3gs.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/function/13.2-2-s.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/function/S13.2.1_A5_T1.js # .toString is not a function.
|
||||
language/statements/function/S13.2_A7_T1.js # The called value is not a function.
|
||||
language/statements/function/S13_A3_T1.js # Unknown identifier '…'.
|
||||
language/statements/function/S13_A6_T1.js # Identifier '…' has already been declared.
|
||||
language/statements/function/S14_A2.js # Unknown identifier '…'.
|
||||
language/statements/function/S14_A5_T1.js # Identifier '…' has already been declared.
|
||||
language/statements/function/S14_A5_T2.js # Identifier '…' has already been declared.
|
||||
language/statements/function/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/function/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/function/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran
|
||||
language/statements/generators/dflt-params-abrupt.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dflt-params-ref-later.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dflt-params-ref-self.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-init-iter-get-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-ary-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-elision-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-init-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-init-undefined.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-list-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-ary-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-eval-err.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-id-init-throws.js # Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-id-init-unresolvable.js # Expected a ReferenceError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all
|
||||
language/statements/generators/has-instance.js # The right-hand side of '…' must be a supported constructor: Error (or a specific error type like Typ
|
||||
language/statements/generators/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran
|
||||
language/statements/labeled/value-await-non-module-escaped.js # Failed to parse TypeScript: Keywords cannot contain escape characters.
|
||||
language/statements/labeled/value-await-non-module.js # Failed to parse TypeScript: Expression expected.
|
||||
language/statements/let/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/let/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/let/dstr/obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/let/dstr/obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/let/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/let/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/return/S12.9_A1_T1.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T10.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T2.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T3.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T4.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T5.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T6.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T7.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T8.js # expected SyntaxError but the program ran
|
||||
language/statements/return/S12.9_A1_T9.js # expected SyntaxError but the program ran
|
||||
language/statements/switch/S12.11_A1_T4.js # Switch discriminants must be data values.
|
||||
language/statements/switch/scope-lex-open-dflt.js # Switch discriminants must be data values.
|
||||
language/statements/try/S12.14_A19_T1.js # .toString is not a function.
|
||||
language/statements/try/S12.14_A19_T2.js # .toString is not a function.
|
||||
language/statements/try/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/try/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/try/dstr/obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/try/dstr/obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/try/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/try/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/obj-init-null.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/obj-init-undefined.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error
|
||||
language/statements/variable/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error
|
||||
@@ -192,9 +192,10 @@ describe("blocked member names on tool paths", () => {
|
||||
`,
|
||||
),
|
||||
).toEqual([true, true, true, null, true, null, null, null, true, null, null, "undefined"])
|
||||
expect((await failure(runtime, `return (() => 1).constructor`)).message).toContain(
|
||||
"Cannot read properties of a function",
|
||||
)
|
||||
expect(await value(runtime, `return [(() => 1).constructor, typeof (() => 1).__proto__]`)).toEqual([
|
||||
null,
|
||||
"undefined",
|
||||
])
|
||||
const escape = await failure(runtime, `return ({}).constructor.constructor.constructor("return 1")()`)
|
||||
expect(escape.message).toContain("Cannot access a property on a non-object value")
|
||||
const poisoned = await failure(runtime, `const o = {}; o.__proto__.constructor("return 1")`)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user