mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
Merge branch 'main' into codeflash/optimize-checkForValidAPIKey-mkwv868t
This commit is contained in:
commit
d44ca16d27
917 changed files with 196719 additions and 11900 deletions
54
.claude/rules/aiservice.md
Normal file
54
.claude/rules/aiservice.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
paths:
|
||||
- "django/**"
|
||||
---
|
||||
# AIService (Django-Ninja Backend)
|
||||
|
||||
## Important
|
||||
|
||||
NEVER start, restart, or manage the dev server (uvicorn, nohup, background processes). The developer will run services manually.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
uv sync # Install dependencies
|
||||
uv run uvicorn aiservice.asgi:application --reload # Dev server
|
||||
uv run pytest # Run all tests
|
||||
uv run pytest tests/path/test_file.py::test_name -v # Single test
|
||||
uv run mypy . # Type check
|
||||
uv run ty check # Type check (ty)
|
||||
uv run ruff check . # Lint
|
||||
uv run ruff format . # Format
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
- All endpoints are `async def` — runs under ASGI via uvicorn
|
||||
- Django-Ninja with Pydantic schemas. APIs defined in `aiservice/urls.py`
|
||||
- Use Pydantic `BaseModel` or `ninja.Schema` for all request/response types
|
||||
- Use `aiservice/llm.py` for LLM calls — never call provider APIs directly
|
||||
- Prompts stored as `.md` files alongside their modules
|
||||
- Always use `libcst` for code transformation, never `ast` (preserves formatting)
|
||||
- Use `asyncio.TaskGroup` for concurrent operations
|
||||
|
||||
## Multi-Language Handlers
|
||||
|
||||
`core/languages/` has per-language modules registering via `core/registry`. Routers in `core/shared/` dispatch by `language` field. To add a language: create `core/languages/<lang>/`, register in `__init__.py`, implement protocol methods.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- No automatic exception handling — use `get_object_or_404` explicitly
|
||||
- Auth: `HttpBearer` before `django_auth` to avoid CSRF errors
|
||||
- Lazy imports in routers to avoid circular dependencies
|
||||
|
||||
## Testing
|
||||
|
||||
- Tests in `tests/` by feature: `optimizer/`, `testgen/`, `integration/`, `validators/`
|
||||
- `@pytest.mark.asyncio` for async tests
|
||||
- Factories: `create_optimizer_context()`, `create_refiner_context()`
|
||||
|
||||
## Style
|
||||
|
||||
- 120 char line length, Python 3.12+
|
||||
- Minimal comments — explain "why" not "what"
|
||||
- No docstrings unless requested
|
||||
15
.claude/rules/architecture.md
Normal file
15
.claude/rules/architecture.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Service Architecture
|
||||
|
||||
```
|
||||
VSC-Extension / CLI → cf-api (Express, :3001) → aiservice (Django-Ninja, :8000)
|
||||
cf-webapp (:3000) reads from the same PostgreSQL DB via Prisma
|
||||
```
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Optimization Candidate** — LLM-generated code that may be faster
|
||||
- **Read-Write Context** — code the LLM can modify
|
||||
- **Read-Only Context** — code provided as info only (not modified)
|
||||
- **Tracer** — collects input args for a Python function at runtime
|
||||
- **Replay Test** — reruns traced inputs to verify behavior
|
||||
- **Comparator** — compares two Python objects for equality
|
||||
13
.claude/rules/cf-webapp.md
Normal file
13
.claude/rules/cf-webapp.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
paths:
|
||||
- "js/cf-webapp/**"
|
||||
---
|
||||
# Next.js Patterns (cf-webapp)
|
||||
|
||||
- Default to server components. `"use client"` only for interactivity/state/browser APIs. Push client boundaries as low in the tree as possible
|
||||
- Server actions go in files with `"use server"` at the top — never define inside client components
|
||||
- Props passed from server → client must be JSON-serializable (no functions or class instances)
|
||||
- Prisma queries go in server components. Client components fetch via API or receive data as props
|
||||
- Path alias: `@/*` → `./src/*`
|
||||
- Tree-sitter WASM files in `public/` built by `postinstall` — don't delete
|
||||
- `@codeflash-ai/common` must be in `transpilePackages` (top-level await)
|
||||
4
.claude/rules/git.md
Normal file
4
.claude/rules/git.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Git
|
||||
|
||||
- Always create a new branch from `main` — never commit directly to `main`
|
||||
- Conventional commits: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
|
||||
27
.claude/rules/js-packages.md
Normal file
27
.claude/rules/js-packages.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
paths:
|
||||
- "js/**"
|
||||
---
|
||||
# JS/TS Packages
|
||||
|
||||
NEVER start, restart, or manage dev servers (npm run dev, node, nohup, background processes). The developer will run services manually.
|
||||
|
||||
All use ESLint + Prettier. Run commands from each package directory.
|
||||
|
||||
## Prisma
|
||||
|
||||
Schema lives in `common/prisma/schema.prisma`, shared by cf-api and cf-webapp. `common` is CommonJS — use `require`-style imports when working with it directly. Published as `@codeflash-ai/common` to GitHub Packages.
|
||||
|
||||
## Package Gotchas
|
||||
|
||||
### cf-api
|
||||
- Webhook routes MUST be registered before body parser (raw body needed for signature verification)
|
||||
- `instrument.ts` must be imported first in entry point (Sentry init)
|
||||
- Tests use dependency injection: `setXxxDependencies()` / `resetXxxDependencies()`
|
||||
|
||||
### VSC-Extension
|
||||
- **Different prettier config**: 80 width + semicolons (vs 100/no-semi elsewhere)
|
||||
- npm workspaces for local `@codeflash/*` packages
|
||||
- Sidebar is a separate Vite/React app embedded via webview postMessage
|
||||
- `acquireVsCodeApi()` called once per session — store and reuse
|
||||
- esbuild excludes `vscode` module (provided by runtime)
|
||||
22
.claude/rules/pr-review.md
Normal file
22
.claude/rules/pr-review.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# PR Review Guidelines
|
||||
|
||||
When reviewing PRs, follow these priorities:
|
||||
|
||||
## CRITICAL - Always comment:
|
||||
- Logic errors or bugs that will cause failures
|
||||
- Security vulnerabilities
|
||||
- Test method names with typos (won't be discovered by test runner)
|
||||
- Breaking changes without migration path
|
||||
|
||||
## SKIP - Don't comment on:
|
||||
- Code style or formatting (we have linters for this)
|
||||
- Suggestions for additional features or refactoring
|
||||
- "Consider" or "might want to" suggestions
|
||||
- Performance optimizations without profiling data
|
||||
- Duplicate concerns (one comment per issue)
|
||||
|
||||
## Process:
|
||||
1. Check if previous comments on same lines are now fixed - resolve those first
|
||||
2. Limit to 5-7 high-signal comments per review
|
||||
3. Group related issues into one comment when possible
|
||||
4. If many issues exist, use a summary comment instead of 20+ inline comments
|
||||
11
.claude/skills/fix-prek.md
Normal file
11
.claude/skills/fix-prek.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Fix Formatting & Linting (prek)
|
||||
|
||||
When asked to fix formatting, linting, or pre-commit issues (e.g., "fix prek", "fix formatting", "run pre-k"):
|
||||
|
||||
1. Run `uv run prek run --all-files` to auto-fix formatting issues
|
||||
2. If prek modifies files, stage them with `git add .`
|
||||
3. Commit with message: `fix: auto-format with prek`
|
||||
4. Push the changes with `git push`
|
||||
5. Comment on the PR confirming what was fixed
|
||||
|
||||
Do NOT just review the code - actually run prek and push the fixes!
|
||||
1
.claude/skills/tessl-add-api-endpoint
Normal file
1
.claude/skills/tessl-add-api-endpoint
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-api-endpoint
|
||||
1
.claude/skills/tessl-add-language-support
Normal file
1
.claude/skills/tessl-add-language-support
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-language-support
|
||||
1
.claude/skills/tessl-debug-optimization-failure
Normal file
1
.claude/skills/tessl-debug-optimization-failure
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-optimization-failure
|
||||
1
.claude/skills/tessl-debug-test-generation
Normal file
1
.claude/skills/tessl-debug-test-generation
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-test-generation
|
||||
4
.codex/config.toml
Normal file
4
.codex/config.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[mcp_servers.tessl]
|
||||
type = "stdio"
|
||||
command = "tessl"
|
||||
args = [ "mcp", "start" ]
|
||||
1
.codex/skills/tessl-add-api-endpoint
Normal file
1
.codex/skills/tessl-add-api-endpoint
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-api-endpoint
|
||||
1
.codex/skills/tessl-add-language-support
Normal file
1
.codex/skills/tessl-add-language-support
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-language-support
|
||||
1
.codex/skills/tessl-debug-optimization-failure
Normal file
1
.codex/skills/tessl-debug-optimization-failure
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-optimization-failure
|
||||
1
.codex/skills/tessl-debug-test-generation
Normal file
1
.codex/skills/tessl-debug-test-generation
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-test-generation
|
||||
12
.gemini/settings.json
Normal file
12
.gemini/settings.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"tessl": {
|
||||
"type": "stdio",
|
||||
"command": "tessl",
|
||||
"args": [
|
||||
"mcp",
|
||||
"start"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
1
.gemini/skills/tessl-add-api-endpoint
Normal file
1
.gemini/skills/tessl-add-api-endpoint
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-api-endpoint
|
||||
1
.gemini/skills/tessl-add-language-support
Normal file
1
.gemini/skills/tessl-add-language-support
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/add-language-support
|
||||
1
.gemini/skills/tessl-debug-optimization-failure
Normal file
1
.gemini/skills/tessl-debug-optimization-failure
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-optimization-failure
|
||||
1
.gemini/skills/tessl-debug-test-generation
Normal file
1
.gemini/skills/tessl-debug-test-generation
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../.tessl/tiles/codeflash/codeflash-internal-skills/skills/debug-test-generation
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -3,3 +3,5 @@
|
|||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
|
||||
.github/workflows/*.lock.yml linguist-generated=true merge=ours
|
||||
153
.github/workflows/claude.yml
vendored
153
.github/workflows/claude.yml
vendored
|
|
@ -13,33 +13,84 @@ on:
|
|||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
# Automatic PR review (read-only)
|
||||
# Automatic PR review (can fix linting issues and push)
|
||||
pr-review:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: django/aiservice
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv venv --seed
|
||||
uv sync
|
||||
|
||||
- name: Configure AWS Credentials (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_foundry: "true"
|
||||
use_bedrock: "true"
|
||||
use_sticky_comment: true
|
||||
prompt: |
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||
EVENT: ${{ github.event.action }}
|
||||
|
||||
IMPORTANT: This repo has Python code in `django/aiservice/`. Run uv/prek commands from that directory.
|
||||
|
||||
## STEP 1: Run prek and mypy checks, fix issues
|
||||
|
||||
First, run these checks on files changed in this PR:
|
||||
1. `cd django/aiservice && uv run prek run --from-ref origin/main` - linting/formatting issues
|
||||
2. `cd django/aiservice && uv run mypy <changed_files>` - type checking issues
|
||||
|
||||
If there are prek issues:
|
||||
- For SAFE auto-fixable issues (formatting, import sorting, trailing whitespace, etc.), run `cd django/aiservice && uv run prek run --from-ref origin/main` again to auto-fix them
|
||||
- For issues that prek cannot auto-fix, do NOT attempt to fix them manually — report them as remaining issues in your summary
|
||||
|
||||
If there are mypy issues:
|
||||
- Fix type annotation issues (missing return types, Optional/None unions, import errors for type hints, incorrect types)
|
||||
- Do NOT add `type: ignore` comments - always fix the root cause
|
||||
|
||||
After fixing issues:
|
||||
- Stage the fixed files with `git add`
|
||||
- Commit with message "style: auto-fix linting issues" or "fix: resolve mypy type errors" as appropriate
|
||||
- Push the changes with `git push`
|
||||
|
||||
IMPORTANT - Verification after fixing:
|
||||
- After committing fixes, run `cd django/aiservice && uv run prek run --from-ref origin/main` ONE MORE TIME to verify all issues are resolved
|
||||
- If errors remain, either fix them or report them honestly as unfixed in your summary
|
||||
- NEVER claim issues are fixed without verifying. If you cannot fix an issue, say so
|
||||
|
||||
Do NOT attempt to fix:
|
||||
- Type errors that require logic changes or refactoring
|
||||
- Complex generic type issues
|
||||
- Anything that could change runtime behavior
|
||||
|
||||
## STEP 2: Review the PR
|
||||
|
||||
${{ github.event.action == 'synchronize' && 'This is a RE-REVIEW after new commits. First, get the list of changed files in this latest push using `gh pr diff`. Review ONLY the changed files. Check ALL existing review comments and resolve ones that are now fixed.' || 'This is the INITIAL REVIEW.' }}
|
||||
|
||||
Review this PR focusing ONLY on:
|
||||
|
|
@ -54,14 +105,75 @@ jobs:
|
|||
- Only create NEW inline comments for HIGH-PRIORITY issues found in changed files.
|
||||
- Limit to 5-7 NEW comments maximum per review.
|
||||
- Use CLAUDE.md for project-specific guidance.
|
||||
- Use `gh pr comment` for summary-level feedback.
|
||||
- Use `mcp__github_inline_comment__create_inline_comment` sparingly for critical code issues only.
|
||||
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*),Bash(gh issue list:*),Read,Glob,Grep"'
|
||||
|
||||
## STEP 3: Coverage analysis
|
||||
|
||||
Analyze test coverage for changed files:
|
||||
|
||||
1. Get the list of Python files changed in this PR (excluding tests):
|
||||
`git diff --name-only origin/main...HEAD -- '*.py' | grep -v test`
|
||||
|
||||
2. Run tests with coverage on the PR branch (from django/aiservice):
|
||||
`cd django/aiservice && uv run coverage run -m pytest -q --tb=no`
|
||||
`cd django/aiservice && uv run coverage json -o coverage-pr.json`
|
||||
|
||||
3. Get coverage for changed files only:
|
||||
`cd django/aiservice && uv run coverage report --include="<changed_files_comma_separated>"`
|
||||
|
||||
4. Compare with main branch coverage:
|
||||
- Checkout main: `git checkout origin/main`
|
||||
- Run coverage: `cd django/aiservice && uv run coverage run -m pytest -q --tb=no && uv run coverage json -o coverage-main.json`
|
||||
- Checkout back: `git checkout -`
|
||||
|
||||
5. Analyze the diff to identify:
|
||||
- NEW FILES: Files that don't exist on main (require good test coverage)
|
||||
- MODIFIED FILES: Files with changes (changes must be covered by tests)
|
||||
|
||||
6. Report in PR comment with a markdown table:
|
||||
- Coverage % for each changed file (PR vs main)
|
||||
- Overall coverage change
|
||||
- For NEW files: Flag if coverage is below 75%
|
||||
- For MODIFIED files: Flag if the changed lines are not covered by tests
|
||||
- Flag if overall coverage decreased
|
||||
|
||||
Coverage requirements:
|
||||
- New implementations/files: Must have ≥75% test coverage
|
||||
- Modified code: Changed lines should be exercised by existing or new tests
|
||||
- No coverage regressions: Overall coverage should not decrease
|
||||
|
||||
## STEP 4: Post ONE consolidated summary comment
|
||||
|
||||
CRITICAL: You must post exactly ONE summary comment containing ALL results (pre-commit, review, coverage).
|
||||
DO NOT post multiple separate comments. Use this format:
|
||||
|
||||
```
|
||||
## PR Review Summary
|
||||
|
||||
### Prek Checks
|
||||
[status and any fixes made]
|
||||
|
||||
### Code Review
|
||||
[critical issues found, if any]
|
||||
|
||||
### Test Coverage
|
||||
[coverage table and analysis]
|
||||
|
||||
---
|
||||
*Last updated: <timestamp>*
|
||||
```
|
||||
|
||||
To ensure only ONE comment exists:
|
||||
1. Find existing claude[bot] comment: `gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --jq '.[] | select(.user.login == "claude[bot]") | .id' | head -1`
|
||||
2. If found, UPDATE it: `gh api --method PATCH repos/${{ github.repository }}/issues/comments/<ID> -f body="<content>"`
|
||||
3. If not found, CREATE: `gh pr comment ${{ github.event.pull_request.number }} --body "<content>"`
|
||||
4. Delete any OTHER claude[bot] comments to clean up duplicates: `gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --jq '.[] | select(.user.login == "claude[bot]") | .id' | tail -n +2 | xargs -I {} gh api --method DELETE repos/${{ github.repository }}/issues/comments/{}`
|
||||
claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"'
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
env:
|
||||
ANTHROPIC_FOUNDRY_API_KEY: ${{ secrets.ANTHROPIC_FOUNDRY_API_KEY }}
|
||||
ANTHROPIC_FOUNDRY_BASE_URL: ${{ secrets.ANTHROPIC_FOUNDRY_BASE_URL }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
DJANGO_SETTINGS_MODULE: aiservice.settings
|
||||
|
||||
# @claude mentions (can edit and push)
|
||||
claude-mention:
|
||||
|
|
@ -77,9 +189,13 @@ jobs:
|
|||
issues: read
|
||||
id-token: write
|
||||
actions: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: django/aiservice
|
||||
steps:
|
||||
- name: Get PR head ref
|
||||
id: pr-ref
|
||||
working-directory: .
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
|
|
@ -97,14 +213,25 @@ jobs:
|
|||
fetch-depth: 0
|
||||
ref: ${{ steps.pr-ref.outputs.ref }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv venv --seed
|
||||
uv sync
|
||||
|
||||
- name: Configure AWS Credentials (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_foundry: "true"
|
||||
claude_args: '--allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(uv run prek *),Bash(prek *),Bash(gh pr comment*),Bash(gh pr view*)"'
|
||||
use_bedrock: "true"
|
||||
claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"'
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
env:
|
||||
ANTHROPIC_FOUNDRY_API_KEY: ${{ secrets.ANTHROPIC_FOUNDRY_API_KEY }}
|
||||
ANTHROPIC_FOUNDRY_BASE_URL: ${{ secrets.ANTHROPIC_FOUNDRY_BASE_URL }}
|
||||
|
|
|
|||
15
.github/workflows/codeflash-aiservice.yaml
vendored
15
.github/workflows/codeflash-aiservice.yaml
vendored
|
|
@ -49,21 +49,8 @@ jobs:
|
|||
- name: Skip optimization
|
||||
run: echo "Skipping codeflash optimization - no changes in django/aiservice/"
|
||||
|
||||
wait-for-prek:
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.should-run == 'true'
|
||||
name: Wait for prek checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
check-name: prek
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
|
||||
optimize:
|
||||
needs: [check-changes, wait-for-prek]
|
||||
needs: [check-changes]
|
||||
if: needs.check-changes.outputs.should-run == 'true' && github.actor != 'codeflash-ai[bot]'
|
||||
name: Optimize new code in this PR
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
160
.github/workflows/codeflash-js.yaml
vendored
Normal file
160
.github/workflows/codeflash-js.yaml
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
name: Codeflash JS/TS Optimization
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "js/cf-api/**"
|
||||
- "js/cf-webapp/**"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
project:
|
||||
description: 'Project to optimize (cf-api, cf-webapp, or both)'
|
||||
required: false
|
||||
default: 'both'
|
||||
type: choice
|
||||
options:
|
||||
- both
|
||||
- cf-api
|
||||
- cf-webapp
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
checks: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Check which projects have changes
|
||||
check-changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cf-api: ${{ steps.filter.outputs.cf-api }}
|
||||
cf-webapp: ${{ steps.filter.outputs.cf-webapp }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
cf-api:
|
||||
- 'js/cf-api/**'
|
||||
cf-webapp:
|
||||
- 'js/cf-webapp/**'
|
||||
|
||||
# Skip job when no JS changes detected (only for pull_request events)
|
||||
no-js-changes:
|
||||
name: No JS/TS changes detected
|
||||
needs: check-changes
|
||||
if: |
|
||||
github.event_name == 'pull_request'
|
||||
&& needs.check-changes.outputs.cf-api != 'true'
|
||||
&& needs.check-changes.outputs.cf-webapp != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Skip optimization
|
||||
run: echo "Skipping codeflash optimization - no changes in js/cf-api/ or js/cf-webapp/"
|
||||
|
||||
# Optimize cf-api (Jest)
|
||||
optimize-cf-api:
|
||||
needs: [check-changes]
|
||||
if: |
|
||||
(
|
||||
needs.check-changes.outputs.cf-api == 'true'
|
||||
|| github.event_name == 'workflow_dispatch' && (github.event.inputs.project == 'cf-api' || github.event.inputs.project == 'both')
|
||||
)
|
||||
&& github.actor != 'codeflash-ai[bot]'
|
||||
name: Optimize cf-api
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
|
||||
CODEFLASH_PR_NUMBER: ${{ github.event.number }}
|
||||
COLUMNS: 110
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: js/cf-api/package-lock.json
|
||||
|
||||
- name: Install cf-api dependencies
|
||||
working-directory: js/cf-api
|
||||
run: npm ci
|
||||
|
||||
- name: Install jest-junit for test reporting
|
||||
working-directory: js/cf-api
|
||||
run: npm install --save-dev jest-junit
|
||||
|
||||
- name: Set up Python and install Codeflash
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install Codeflash CLI
|
||||
run: |
|
||||
uv tool install git+https://github.com/codeflash-ai/codeflash@main
|
||||
|
||||
- name: Run Codeflash optimization
|
||||
working-directory: js/cf-api
|
||||
run: |
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
codeflash --all --verbose --yes
|
||||
|
||||
# Optimize cf-webapp (Vitest)
|
||||
optimize-cf-webapp:
|
||||
needs: [check-changes]
|
||||
if: |
|
||||
(
|
||||
needs.check-changes.outputs.cf-webapp == 'true'
|
||||
|| github.event_name == 'workflow_dispatch' && (github.event.inputs.project == 'cf-webapp' || github.event.inputs.project == 'both')
|
||||
)
|
||||
&& github.actor != 'codeflash-ai[bot]'
|
||||
name: Optimize cf-webapp
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
|
||||
CODEFLASH_PR_NUMBER: ${{ github.event.number }}
|
||||
COLUMNS: 110
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: js/cf-webapp/package-lock.json
|
||||
|
||||
- name: Install cf-webapp dependencies
|
||||
working-directory: js/cf-webapp
|
||||
run: npm ci
|
||||
|
||||
- name: Set up Python and install Codeflash
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install Codeflash CLI
|
||||
run: |
|
||||
uv tool install git+https://github.com/codeflash-ai/codeflash@main
|
||||
|
||||
- name: Run Codeflash optimization
|
||||
working-directory: js/cf-webapp
|
||||
run: |
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
codeflash --all --verbose --yes
|
||||
22
.github/workflows/django-unit-tests.yaml
vendored
22
.github/workflows/django-unit-tests.yaml
vendored
|
|
@ -46,25 +46,9 @@ jobs:
|
|||
- name: Skip tests
|
||||
run: echo "Skipping django unit tests - no changes in django/aiservice/"
|
||||
|
||||
wait-for-prek:
|
||||
name: Wait for prek checks
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.should-run == 'true' && github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
check-name: prek
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
|
||||
unit-tests:
|
||||
needs: [check-changes, wait-for-prek]
|
||||
if: |
|
||||
always() &&
|
||||
needs.check-changes.outputs.should-run == 'true' &&
|
||||
(needs.wait-for-prek.result == 'success' || needs.wait-for-prek.result == 'skipped')
|
||||
needs: [check-changes]
|
||||
if: needs.check-changes.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
|
|
@ -96,7 +80,7 @@ jobs:
|
|||
|
||||
django-unit-tests-status:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-changes, no-aiservice-changes, wait-for-prek, unit-tests]
|
||||
needs: [check-changes, no-aiservice-changes, unit-tests]
|
||||
if: always()
|
||||
defaults:
|
||||
run:
|
||||
|
|
|
|||
119
.github/workflows/duplicate-code-detector.yml
vendored
Normal file
119
.github/workflows/duplicate-code-detector.yml
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
name: Duplicate Code Detector
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
detect-duplicates:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.ref || github.ref }}
|
||||
|
||||
- name: Start Serena MCP server
|
||||
run: |
|
||||
docker pull ghcr.io/github/serena-mcp-server:latest
|
||||
docker run -d --name serena \
|
||||
--network host \
|
||||
-v "${{ github.workspace }}:${{ github.workspace }}:rw" \
|
||||
ghcr.io/github/serena-mcp-server:latest \
|
||||
serena start-mcp-server --context codex --project "${{ github.workspace }}"
|
||||
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
{
|
||||
"mcpServers": {
|
||||
"serena": {
|
||||
"command": "docker",
|
||||
"args": ["exec", "-i", "serena", "serena", "start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Configure AWS Credentials (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Run Claude Code
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_bedrock: "true"
|
||||
use_sticky_comment: true
|
||||
allowed_bots: "claude[bot],codeflash-ai[bot]"
|
||||
claude_args: '--model us.anthropic.claude-sonnet-4-6 --mcp-config /tmp/mcp-config/mcp-servers.json --allowedTools "Read,Glob,Grep,Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(wc *),Bash(find *),mcp__serena__*"'
|
||||
prompt: |
|
||||
You are a duplicate code detector with access to Serena semantic code analysis.
|
||||
|
||||
## Setup
|
||||
|
||||
First activate the project in Serena:
|
||||
- Use `mcp__serena__activate_project` with the workspace path `${{ github.workspace }}`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Get the list of changed source files (excluding tests):
|
||||
`git diff --name-only origin/main...HEAD -- '*.py' '*.ts' '*.tsx' '*.js' '*.jsx' | grep -v -E '(test_|_test\.|\.test\.|\.spec\.|/tests/|/test/|/__tests__/)'`
|
||||
|
||||
2. Use Serena's semantic analysis on changed files:
|
||||
- `mcp__serena__get_symbols_overview` to understand file structure
|
||||
- `mcp__serena__find_symbol` to search for similarly named symbols across the codebase
|
||||
- `mcp__serena__find_referencing_symbols` to understand usage patterns
|
||||
- `mcp__serena__search_for_pattern` to find similar code patterns
|
||||
|
||||
3. For each changed file, look for:
|
||||
- **Exact Duplication**: Identical code blocks (>10 lines) in multiple locations
|
||||
- **Structural Duplication**: Same logic with minor variations (different variable names)
|
||||
- **Functional Duplication**: Different implementations of the same functionality
|
||||
- **Copy-Paste Programming**: Similar blocks that could be extracted into shared utilities
|
||||
|
||||
4. Cross-reference against the rest of the codebase using Serena:
|
||||
- Search for similar function signatures and logic patterns
|
||||
- Check if new code duplicates existing utilities or helpers
|
||||
- Look for repeated patterns across modules
|
||||
- Check across service boundaries (django/aiservice, js/cf-api, js/cf-webapp, js/common)
|
||||
|
||||
## What to Report
|
||||
|
||||
- Identical or nearly identical functions in different files
|
||||
- Repeated code blocks that could be extracted to utilities
|
||||
- Similar classes or modules with overlapping functionality
|
||||
- Copy-pasted code with minor modifications
|
||||
- Duplicated business logic across components or services
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Standard boilerplate (imports, __init__, exports, etc.)
|
||||
- Test setup/teardown code
|
||||
- Configuration with similar structure
|
||||
- Language-specific patterns (constructors, getters/setters)
|
||||
- Small snippets (<5 lines) unless highly repetitive
|
||||
- Workflow files under .github/
|
||||
- Prisma schema and migration files
|
||||
- node_modules, .venv, build artifacts
|
||||
|
||||
## Output
|
||||
|
||||
Post a single PR comment with your findings. For each pattern found:
|
||||
- Severity (High/Medium/Low)
|
||||
- File locations with line numbers
|
||||
- Code samples showing the duplication
|
||||
- Concrete refactoring suggestion
|
||||
|
||||
If no significant duplication is found, say so briefly. Do not create issues — just comment on the PR.
|
||||
- name: Stop Serena
|
||||
if: always()
|
||||
run: docker stop serena && docker rm serena || true
|
||||
22
.github/workflows/end-to-end-tests.yaml
vendored
22
.github/workflows/end-to-end-tests.yaml
vendored
|
|
@ -50,26 +50,9 @@ jobs:
|
|||
- name: Mark as success
|
||||
run: exit 0
|
||||
|
||||
prek-check:
|
||||
name: Wait for prek checks
|
||||
needs: check-changes
|
||||
if: ${{ github.event_name == 'pull_request' && needs.check-changes.outputs.should_run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
checks: read
|
||||
steps:
|
||||
- uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
check-name: prek
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
|
||||
unit-tests-check:
|
||||
name: Wait for unit tests
|
||||
needs: [check-changes, prek-check]
|
||||
needs: [check-changes]
|
||||
if: ${{ github.event_name == 'pull_request' && needs.check-changes.outputs.aiservice_changed == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
|
@ -209,7 +192,7 @@ jobs:
|
|||
e2e-status:
|
||||
name: E2E Tests Status
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-changes, no-changes-detected, prek-check, unit-tests-check, e2e-test]
|
||||
needs: [check-changes, no-changes-detected, unit-tests-check, e2e-test]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check all job statuses
|
||||
|
|
@ -222,7 +205,6 @@ jobs:
|
|||
else
|
||||
echo "✗ End-to-end tests workflow failed"
|
||||
echo "no-changes-detected: ${{ needs.no-changes-detected.result }}"
|
||||
echo "prek-check: ${{ needs.prek-check.result }}"
|
||||
echo "unit-tests-check: ${{ needs.unit-tests-check.result }}"
|
||||
echo "e2e-test: ${{ needs.e2e-test.result }}"
|
||||
exit 1
|
||||
|
|
|
|||
1
.github/workflows/prek.yaml
vendored
1
.github/workflows/prek.yaml
vendored
|
|
@ -4,6 +4,7 @@ on: [pull_request]
|
|||
jobs:
|
||||
prek:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -255,4 +255,8 @@ fabric.properties
|
|||
|
||||
*/node_modules/*
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
||||
/cli/experiments/js-serialization-experiment/node_modules/*
|
||||
/cli/packages/codeflash/.npmrc
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$/django/aiservice" />
|
||||
<option name="settingsModule" value="aiservice/settings.py" />
|
||||
<option name="manageScript" value="manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/cli/tests" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/django/aiservice" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/js/cf-api" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/js/cf-webapp" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/js/common" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/cli" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/django/aiservice/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.aider.ident.cache.v1" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.aider.tags.cache.v1" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.mypy_cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pytest_cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/cli/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.aider.tags.cache.v3" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/js/cf-api/node_modules" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/js/cf-webapp/node_modules" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/js/common/node_modules" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/cli/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="uv (codeflash-internal)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="langchain" />
|
||||
</component>
|
||||
<component name="PackageRequirementsSettings">
|
||||
<option name="requirementsPath" value="" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||
</component>
|
||||
</module>
|
||||
12
.mcp.json
Normal file
12
.mcp.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"tessl": {
|
||||
"type": "stdio",
|
||||
"command": "tessl",
|
||||
"args": [
|
||||
"mcp",
|
||||
"start"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
31
.tessl/RULES.md
Normal file
31
.tessl/RULES.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Agent Rules
|
||||
|
||||
This file is updated when running `tessl install`. If a linked file is missing, make sure to run the command to download any missing tiles from the registry.
|
||||
|
||||
## codeflash/codeflash-internal-rules — code-style
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/code-style.md [code-style](tiles/codeflash/codeflash-internal-rules/rules/code-style.md)
|
||||
|
||||
## codeflash/codeflash-internal-rules — architecture
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/architecture.md [architecture](tiles/codeflash/codeflash-internal-rules/rules/architecture.md)
|
||||
|
||||
## codeflash/codeflash-internal-rules — optimization-patterns
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/optimization-patterns.md [optimization-patterns](tiles/codeflash/codeflash-internal-rules/rules/optimization-patterns.md)
|
||||
|
||||
## codeflash/codeflash-internal-rules — git-conventions
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/git-conventions.md [git-conventions](tiles/codeflash/codeflash-internal-rules/rules/git-conventions.md)
|
||||
|
||||
## codeflash/codeflash-internal-rules — testing-rules
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/testing-rules.md [testing-rules](tiles/codeflash/codeflash-internal-rules/rules/testing-rules.md)
|
||||
|
||||
## codeflash/codeflash-internal-rules — multi-language-handlers
|
||||
|
||||
@tiles/codeflash/codeflash-internal-rules/rules/multi-language-handlers.md [multi-language-handlers](tiles/codeflash/codeflash-internal-rules/rules/multi-language-handlers.md)
|
||||
|
||||
## tessl/cli-setup — query_library_docs
|
||||
|
||||
@tiles/tessl/cli-setup/steering/query_library_docs.md [query_library_docs](tiles/tessl/cli-setup/steering/query_library_docs.md)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# AIService Endpoints
|
||||
|
||||
All Django-Ninja API endpoints registered in `aiservice/urls.py`.
|
||||
|
||||
## Endpoint Map
|
||||
|
||||
| Path | API | Module | Description |
|
||||
|------|-----|--------|-------------|
|
||||
| `/ai/optimize` | `optimize_api` | `core.shared.optimizer_router` | Main optimization — dispatches by language |
|
||||
| `/ai/optimize-line-profiler` | `optimize_line_profiler_api` | `core.languages.python.optimizer.optimizer_line_profiler` | Line profiler optimization |
|
||||
| `/ai/testgen` | `testgen_api` | `core.shared.testgen_router` | Test generation — dispatches by language |
|
||||
| `/ai/log_features` | `features_api` | `core.log_features.log_features` | Feature logging |
|
||||
| `/ai/refinement` | `refinement_api` | `core.languages.python.optimizer.refinement` | Candidate refinement |
|
||||
| `/ai/explain` | `explanations_api` | `core.languages.python.explanations.explanations` | Code explanations |
|
||||
| `/ai/rank` | `ranker_api` | `core.shared.ranker.ranker` | Function ranking |
|
||||
| `/ai/optimization_review` | `optimization_review_api` | `core.languages.python.optimization_review.optimization_review` | Optimization review |
|
||||
| `/ai/code_repair` | `code_repair_api` | `core.languages.python.code_repair.code_repair` | Code repair for failed candidates |
|
||||
| `/ai/adaptive_optimize` | `adaptive_optimize_api` | `core.languages.python.adaptive_optimizer.adaptive_optimizer` | Adaptive optimization |
|
||||
| `/ai/workflow-gen` | `workflow_gen_api` | `core.shared.workflow_gen.workflow_gen` | Workflow generation |
|
||||
| `/ai/rewrite_jit` | `jit_rewrite_api` | `core.languages.python.jit_rewrite.jit_rewrite` | JIT rewrite |
|
||||
|
||||
## Common Patterns
|
||||
|
||||
- All endpoints are `async def`
|
||||
- Authentication via `AuthenticatedRequest` (HttpBearer + django_auth)
|
||||
- Request schemas use `ninja.Schema` (Pydantic under the hood)
|
||||
- Response schemas define typed success and error responses: `response={200: SuccessSchema, 400: ErrorSchema, 500: ErrorSchema}`
|
||||
- Multi-language endpoints (optimize, testgen) dispatch by `data.language` field
|
||||
- Python-only endpoints import directly from `core.languages.python.*`
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# CF-API Endpoints
|
||||
|
||||
Express routes in `js/cf-api/routes/`. The cf-api acts as middleware between clients (VSC-Extension, CLI) and the aiservice backend.
|
||||
|
||||
## Route Registration Order (`routes/index.ts`)
|
||||
|
||||
Registration order matters — webhook routes must be before the body parser:
|
||||
|
||||
1. **Webhook routes** — before `express.json()` (raw body for signature verification)
|
||||
2. **Body parser** — `express.json({ limit: JSON_BODY_LIMIT })`
|
||||
3. **Public routes** — no authentication required
|
||||
4. **Protected routes** — require API key (`checkForValidAPIKey` middleware)
|
||||
|
||||
## Route Files
|
||||
|
||||
### `webhook.routes.ts`
|
||||
|
||||
- `POST /github/webhooks` — GitHub App webhook handler (Octokit signature verification)
|
||||
- `POST /stripe/webhooks` — Stripe webhook handler
|
||||
- Both need raw body access (before JSON parser)
|
||||
|
||||
### `optimization.routes.ts`
|
||||
|
||||
Protected optimization endpoints:
|
||||
- `POST /suggest-pr-changes` — suggest PR changes
|
||||
- `POST /create-pr` — create optimization PR
|
||||
- `POST /verify-existing-optimizations` — check existing optimizations
|
||||
- `POST /is-already-optimized` — check if code was already optimized
|
||||
- `POST /add-code-hash` — add optimized code context hash
|
||||
- `POST /mark-as-success` — mark optimization as successful
|
||||
- `POST /create-staging` — create staging review
|
||||
- `POST /get-staging-code` — get staged code
|
||||
- `POST /commit-staging-code` — commit staged code
|
||||
- `POST /test-repo` — add repository manually
|
||||
|
||||
### `github.routes.ts`
|
||||
|
||||
GitHub-related endpoints for repository management.
|
||||
|
||||
### `subscription.routes.ts`
|
||||
|
||||
Subscription management endpoints.
|
||||
|
||||
### `user.routes.ts`
|
||||
|
||||
User management endpoints.
|
||||
|
||||
### `public.routes.ts`
|
||||
|
||||
Public endpoints (no authentication): health checks, version info.
|
||||
|
||||
## Middleware Stack
|
||||
|
||||
- `checkForValidAPIKey` — API key authentication
|
||||
- `trackEndpointCalls` — PostHog endpoint tracking
|
||||
- `idLimiter` — rate limiting
|
||||
- `logAuthEvent` / `logRequestBody` — enhanced logging (dev only)
|
||||
- `trackUsage` — usage tracking for optimization endpoints
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Configuration & Thresholds
|
||||
|
||||
Key constants and configuration values used across the optimization pipeline.
|
||||
|
||||
## Model Distribution (`core/shared/optimizer_config.py`)
|
||||
|
||||
- `MAX_OPTIMIZER_CALLS = 6` — maximum parallel optimization calls
|
||||
- `MAX_OPTIMIZER_LP_CALLS = 7` — maximum line profiler optimization calls
|
||||
- Distribution formula: `claude_calls = (total - 1) // 2`, `gpt_calls = total - claude_calls`
|
||||
- Example: 6 total → 4 OpenAI + 2 Anthropic
|
||||
|
||||
## Default Request Values
|
||||
|
||||
- `OptimizeSchema.n_candidates = 5` — default optimization candidates
|
||||
- `OptimizeSchemaLP.n_candidates = 6` — default line profiler candidates
|
||||
- `OptimizeSchema.language = "python"` — default language
|
||||
|
||||
## LLM Model Costs (USD per 1M tokens)
|
||||
|
||||
| Model | Input | Cached Input | Output |
|
||||
|-------|-------|-------------|--------|
|
||||
| GPT-4.1 | $2.00 | $0.50 | $8.00 |
|
||||
| GPT-5-mini | $0.25 | $0.03 | $2.00 |
|
||||
| Claude Sonnet 4.5 | $3.00 | — | $15.00 |
|
||||
| Claude Haiku 4.5 | $1.00 | — | $5.00 |
|
||||
|
||||
## Model Assignments
|
||||
|
||||
| Purpose | Primary | Fallback |
|
||||
|---------|---------|----------|
|
||||
| Optimization | GPT-5-mini | Claude Sonnet 4.5 |
|
||||
| Explanation | GPT-5-mini | Claude Sonnet 4.5 |
|
||||
| Planning | GPT-5-mini | Claude Sonnet 4.5 |
|
||||
| Execution | GPT-5-mini | Claude Sonnet 4.5 |
|
||||
| Ranking | GPT-5-mini | Claude Sonnet 4.5 |
|
||||
| Refinement | Claude Sonnet 4.5 | GPT-5-mini |
|
||||
| Code Repair | Claude Sonnet 4.5 | GPT-5-mini |
|
||||
| Explanations | Claude Sonnet 4.5 | GPT-5-mini |
|
||||
| Optimization Review | Claude Sonnet 4.5 | GPT-5-mini |
|
||||
| Adaptive Optimize | Claude Sonnet 4.5 | GPT-5-mini |
|
||||
| Cost-effective (testgen) | Claude Haiku 4.5 | GPT-5-mini |
|
||||
|
||||
## Ruff Configuration (`pyproject.toml`)
|
||||
|
||||
- `line-length = 120`
|
||||
- `select = ["ALL"]` with specific ignores (see `tool.ruff.lint.ignore`)
|
||||
- `fix = true`, `show-fixes = true`
|
||||
- Runtime-evaluated base classes: `pydantic.BaseModel`, `ninja.Schema`, `typing.TypedDict`
|
||||
- Excluded paths: `core/languages/python/testgen/tests`, `core/languages/python/testgen/sqlalchemy`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Context Extraction
|
||||
|
||||
How code context is extracted and prepared for LLM optimization prompts.
|
||||
|
||||
## Context Types
|
||||
|
||||
### Single-File Context (`SingleOptimizerContext`)
|
||||
|
||||
Used when the function to optimize lives in a single file:
|
||||
- Extracts the function source code
|
||||
- Collects helper functions and class definitions
|
||||
- Formats as system prompt + user prompt
|
||||
|
||||
### Multi-File Context (`MultiOptimizerContext`)
|
||||
|
||||
Used when the function spans or depends on multiple files:
|
||||
- Collects code from multiple source files
|
||||
- Manages file-path-annotated code blocks
|
||||
|
||||
## `BaseOptimizerContext` (`optimizer_context.py`)
|
||||
|
||||
Abstract base class for all context types:
|
||||
|
||||
### Factory Method
|
||||
|
||||
`get_dynamic_context()` — dispatches to `SingleOptimizerContext` or `MultiOptimizerContext` based on the input.
|
||||
|
||||
### Prompt Construction
|
||||
|
||||
- `get_system_prompt(python_version_str)` — builds system prompt with language version
|
||||
- `get_user_prompt(dependency_code, line_profiler_results)` — builds user prompt with code and optional profiler data
|
||||
|
||||
### LLM Response Parsing
|
||||
|
||||
- `extract_code_and_explanation_from_llm_res(content)` — parses markdown code blocks from LLM output, extracts code and explanation text
|
||||
- `parse_and_generate_candidate_schema()` — converts extracted code into `OptimizeResponseItemSchema`
|
||||
- `is_valid_code()` — validates the extracted code is syntactically correct
|
||||
|
||||
## Code Formatting
|
||||
|
||||
LLM responses use markdown code blocks with file path annotations:
|
||||
|
||||
```
|
||||
\`\`\`python:path/to/file.py
|
||||
# optimized code here
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
The context extraction system both generates this format (for prompts) and parses it (from responses).
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Domain Types
|
||||
|
||||
Core schemas and models used across the codeflash-internal backend.
|
||||
|
||||
## Request/Response Schemas (`core/shared/optimizer_models.py`)
|
||||
|
||||
### `OptimizedCandidateSource` enum
|
||||
|
||||
How a candidate was generated: `OPTIMIZE`, `OPTIMIZE_LP`, `REFINE`, `REPAIR`, `ADAPTIVE`, `JIT_REWRITE`.
|
||||
|
||||
### `OptimizeSchema` (ninja.Schema)
|
||||
|
||||
Main optimization request:
|
||||
- `source_code: str` — code to optimize
|
||||
- `dependency_code: str | None` — read-only dependency code
|
||||
- `trace_id: str` — unique request identifier
|
||||
- `language: str = "python"` — language identifier (python, javascript, typescript)
|
||||
- `language_version: str | None` — e.g., "ES2022", "Node 20", or Python version
|
||||
- `n_candidates: int = 5` — number of optimization candidates to generate
|
||||
- `is_async: bool | None = False` — whether the function is async
|
||||
- `python_version: str | None` — optional, for multi-language support
|
||||
- `experiment_metadata`, `codeflash_version`, `current_username`, `repo_owner`, `repo_name` — tracking fields
|
||||
|
||||
### `OptimizeSchemaLP` (ninja.Schema)
|
||||
|
||||
Line profiler variant — same as `OptimizeSchema` plus:
|
||||
- `line_profiler_results: str | None` — line profiler output
|
||||
- `n_candidates: int = 6` — default higher for line profiler optimization
|
||||
|
||||
## Domain Models (`core/log_features/models.py`)
|
||||
|
||||
### `OptimizationFeatures` (Django Model)
|
||||
|
||||
Stores per-optimization data:
|
||||
- `trace_id` (PK) — unique optimization identifier
|
||||
- `original_code` — original source code
|
||||
- `optimizations_raw` / `optimizations_post` — raw and postprocessed candidate code
|
||||
- `speedup_ratio` — best speedup achieved
|
||||
- `test_results` — test execution results
|
||||
- Approval workflow: `approval_status`, `approved_by`, `approved_at`
|
||||
|
||||
### `OptimizationEvents` (Django Model)
|
||||
|
||||
Event logging for optimization lifecycle:
|
||||
- `pr_created`, `pr_merged`, `pr_closed` — PR events
|
||||
- `llm_cost` — total LLM API cost
|
||||
- `speedup_x` / `speedup_pct` — performance improvement metrics
|
||||
|
||||
### `Repositories` (Django Model)
|
||||
|
||||
GitHub repository metadata:
|
||||
- `installation_id` — GitHub App installation ID
|
||||
- `is_private` — whether the repo is private
|
||||
- `optimizations_limit` / `optimizations_used` — usage tracking
|
||||
|
||||
## Response Schemas (`core/shared/optimizer_schemas.py`)
|
||||
|
||||
- `OptimizeResponseSchema` — successful optimization response with candidates
|
||||
- `OptimizeErrorResponseSchema` — error response with message
|
||||
21
.tessl/tiles/codeflash/codeflash-internal-docs/docs/index.md
Normal file
21
.tessl/tiles/codeflash/codeflash-internal-docs/docs/index.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Codeflash Internal Documentation
|
||||
|
||||
CodeFlash's AI-powered code optimization backend. The aiservice (Django-Ninja) receives optimization requests from cf-api, dispatches to language-specific handlers, calls LLMs, postprocesses results, and returns optimized candidates.
|
||||
|
||||
## Service Flow
|
||||
|
||||
```
|
||||
VSC-Extension / CLI → cf-api (Express, :3001) → aiservice (Django-Ninja, :8000)
|
||||
cf-webapp (:3000) reads from the same PostgreSQL DB via Prisma
|
||||
```
|
||||
|
||||
## Documentation Pages
|
||||
|
||||
- [Domain Types](domain-types.md) — Core schemas and domain models
|
||||
- [Optimization Pipeline](optimization-pipeline.md) — End-to-end optimization flow
|
||||
- [Test Generation Pipeline](test-generation-pipeline.md) — Test generation flow
|
||||
- [Context Extraction](context-extraction.md) — How code context is extracted for LLM prompts
|
||||
- [AIService Endpoints](aiservice-endpoints.md) — All Django-Ninja endpoints
|
||||
- [CF-API Endpoints](cf-api-endpoints.md) — All Express routes
|
||||
- [Configuration & Thresholds](configuration-thresholds.md) — Model distribution, costs, constants
|
||||
- [LLM Provider Abstraction](llm-provider-abstraction.md) — llm.py usage, client creation, cost tracking
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
# LLM Provider Abstraction
|
||||
|
||||
Unified LLM interface in `aiservice/llm.py` — all LLM calls go through this module.
|
||||
|
||||
## Model Definition (`LLM` dataclass)
|
||||
|
||||
```python
|
||||
@pydantic_dataclass
|
||||
class LLM:
|
||||
name: str # deployment name (e.g., "gpt-5-mini")
|
||||
max_tokens: int # max context window
|
||||
model_type: Literal["openai", "anthropic", "google"]
|
||||
input_cost: float # USD per 1M tokens
|
||||
cached_input_cost: float # USD per 1M cached tokens
|
||||
output_cost: float # USD per 1M tokens
|
||||
```
|
||||
|
||||
Concrete models: `OpenAI_GPT_4_1`, `OpenAI_GPT_5_Mini`, `Anthropic_Claude_Sonnet_4_5`, `Anthropic_Claude_Haiku_4_5`.
|
||||
|
||||
## Client Setup
|
||||
|
||||
- `_create_openai_client()` — returns `AsyncAzureOpenAI` (reads `AZURE_OPENAI_*` env vars)
|
||||
- `_create_anthropic_client()` — returns `AsyncAnthropicFoundry` (reads `ANTHROPIC_FOUNDRY_API_KEY` + `ANTHROPIC_FOUNDRY_BASE_URL`)
|
||||
- `get_llm_client(model_type)` — creates a fresh client per request to avoid event loop issues with Django dev server
|
||||
|
||||
## Calling LLMs
|
||||
|
||||
```python
|
||||
from aiservice.llm import call_llm, LLM
|
||||
|
||||
response: LLMResponse = await call_llm(
|
||||
llm=model, # LLM instance
|
||||
messages=messages, # OpenAI-format messages
|
||||
call_type="optimization", # tracking label
|
||||
trace_id=trace_id, # request identifier
|
||||
max_tokens=16384, # max output tokens
|
||||
user_id=user_id, # optional tracking
|
||||
)
|
||||
# response.content: str
|
||||
# response.usage: LLMUsage(input_tokens, output_tokens)
|
||||
# response.raw_response: ChatCompletion | AnthropicMessage
|
||||
```
|
||||
|
||||
### Provider Handling
|
||||
|
||||
- **OpenAI (Azure)**: Uses `client.chat.completions.create()`. GPT-5-mini uses `max_completion_tokens`, older models use `max_tokens`
|
||||
- **Anthropic (Foundry)**: Extracts system prompt from messages list, passes separately via `system=` kwarg. Concatenates text blocks from response
|
||||
|
||||
### Observability
|
||||
|
||||
- Every call is recorded to database via `record_llm_call()` in the `finally` block
|
||||
- Includes: trace_id, call_type, model, messages, result, error, cost, latency
|
||||
|
||||
## Cost Calculation
|
||||
|
||||
`calculate_llm_cost(response, llm)` accounts for cached vs non-cached input tokens:
|
||||
- **Anthropic**: `cache_read_input_tokens` and `cache_creation_input_tokens` are additive to `input_tokens`
|
||||
- **OpenAI**: `cached_tokens` is a subset of `prompt_tokens`
|
||||
|
||||
## Response Types
|
||||
|
||||
- `LLMResponse` — wraps `content: str`, `usage: LLMUsage`, `raw_response`
|
||||
- `LLMUsage` — `input_tokens: int`, `output_tokens: int`
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# Optimization Pipeline
|
||||
|
||||
End-to-end flow from optimization request to response.
|
||||
|
||||
## 1. Request Entry (`core/shared/optimizer_router.py`)
|
||||
|
||||
The `optimize_api` NinjaAPI router receives a POST request with `OptimizeSchema`. It dispatches by `data.language`:
|
||||
- `"javascript"` / `"typescript"` → `core.languages.js_ts.optimizer.optimize_javascript`
|
||||
- `"java"` → `core.languages.java.optimizer.optimize_java`
|
||||
- Default → `core.languages.python.optimizer.optimizer.optimize_python`
|
||||
|
||||
All imports are lazy (inside the function body) to avoid circular dependencies.
|
||||
|
||||
## 2. Python Optimization (`core/languages/python/optimizer/optimizer.py`)
|
||||
|
||||
### `optimize_python(request, data) → (status, response)`
|
||||
|
||||
Entry point for Python optimization. Extracts user ID from request, builds context, and calls `optimize_python_code()`.
|
||||
|
||||
### `optimize_python_code_single()`
|
||||
|
||||
Single LLM optimization call:
|
||||
1. Builds system + user prompts from `BaseOptimizerContext`
|
||||
2. Calls `call_llm()` with the optimize model
|
||||
3. Parses response via `ctx.extract_code_and_explanation_from_llm_res()`
|
||||
4. Validates via `ctx.parse_and_generate_candidate_schema()`
|
||||
5. Returns `(OptimizeResponseItemSchema, llm_cost, model_name)` or `(None, cost, model)`
|
||||
|
||||
### `optimize_python_code()`
|
||||
|
||||
Parallel optimization with multiple models:
|
||||
1. Gets model distribution via `get_model_distribution(n_candidates, MAX_OPTIMIZER_CALLS)`
|
||||
2. Runs parallel calls using `asyncio.TaskGroup`
|
||||
3. Each call gets a `call_sequence` number for tracking
|
||||
4. Collects results, costs, and model info from all parallel calls
|
||||
|
||||
## 3. Context Extraction (`optimizer_context.py`)
|
||||
|
||||
- `BaseOptimizerContext.get_dynamic_context()` — factory dispatching to `SingleOptimizerContext` or `MultiOptimizerContext`
|
||||
- Handles prompt construction with `get_system_prompt()` / `get_user_prompt()`
|
||||
- `extract_code_and_explanation_from_llm_res()` — parses markdown code blocks from LLM output
|
||||
- `parse_and_generate_candidate_schema()` — converts extracted code to response schema
|
||||
|
||||
## 4. Postprocessing (`postprocess.py`)
|
||||
|
||||
- `deduplicate_optimizations()` — AST-based dedup using `ast.parse()` + `ast.dump()`
|
||||
- `equality_check()` — filters out candidates identical to original code
|
||||
- Uses `libcst` for all code transformations
|
||||
|
||||
## 5. Model Distribution (`optimizer_config.py`)
|
||||
|
||||
- `MAX_OPTIMIZER_CALLS = 6`, `MAX_OPTIMIZER_LP_CALLS = 7`
|
||||
- `get_model_distribution()` splits between OpenAI and Anthropic
|
||||
- Formula: `claude_calls = (total - 1) // 2`, `gpt_calls = total - claude_calls`
|
||||
- Returns `[(OPENAI_MODEL, gpt_calls), (ANTHROPIC_MODEL, claude_calls)]`
|
||||
|
||||
## 6. Response
|
||||
|
||||
Returns `(200, OptimizeResponseSchema)` with list of `OptimizeResponseItemSchema` candidates, or `(400/500, OptimizeErrorResponseSchema)` on failure.
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `core/shared/optimizer_router.py` | Language dispatch |
|
||||
| `core/languages/python/optimizer/optimizer.py` | Python optimization flow |
|
||||
| `core/languages/python/optimizer/context_utils/optimizer_context.py` | Context & prompt management |
|
||||
| `core/languages/python/optimizer/postprocess.py` | Dedup & validation |
|
||||
| `core/shared/optimizer_config.py` | Model distribution |
|
||||
| `core/shared/optimizer_models.py` | Request/response schemas |
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Test Generation Pipeline
|
||||
|
||||
Flow from test generation request to instrumented test output.
|
||||
|
||||
## 1. Request Entry (`core/shared/testgen_router.py`)
|
||||
|
||||
The `testgen_api` NinjaAPI router receives a POST request and dispatches by `data.language`:
|
||||
- `"javascript"` / `"typescript"` → `core.languages.js_ts.testgen.generate_tests_javascript`
|
||||
- `"java"` → `core.languages.java.testgen.generate_tests_java`
|
||||
- Default → `core.languages.python.testgen.testgen.generate_tests_python`
|
||||
|
||||
## 2. Python Testgen (`core/languages/python/testgen/testgen.py`)
|
||||
|
||||
### `build_prompt()`
|
||||
|
||||
Jinja2-based prompt builder for test generation:
|
||||
- Constructs system and user prompts from context
|
||||
- Handles async/sync function variants
|
||||
- Includes source code, dependency code, and test context
|
||||
|
||||
### `instrument_tests()`
|
||||
|
||||
Applies instrumentation to generated tests:
|
||||
- Behavior instrumentation (captures return values, stdout)
|
||||
- Performance instrumentation (timing loops)
|
||||
|
||||
### `LLMOutputParseError`
|
||||
|
||||
Custom exception for LLM response parsing failures — includes raw output for debugging.
|
||||
|
||||
## 3. Instrumentation (`testgen/instrumentation/instrument_new_tests.py`)
|
||||
|
||||
### Framework Detection
|
||||
|
||||
- `detect_frameworks_from_code()` — parses imports to identify ML frameworks (PyTorch, TensorFlow, JAX) and their aliases
|
||||
- Used to add GPU sync calls in timing blocks
|
||||
|
||||
### Device Sync
|
||||
|
||||
- `_create_device_sync_precompute_statements()` — pre-computes device sync checks (CUDA, MPS) outside timing blocks
|
||||
- Ensures accurate timing for GPU-accelerated code
|
||||
|
||||
## 4. Postprocessing (`testgen/postprocessing/`)
|
||||
|
||||
- Import management: `add_missing_imports.py` adds `from __future__ import annotations`
|
||||
- Test validation and cleanup
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `core/shared/testgen_router.py` | Language dispatch |
|
||||
| `core/languages/python/testgen/testgen.py` | Testgen flow |
|
||||
| `core/languages/python/testgen/instrumentation/instrument_new_tests.py` | Test instrumentation |
|
||||
| `core/languages/python/testgen/postprocessing/` | Import management, cleanup |
|
||||
7
.tessl/tiles/codeflash/codeflash-internal-docs/tile.json
Normal file
7
.tessl/tiles/codeflash/codeflash-internal-docs/tile.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "codeflash/codeflash-internal-docs",
|
||||
"version": "0.1.0",
|
||||
"summary": "Internal documentation for the codeflash-internal aiservice backend",
|
||||
"private": true,
|
||||
"docs": "docs/index.md"
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Architecture
|
||||
|
||||
## Service Flow
|
||||
|
||||
```
|
||||
VSC-Extension / CLI → cf-api (Express, :3001) → aiservice (Django-Ninja, :8000)
|
||||
cf-webapp (:3000) reads from the same PostgreSQL DB via Prisma
|
||||
```
|
||||
|
||||
## Monorepo Layout
|
||||
|
||||
```
|
||||
codeflash-internal/
|
||||
├── django/aiservice/ # Python backend (Django-Ninja, ASGI)
|
||||
│ ├── aiservice/ # Django project: settings, urls.py, llm.py
|
||||
│ ├── authapp/ # Authentication (HttpBearer + django_auth)
|
||||
│ ├── core/ # Business logic
|
||||
│ │ ├── shared/ # Cross-language routers (optimizer_router, testgen_router, ranker)
|
||||
│ │ ├── languages/ # Per-language handlers (python/, js_ts/, java/)
|
||||
│ │ ├── protocols/ # Handler protocols (LanguageHandler, OptimizerProtocol, etc.)
|
||||
│ │ ├── registry.py # Language handler registration
|
||||
│ │ ├── dispatcher.py # Handler lookup by language + feature
|
||||
│ │ └── log_features/ # Domain models (OptimizationFeatures, OptimizationEvents)
|
||||
│ └── tests/ # pytest tests by feature: optimizer/, testgen/, integration/
|
||||
├── js/
|
||||
│ ├── cf-api/ # Express middleware layer (:3001)
|
||||
│ ├── cf-webapp/ # Next.js dashboard (:3000)
|
||||
│ ├── common/ # Shared Prisma schema + utilities (CommonJS)
|
||||
│ └── VSC-Extension/ # VS Code extension
|
||||
├── cli/ # CLI tools
|
||||
└── deployment/ # Infrastructure configs
|
||||
```
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Optimization Candidate** — LLM-generated code that may be faster
|
||||
- **Read-Write Context** — code the LLM can modify
|
||||
- **Read-Only Context** — code provided as info only (not modified)
|
||||
- **Tracer** — collects input args for a Python function at runtime
|
||||
- **Replay Test** — reruns traced inputs to verify behavior
|
||||
- **Comparator** — compares two Python objects for equality
|
||||
|
||||
## Key Entry Points
|
||||
|
||||
| Task | Start here |
|
||||
|------|------------|
|
||||
| Optimization dispatch | `core/shared/optimizer_router.py` |
|
||||
| Testgen dispatch | `core/shared/testgen_router.py` |
|
||||
| Python optimization | `core/languages/python/optimizer/optimizer.py` |
|
||||
| LLM provider abstraction | `aiservice/llm.py` |
|
||||
| Endpoint registration | `aiservice/urls.py` |
|
||||
| Domain models | `core/log_features/models.py` |
|
||||
| Request/response schemas | `core/shared/optimizer_models.py` |
|
||||
| Handler registration | `core/registry.py` + `core/dispatcher.py` |
|
||||
| cf-api route registration | `js/cf-api/routes/index.ts` |
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Code Style
|
||||
|
||||
## Python (aiservice)
|
||||
|
||||
- **Python 3.12+** — use modern syntax (type unions `X | Y`, `match` statements)
|
||||
- **Line length**: 120 characters
|
||||
- **Tooling**: Ruff for linting/formatting (`ruff check .`, `ruff format .`), mypy strict mode, ty for type checking, prek for pre-commit (`uv run prek run --all-files`)
|
||||
- **Package management**: Always use `uv` — run commands via `uv run`
|
||||
- **Comments**: Minimal — only explain "why", not "what"
|
||||
- **Docstrings**: Do not add unless explicitly requested
|
||||
- **Source transforms**: Use `libcst` for code modification/transformation (preserves formatting); `ast` is acceptable for read-only analysis
|
||||
- **Async**: All endpoints are `async def` — runs under ASGI via uvicorn. Use `asyncio.TaskGroup` for concurrent operations
|
||||
- **Schemas**: Use Pydantic `BaseModel` or `ninja.Schema` for all request/response types
|
||||
- **LLM calls**: Use `aiservice/llm.py` — never call provider APIs directly
|
||||
- **Prompts**: Stored as `.md` files alongside their modules, rendered with Jinja2
|
||||
- **Lazy imports**: Use inside function bodies in routers to avoid circular dependencies (`# noqa: PLC0415`)
|
||||
|
||||
## JavaScript/TypeScript (cf-api, cf-webapp, VSC-Extension)
|
||||
|
||||
- All JS/TS packages use ESLint + Prettier. Run commands from each package directory
|
||||
- **cf-api**: Express app. Webhook routes MUST be registered before body parser (raw body needed for signature verification). `instrument.ts` must be imported first in entry point (Sentry). Tests use dependency injection: `setXxxDependencies()` / `resetXxxDependencies()`
|
||||
- **cf-webapp**: Next.js. Default to server components; `"use client"` only for interactivity. Server actions in `"use server"` files. Prisma queries in server components only. Path alias `@/*` → `./src/*`
|
||||
- **VSC-Extension**: Different prettier config (80 width + semicolons vs 100/no-semi elsewhere). npm workspaces for local `@codeflash/*` packages. Sidebar is a separate Vite/React app embedded via webview postMessage
|
||||
- **Prisma**: Schema in `common/prisma/schema.prisma`, shared by cf-api and cf-webapp. `common` is CommonJS — use `require`-style imports
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Git Conventions
|
||||
|
||||
- **Always create a new branch from `main`** — never commit directly to `main`
|
||||
- Use conventional commit format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
|
||||
- Keep commits atomic — one logical change per commit
|
||||
- Commit message body should be concise (1-2 sentences max)
|
||||
- PR titles should also use conventional format
|
||||
- Branch naming: `cf-#-title` (lowercase, hyphenated) where `#` is the Linear issue number
|
||||
- If related to a Linear issue, include `CF-#` in the PR body
|
||||
- Pre-commit: `uv run prek run --all-files` from repo root
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Multi-Language Handlers
|
||||
|
||||
## Registry Pattern
|
||||
|
||||
- `core/registry.py` provides `LanguageRegistry` — a dict mapping `language_id → handler_class`
|
||||
- Module-level singleton: `registry = LanguageRegistry()`
|
||||
- Decorator registration: `@register_handler("python")` on handler classes
|
||||
- Duplicate registration raises `ValueError`
|
||||
|
||||
## Dispatcher
|
||||
|
||||
- `core/dispatcher.py` provides `get_handler_for_language()` and `get_handler_for_feature()`
|
||||
- `Feature` enum maps feature names to capability flags: `TESTGEN`, `OPTIMIZER`, `CODE_REPAIR`, `JIT_REWRITE`, `OPTIMIZATION_REVIEW`, `EXPLANATIONS`
|
||||
- `get_handler_for_feature()` checks `supports_*` attribute before instantiation, raises `HandlerNotImplementedError` if unsupported
|
||||
|
||||
## Protocols
|
||||
|
||||
- `core/protocols/` defines runtime-checkable protocols:
|
||||
- `LanguageHandler` (base): declares `language`, `supports_*` booleans
|
||||
- `OptimizerProtocol`: `optimizer_optimize(request, data)`
|
||||
- `TestGenProtocol`: `testgen_generate(request, data)`
|
||||
- `CodeRepairProtocol`: `code_repair_repair(user_id, optimization_id, ctx)`
|
||||
|
||||
## Adding a New Language
|
||||
|
||||
1. Create `core/languages/<lang>/` directory
|
||||
2. Implement handler class with `@register_handler("<lang>")` decorator
|
||||
3. Set `supports_*` flags for implemented features
|
||||
4. Implement protocol methods for each supported feature
|
||||
5. Import the module in `core/languages/__init__.py` so the decorator fires
|
||||
6. Add language routing in `core/shared/optimizer_router.py` and `core/shared/testgen_router.py`
|
||||
7. Add tests in `tests/` for the new handler
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Optimization Patterns
|
||||
|
||||
## Router Dispatch
|
||||
|
||||
- Shared routers in `core/shared/` dispatch by the `language` field on the request schema
|
||||
- Lazy imports inside the endpoint body to avoid circular dependencies:
|
||||
```python
|
||||
if data.language in ("javascript", "typescript"):
|
||||
from core.languages.js_ts.optimizer import optimize_javascript # noqa: PLC0415
|
||||
return await optimize_javascript(request, data)
|
||||
```
|
||||
- Default language is Python
|
||||
|
||||
## Context Extraction
|
||||
|
||||
- `BaseOptimizerContext` in `optimizer_context.py` handles prompt management and code extraction
|
||||
- Two context types: `SingleOptimizerContext` (single-file) and `MultiOptimizerContext` (multi-file)
|
||||
- `extract_code_and_explanation_from_llm_res()` parses LLM markdown response into code blocks
|
||||
- `parse_and_generate_candidate_schema()` converts extracted code to `OptimizeResponseItemSchema`
|
||||
|
||||
## Model Distribution
|
||||
|
||||
- `get_model_distribution()` in `optimizer_config.py` splits calls between OpenAI and Anthropic
|
||||
- Formula: `claude_calls = (total - 1) // 2`, `gpt_calls = total - claude_calls`
|
||||
- `MAX_OPTIMIZER_CALLS = 6`, `MAX_OPTIMIZER_LP_CALLS = 7`
|
||||
- Claude gets fewer calls as it's more expensive
|
||||
|
||||
## Postprocessing
|
||||
|
||||
- `postprocess.py` handles deduplication and validation of optimization candidates
|
||||
- Deduplication: normalize with `ast.parse()` + `ast.dump()`, skip duplicates with identical AST
|
||||
- Equality check: compare optimized code to original to skip no-ops
|
||||
- Uses `libcst` for all code transformations (preserves formatting)
|
||||
|
||||
## Prompt Templates
|
||||
|
||||
- Prompts stored as `.md` files alongside their modules
|
||||
- Rendered with Jinja2 (e.g., `build_prompt()` in testgen)
|
||||
- System and user prompts are constructed per-context type
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Testing Rules
|
||||
|
||||
## Python (aiservice)
|
||||
|
||||
- Tests in `tests/` by feature: `optimizer/`, `testgen/`, `testgen_postprocessing/`, `testgen_instrumentation/`, `integration/`, `validators/`
|
||||
- `@pytest.mark.asyncio` for async tests
|
||||
- `pytest.ini` sets `DJANGO_SETTINGS_MODULE = aiservice.settings`
|
||||
- `conftest.py` provides `normalize_code()` helper (AST parse/unparse for quote normalization)
|
||||
- Test factories: `create_optimizer_context()`, `create_refiner_context()`
|
||||
- Run tests: `uv run pytest` (all), `uv run pytest tests/path/test_file.py::test_name -v` (single)
|
||||
|
||||
## JS/TS (cf-api)
|
||||
|
||||
- Tests use dependency injection: `setXxxDependencies()` / `resetXxxDependencies()`
|
||||
|
||||
## PR Review
|
||||
|
||||
- **Always comment on**: Logic errors, security vulnerabilities, test name typos, breaking changes without migration
|
||||
- **Never comment on**: Code style/formatting (linters handle it), "consider" suggestions, performance without profiling data
|
||||
- Limit to 5-7 high-signal comments per review
|
||||
26
.tessl/tiles/codeflash/codeflash-internal-rules/tile.json
Normal file
26
.tessl/tiles/codeflash/codeflash-internal-rules/tile.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "codeflash/codeflash-internal-rules",
|
||||
"version": "0.1.0",
|
||||
"summary": "Coding standards and conventions for the codeflash-internal monorepo",
|
||||
"private": true,
|
||||
"rules": {
|
||||
"code-style": {
|
||||
"rules": "rules/code-style.md"
|
||||
},
|
||||
"architecture": {
|
||||
"rules": "rules/architecture.md"
|
||||
},
|
||||
"optimization-patterns": {
|
||||
"rules": "rules/optimization-patterns.md"
|
||||
},
|
||||
"git-conventions": {
|
||||
"rules": "rules/git-conventions.md"
|
||||
},
|
||||
"testing-rules": {
|
||||
"rules": "rules/testing-rules.md"
|
||||
},
|
||||
"multi-language-handlers": {
|
||||
"rules": "rules/multi-language-handlers.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
---
|
||||
name: add-api-endpoint
|
||||
description: >
|
||||
Guide for adding a new API endpoint to the codeflash-internal system.
|
||||
Use when adding a new endpoint, creating a route, or implementing a new API.
|
||||
Covers both aiservice (Django-Ninja) and cf-api (Express) endpoints
|
||||
including schemas, routers, authentication, URL registration, and tests.
|
||||
---
|
||||
|
||||
# Add API Endpoint
|
||||
|
||||
Use this workflow when adding a new endpoint to either the aiservice backend or the cf-api middleware. Follow the section that matches your target service.
|
||||
|
||||
## Part A: AIService Endpoint (Django-Ninja)
|
||||
|
||||
### Step 1: Define Schemas
|
||||
|
||||
Create request and response schemas.
|
||||
|
||||
1. Create or update schemas in the appropriate module (e.g., `core/shared/` for shared, `core/languages/python/` for Python-specific)
|
||||
2. Use `ninja.Schema` for all request/response types:
|
||||
```python
|
||||
from ninja import Schema
|
||||
|
||||
class MyRequestSchema(Schema):
|
||||
source_code: str
|
||||
trace_id: str
|
||||
language: str = "python"
|
||||
|
||||
class MyResponseSchema(Schema):
|
||||
results: list[str]
|
||||
|
||||
class MyErrorResponseSchema(Schema):
|
||||
message: str
|
||||
```
|
||||
3. Follow existing patterns in `core/shared/optimizer_models.py`
|
||||
|
||||
**Checkpoint**: Schemas should use Pydantic validation. Test with `MyRequestSchema.model_validate(data)`.
|
||||
|
||||
### Step 2: Create the Router
|
||||
|
||||
Create a NinjaAPI router for the endpoint.
|
||||
|
||||
1. Create a new module (e.g., `core/shared/my_feature.py` or `core/languages/python/my_feature/my_feature.py`)
|
||||
2. Define the router and endpoint:
|
||||
```python
|
||||
from ninja import NinjaAPI
|
||||
from authapp.auth import AuthenticatedRequest
|
||||
|
||||
my_feature_api = NinjaAPI(urls_namespace="my_feature")
|
||||
|
||||
@my_feature_api.post(
|
||||
"/", response={200: MyResponseSchema, 400: MyErrorResponseSchema, 500: MyErrorResponseSchema}
|
||||
)
|
||||
async def my_endpoint(
|
||||
request: AuthenticatedRequest, data: MyRequestSchema
|
||||
) -> tuple[int, MyResponseSchema | MyErrorResponseSchema]:
|
||||
# Implementation here
|
||||
return 200, MyResponseSchema(results=[])
|
||||
```
|
||||
3. All endpoints must be `async def`
|
||||
4. Use `AuthenticatedRequest` for authenticated endpoints
|
||||
5. For multi-language endpoints, add dispatch by `data.language` with lazy imports
|
||||
|
||||
**Checkpoint**: The endpoint should handle success and error cases with proper status codes.
|
||||
|
||||
### Step 3: Register in urls.py
|
||||
|
||||
Add the endpoint to `aiservice/urls.py`.
|
||||
|
||||
1. Import the API object:
|
||||
```python
|
||||
from core.shared.my_feature import my_feature_api
|
||||
```
|
||||
2. Add to `urlpatterns`:
|
||||
```python
|
||||
path("ai/my-feature", my_feature_api.urls),
|
||||
```
|
||||
3. Follow the naming convention: `ai/<kebab-case-name>`
|
||||
|
||||
**Checkpoint**: The endpoint should be accessible at `/ai/my-feature/`.
|
||||
|
||||
### Step 4: Add Tests
|
||||
|
||||
Write tests for the endpoint.
|
||||
|
||||
1. Create test file in `tests/` matching the source structure
|
||||
2. Test the handler function directly (not via HTTP):
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_my_endpoint():
|
||||
# Mock request and data
|
||||
result = await my_endpoint(mock_request, mock_data)
|
||||
assert result[0] == 200
|
||||
```
|
||||
3. Run: `uv run pytest tests/path/test_file.py -v`
|
||||
|
||||
**Checkpoint**: Tests pass. Run `uv run prek run --all-files`.
|
||||
|
||||
## Part B: CF-API Endpoint (Express)
|
||||
|
||||
### Step 1: Create Endpoint Handler
|
||||
|
||||
Create the endpoint handler in `js/cf-api/endpoints/`.
|
||||
|
||||
1. Create `js/cf-api/endpoints/my-feature.ts`:
|
||||
```typescript
|
||||
import { Request, Response } from "express"
|
||||
|
||||
export async function myFeature(req: Request, res: Response) {
|
||||
// Implementation
|
||||
res.status(200).json({ results: [] })
|
||||
}
|
||||
```
|
||||
2. Follow existing patterns in `endpoints/`
|
||||
|
||||
**Checkpoint**: Handler should return appropriate status codes and JSON responses.
|
||||
|
||||
### Step 2: Create Route File or Add to Existing
|
||||
|
||||
Add the route to the appropriate route file.
|
||||
|
||||
1. If it's a new domain, create `js/cf-api/routes/my-feature.routes.ts`:
|
||||
```typescript
|
||||
import { Router } from "express"
|
||||
import { addAsync } from "@awaitjs/express"
|
||||
import { myFeature } from "../endpoints/my-feature.js"
|
||||
import { ROUTES } from "../constants/index.js"
|
||||
|
||||
const router = addAsync(Router()) as any
|
||||
router.postAsync(ROUTES.MY_FEATURE, myFeature)
|
||||
export default router
|
||||
```
|
||||
2. If it belongs to an existing domain, add to the corresponding route file
|
||||
3. Add the route constant to `constants/index.ts`
|
||||
|
||||
**Checkpoint**: Route file exports a router with the endpoint registered.
|
||||
|
||||
### Step 3: Register in Route Index
|
||||
|
||||
Register the route in `js/cf-api/routes/index.ts`.
|
||||
|
||||
1. Import the route module
|
||||
2. Register in the correct section:
|
||||
- Before body parser: webhook routes only
|
||||
- Public routes: no auth required
|
||||
- Protected routes: after `checkForValidAPIKey` middleware
|
||||
3. Apply middleware as needed (`trackUsage`, `idLimiter`, etc.)
|
||||
|
||||
**Checkpoint**: The endpoint should be accessible with proper authentication.
|
||||
|
||||
### Step 4: Add Tests
|
||||
|
||||
Write tests using the dependency injection pattern.
|
||||
|
||||
1. Create test file in `js/cf-api/__tests__/`
|
||||
2. Use `setXxxDependencies()` / `resetXxxDependencies()` for mocking
|
||||
3. Follow existing test patterns
|
||||
|
||||
**Checkpoint**: Tests pass with `npm test`.
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | What to modify |
|
||||
|------|---------------|
|
||||
| `core/shared/optimizer_models.py` | Schema patterns |
|
||||
| `aiservice/urls.py` | AIService endpoint registration |
|
||||
| `js/cf-api/routes/index.ts` | CF-API route registration |
|
||||
| `js/cf-api/constants/index.ts` | Route constants |
|
||||
| `authapp/auth.py` | Authentication patterns |
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
---
|
||||
name: add-language-support
|
||||
description: >
|
||||
Guide for adding a new programming language to the multi-language system.
|
||||
Use when extending the aiservice to support a new language (e.g., Rust, Go).
|
||||
Covers directory creation, handler implementation, registry registration,
|
||||
protocol compliance, prompt templates, router updates, and tests.
|
||||
---
|
||||
|
||||
# Add Language Support
|
||||
|
||||
Use this workflow when adding a new programming language to the codeflash-internal optimization system. Follow steps in order — each builds on the previous.
|
||||
|
||||
## Step 1: Create Language Directory
|
||||
|
||||
Create the directory structure under `core/languages/`.
|
||||
|
||||
1. Create `core/languages/<lang>/` with `__init__.py`
|
||||
2. Create subdirectories for each feature:
|
||||
```
|
||||
core/languages/<lang>/
|
||||
├── __init__.py # Handler class + @register_handler decorator
|
||||
├── optimizer/ # Optimization handler
|
||||
│ ├── __init__.py
|
||||
│ └── optimizer.py
|
||||
└── testgen/ # Test generation handler (if supported)
|
||||
├── __init__.py
|
||||
└── testgen.py
|
||||
```
|
||||
3. Follow existing patterns from `core/languages/python/` or `core/languages/js_ts/`
|
||||
|
||||
**Checkpoint**: The directory structure should match existing language modules. Verify with `ls core/languages/`.
|
||||
|
||||
## Step 2: Implement Handler Class
|
||||
|
||||
Create the handler class in `core/languages/<lang>/__init__.py`.
|
||||
|
||||
1. Read `core/protocols/base.py` for the `LanguageHandler` protocol
|
||||
2. Implement the handler:
|
||||
```python
|
||||
from core.registry import register_handler
|
||||
|
||||
@register_handler("<lang>")
|
||||
class <Lang>Handler:
|
||||
language = "<lang>"
|
||||
supports_testgen = False # Set True if implementing testgen
|
||||
supports_optimizer = True # Set True if implementing optimizer
|
||||
supports_code_repair = False
|
||||
supports_jit_rewrite = False
|
||||
supports_optimization_review = False
|
||||
supports_explanations = False
|
||||
```
|
||||
3. Set `supports_*` flags for each feature you implement
|
||||
4. The `@register_handler` decorator registers the class with the global registry
|
||||
|
||||
**Checkpoint**: After this step, `registry.list_available()` should include your language ID.
|
||||
|
||||
## Step 3: Register the Module
|
||||
|
||||
Ensure the handler module is imported so the decorator fires.
|
||||
|
||||
1. Read `core/languages/__init__.py` — check how existing languages are imported
|
||||
2. Add your language import so `@register_handler` fires on startup
|
||||
3. Import should be at module level or via lazy import pattern
|
||||
|
||||
**Checkpoint**: Run `python -c "from core.languages import <lang>"` to verify no import errors.
|
||||
|
||||
## Step 4: Implement Protocol Methods
|
||||
|
||||
Implement the protocol methods for each supported feature.
|
||||
|
||||
1. Read `core/protocols/optimizer.py` — `OptimizerProtocol` requires `optimizer_optimize(request, data)`
|
||||
2. Read `core/protocols/testgen.py` — `TestGenProtocol` requires `testgen_generate(request, data)`
|
||||
3. Read `core/protocols/code_repair.py` — `CodeRepairProtocol` requires `code_repair_repair(user_id, optimization_id, ctx)`
|
||||
4. Follow the pattern from Python/JS handlers:
|
||||
```python
|
||||
async def optimizer_optimize(self, request, data):
|
||||
# 1. Build context from data.source_code
|
||||
# 2. Call call_llm() with language-specific prompts
|
||||
# 3. Parse response
|
||||
# 4. Return (status_code, response_schema)
|
||||
```
|
||||
|
||||
**Checkpoint**: Each method must be `async def` and return the expected response type.
|
||||
|
||||
## Step 5: Create Prompt Templates
|
||||
|
||||
Create language-specific prompt templates.
|
||||
|
||||
1. Create `.md` files alongside the handler modules (e.g., `optimizer/system_prompt.md`)
|
||||
2. Use Jinja2 templating for dynamic content
|
||||
3. Prompts should include language-specific context (version, conventions, stdlib)
|
||||
4. Follow the pattern from `core/languages/python/optimizer/context_utils/`
|
||||
|
||||
**Checkpoint**: Prompts should produce valid, non-empty system and user messages.
|
||||
|
||||
## Step 6: Update Routers
|
||||
|
||||
Add language dispatch to the shared routers.
|
||||
|
||||
1. Edit `core/shared/optimizer_router.py` — add a branch for your language:
|
||||
```python
|
||||
if data.language == "<lang>":
|
||||
from core.languages.<lang>.optimizer import optimize_<lang> # noqa: PLC0415
|
||||
return await optimize_<lang>(request, data)
|
||||
```
|
||||
2. Edit `core/shared/testgen_router.py` — add the same pattern if testgen is supported
|
||||
3. Use lazy imports (inside the function body) to avoid circular dependencies
|
||||
|
||||
**Checkpoint**: Both routers should dispatch correctly for the new language.
|
||||
|
||||
## Step 7: Add Tests
|
||||
|
||||
Write tests for the new language handler.
|
||||
|
||||
1. Create `tests/<lang>/` directory mirroring the source structure
|
||||
2. Test handler registration: verify `registry.get_handler("<lang>")` returns the correct class
|
||||
3. Test feature dispatch: verify `get_handler_for_feature("<lang>", "optimizer")` works
|
||||
4. Test optimization flow end-to-end (mock LLM calls)
|
||||
5. Use `@pytest.mark.asyncio` for async tests
|
||||
6. Run: `uv run pytest tests/<lang>/ -v`
|
||||
|
||||
**Checkpoint**: All tests must pass. Run `uv run prek run --all-files` for formatting/lint.
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | What to modify |
|
||||
|------|---------------|
|
||||
| `core/languages/<lang>/` | New handler directory |
|
||||
| `core/registry.py` | No changes needed (decorator handles it) |
|
||||
| `core/dispatcher.py` | No changes needed (dynamic lookup) |
|
||||
| `core/protocols/` | Reference for protocol methods |
|
||||
| `core/shared/optimizer_router.py` | Add language dispatch branch |
|
||||
| `core/shared/testgen_router.py` | Add language dispatch branch (if testgen) |
|
||||
| `core/languages/__init__.py` | Add import for decorator registration |
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
name: debug-optimization-failure
|
||||
description: >
|
||||
Diagnose why an optimization produced no results or failed silently.
|
||||
Use when an optimization request returns errors, empty results, or all
|
||||
candidates are rejected. Walks through request validation, router dispatch,
|
||||
context extraction, LLM calls, postprocessing, and logging stages.
|
||||
---
|
||||
|
||||
# Debug Optimization Failure
|
||||
|
||||
Use this workflow when an optimization request fails or produces no results. Work through the stages sequentially — stop at the first failure found.
|
||||
|
||||
## Step 1: Validate the Request
|
||||
|
||||
Check that the incoming `OptimizeSchema` is well-formed.
|
||||
|
||||
1. Read `core/shared/optimizer_models.py` — verify the request matches `OptimizeSchema` fields
|
||||
2. Check required fields: `source_code`, `trace_id` must be non-empty
|
||||
3. Check `language` field — must be `"python"`, `"javascript"`, `"typescript"`, or `"java"`
|
||||
4. Check `n_candidates` — default is 5, must be positive
|
||||
|
||||
**Checkpoint**: If the request schema is invalid, the error comes from Pydantic validation. Check the 400 response for field-level errors.
|
||||
|
||||
## Step 2: Check Router Dispatch
|
||||
|
||||
Verify the correct language handler is invoked.
|
||||
|
||||
1. Read `core/shared/optimizer_router.py` — the `optimize()` endpoint dispatches by `data.language`
|
||||
2. Supported routes:
|
||||
- `"javascript"` / `"typescript"` → `core.languages.js_ts.optimizer.optimize_javascript`
|
||||
- `"java"` → `core.languages.java.optimizer.optimize_java`
|
||||
- Default → `core.languages.python.optimizer.optimizer.optimize_python`
|
||||
3. Check for import errors — lazy imports inside the function body may fail if a language module is missing
|
||||
|
||||
**Checkpoint**: If dispatch fails, you'll see an `ImportError`. Check that the language module exists under `core/languages/`.
|
||||
|
||||
## Step 3: Check Context Extraction
|
||||
|
||||
Verify the optimization context is built correctly.
|
||||
|
||||
1. Read `core/languages/python/optimizer/context_utils/optimizer_context.py`
|
||||
2. `BaseOptimizerContext.get_dynamic_context()` dispatches to Single or Multi context
|
||||
3. Check `get_system_prompt()` and `get_user_prompt()` — they should produce non-empty prompts
|
||||
4. Check `extract_code_and_explanation_from_llm_res()` — this parses markdown code blocks from the LLM response
|
||||
|
||||
**Checkpoint**: If context extraction returns empty prompts, check that `source_code` in the request is valid Python/JS code.
|
||||
|
||||
## Step 4: Check LLM Calls
|
||||
|
||||
Verify the LLM is called correctly and returns valid responses.
|
||||
|
||||
1. Read `aiservice/llm.py` — `call_llm()` is the universal call handler
|
||||
2. Check `get_llm_client(model_type)` returns a valid client (not `None`)
|
||||
3. Environment variables required:
|
||||
- OpenAI: `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`, `OPENAI_API_VERSION`
|
||||
- Anthropic: `ANTHROPIC_FOUNDRY_API_KEY`, `ANTHROPIC_FOUNDRY_BASE_URL`
|
||||
4. Check `optimizer_config.py` — `get_model_distribution()` determines how many calls per model
|
||||
5. Look for exceptions: `"LLM client for model type '...' is not available"`
|
||||
|
||||
**Checkpoint**: If LLM calls fail, check environment variables and API key validity. Network errors will raise exceptions.
|
||||
|
||||
## Step 5: Check Postprocessing
|
||||
|
||||
Verify candidates survive postprocessing.
|
||||
|
||||
1. Read `core/languages/python/optimizer/postprocess.py`
|
||||
2. `deduplicate_optimizations()` — removes candidates with identical AST (via `ast.parse()` + `ast.dump()`)
|
||||
3. `equality_check()` — removes candidates identical to the original code
|
||||
4. Check if ALL candidates were deduplicated or matched the original
|
||||
|
||||
**Checkpoint**: If all candidates are removed by postprocessing, the LLM is generating identical or no-op code. Try increasing `n_candidates` or checking prompt quality.
|
||||
|
||||
## Step 6: Check Response Construction
|
||||
|
||||
Verify the response is properly constructed.
|
||||
|
||||
1. Each successful candidate produces an `OptimizeResponseItemSchema`
|
||||
2. `parse_and_generate_candidate_schema()` converts extracted code to the schema
|
||||
3. `is_valid_code()` validates syntax — `cst.ParserSyntaxError` or `ValidationError` means malformed output
|
||||
4. If parsing fails, the candidate is dropped and a Sentry message is captured
|
||||
|
||||
**Checkpoint**: If candidates parse but the response is empty, check the validation step in the optimizer flow.
|
||||
|
||||
## Step 7: Check Logging
|
||||
|
||||
Verify the optimization was logged for debugging.
|
||||
|
||||
1. Read `core/log_features/models.py` — `OptimizationFeatures` stores per-trace-id data
|
||||
2. Check `optimizations_raw` (before postprocessing) vs `optimizations_post` (after)
|
||||
3. LLM calls are recorded via `record_llm_call()` in the `finally` block of `call_llm()`
|
||||
4. PostHog events track `aiservice-optimize-openai-usage`
|
||||
|
||||
**Checkpoint**: If logging shows raw candidates but no post candidates, postprocessing removed them all.
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | What to check |
|
||||
|------|---------------|
|
||||
| `core/shared/optimizer_router.py` | Language dispatch |
|
||||
| `core/shared/optimizer_models.py` | Request validation |
|
||||
| `core/languages/python/optimizer/optimizer.py` | Optimization flow |
|
||||
| `core/languages/python/optimizer/context_utils/optimizer_context.py` | Context extraction |
|
||||
| `core/languages/python/optimizer/postprocess.py` | Dedup and validation |
|
||||
| `aiservice/llm.py` | LLM calls and client setup |
|
||||
| `core/shared/optimizer_config.py` | Model distribution |
|
||||
| `core/log_features/models.py` | Logging and tracking |
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
name: debug-test-generation
|
||||
description: >
|
||||
Diagnose why test generation failed or produced invalid tests.
|
||||
Use when testgen returns errors, empty results, or produces tests
|
||||
that fail to compile or run. Walks through request validation,
|
||||
router dispatch, context building, prompt construction, LLM calls,
|
||||
postprocessing, instrumentation, and output validation.
|
||||
---
|
||||
|
||||
# Debug Test Generation
|
||||
|
||||
Use this workflow when test generation fails or produces invalid tests. Work through the stages sequentially — stop at the first failure found.
|
||||
|
||||
## Step 1: Validate the Request
|
||||
|
||||
Check that the incoming testgen request is well-formed.
|
||||
|
||||
1. Read the testgen request schema in the relevant testgen module
|
||||
2. Verify required fields: `source_code`, `trace_id` must be non-empty
|
||||
3. Check `language` field — must match a supported language
|
||||
4. Check for valid code — source code should parse without syntax errors
|
||||
|
||||
**Checkpoint**: If the request schema is invalid, the error comes from Pydantic validation. Check the 400 response.
|
||||
|
||||
## Step 2: Check Router Dispatch
|
||||
|
||||
Verify the correct language handler is invoked.
|
||||
|
||||
1. Read `core/shared/testgen_router.py` — the `testgen()` endpoint dispatches by `data.language`
|
||||
2. Supported routes:
|
||||
- `"javascript"` / `"typescript"` → `core.languages.js_ts.testgen.generate_tests_javascript`
|
||||
- `"java"` → `core.languages.java.testgen.generate_tests_java`
|
||||
- Default → `core.languages.python.testgen.testgen.generate_tests_python`
|
||||
3. Check for `ImportError` — lazy imports may fail if a language module is broken
|
||||
|
||||
**Checkpoint**: If dispatch fails, check that the language module exists and imports cleanly.
|
||||
|
||||
## Step 3: Check Context Building
|
||||
|
||||
Verify the testgen context is constructed correctly.
|
||||
|
||||
1. Read `core/languages/python/testgen/testgen.py` — `build_prompt()` constructs the prompts
|
||||
2. Check that source code and dependency code are passed correctly
|
||||
3. Verify the Jinja2 template renders without errors
|
||||
4. Check for async/sync variants — the prompt builder handles both
|
||||
|
||||
**Checkpoint**: If context is empty or malformed, check the input `source_code` and `dependency_code`.
|
||||
|
||||
## Step 4: Check Prompt Construction
|
||||
|
||||
Verify the LLM prompts are well-formed.
|
||||
|
||||
1. The `build_prompt()` function uses Jinja2 templates (`.md` files alongside the module)
|
||||
2. System prompt sets the role and language context
|
||||
3. User prompt includes the source code and test context
|
||||
4. Check that prompts are non-empty and contain the function to test
|
||||
|
||||
**Checkpoint**: If prompts are empty, check the template files and Jinja2 rendering.
|
||||
|
||||
## Step 5: Check LLM Response
|
||||
|
||||
Verify the LLM returns valid test code.
|
||||
|
||||
1. Read `aiservice/llm.py` — `call_llm()` handles the API call
|
||||
2. Check for network errors or API key issues (same as optimization debugging)
|
||||
3. Look for `LLMOutputParseError` — this means the LLM returned unparseable output
|
||||
4. Check the raw response content — it should contain markdown code blocks with test code
|
||||
|
||||
**Checkpoint**: If the LLM returns malformed output, check the prompt quality and model selection.
|
||||
|
||||
## Step 6: Check Postprocessing
|
||||
|
||||
Verify generated tests survive postprocessing.
|
||||
|
||||
1. Read `core/languages/python/testgen/postprocessing/` directory
|
||||
2. `add_missing_imports.py` — adds `from __future__ import annotations` if needed
|
||||
3. Check for syntax errors in generated test code — `cst.ParserSyntaxError` means malformed code
|
||||
4. Verify imports are resolved correctly
|
||||
|
||||
**Checkpoint**: If postprocessing fails, the LLM generated syntactically invalid code. Check the raw output.
|
||||
|
||||
## Step 7: Check Instrumentation
|
||||
|
||||
Verify tests are properly instrumented.
|
||||
|
||||
1. Read `core/languages/python/testgen/instrumentation/instrument_new_tests.py`
|
||||
2. `instrument_tests()` applies behavior and performance instrumentation
|
||||
3. `detect_frameworks_from_code()` identifies ML frameworks (PyTorch, TensorFlow, JAX)
|
||||
4. `_create_device_sync_precompute_statements()` adds GPU sync calls for timing accuracy
|
||||
5. Check that instrumented tests still compile — instrumentation may introduce syntax errors
|
||||
|
||||
**Checkpoint**: If instrumented tests fail to compile, check the instrumentation transforms. The issue is usually in import handling or device sync injection.
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | What to check |
|
||||
|------|---------------|
|
||||
| `core/shared/testgen_router.py` | Language dispatch |
|
||||
| `core/languages/python/testgen/testgen.py` | Testgen flow, prompt building |
|
||||
| `core/languages/python/testgen/postprocessing/` | Import management, cleanup |
|
||||
| `core/languages/python/testgen/instrumentation/instrument_new_tests.py` | Test instrumentation |
|
||||
| `aiservice/llm.py` | LLM calls and client setup |
|
||||
20
.tessl/tiles/codeflash/codeflash-internal-skills/tile.json
Normal file
20
.tessl/tiles/codeflash/codeflash-internal-skills/tile.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "codeflash/codeflash-internal-skills",
|
||||
"version": "0.1.0",
|
||||
"summary": "Procedural workflows for developing and debugging codeflash-internal",
|
||||
"private": true,
|
||||
"skills": {
|
||||
"debug-optimization-failure": {
|
||||
"path": "skills/debug-optimization-failure/SKILL.md"
|
||||
},
|
||||
"add-language-support": {
|
||||
"path": "skills/add-language-support/SKILL.md"
|
||||
},
|
||||
"add-api-endpoint": {
|
||||
"path": "skills/add-api-endpoint/SKILL.md"
|
||||
},
|
||||
"debug-test-generation": {
|
||||
"path": "skills/debug-test-generation/SKILL.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
28
.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md
Normal file
28
.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
You are a coding agent with access to an MCP tool called `query_library_docs` provided by `tessl`. This tool allows you to query documentation that may be relevant to your task.
|
||||
|
||||
Before you begin working on the user request, you should use the `query_library_docs` tool to search for relevant documentation. This is especially important when:
|
||||
|
||||
- The request mentions specific code files, functions, classes, or services
|
||||
- The request involves debugging errors or investigating issues
|
||||
- The request asks about how something works or why something behaves a certain way
|
||||
- The request involves making changes to existing code
|
||||
- The request references specific frameworks, libraries, or systems
|
||||
|
||||
When in doubt, use the tool. It's better to query documentation even if it might not be relevant than to miss important context.
|
||||
|
||||
How to use the tool:
|
||||
|
||||
1. Extract key terms, file names, service names, error types, or concepts from the user's request
|
||||
2. Call `query_library_docs` with relevant search terms
|
||||
3. Use the returned documentation to inform your approach to the task
|
||||
4. Proceed with completing the user's request using both the documentation context and your own analysis
|
||||
|
||||
Important rules:
|
||||
|
||||
- Do NOT ask for permission to use the tool - just use it
|
||||
- Do NOT explain that you're going to use the tool - just use it
|
||||
- If the tool fails or returns no results, simply continue with the task as you normally would
|
||||
- You may call the tool multiple times with different search terms if needed
|
||||
- Use the tool BEFORE reading files or making changes
|
||||
|
||||
After using the tool (or if the tool returns nothing useful), proceed directly to completing the user's request. Your response should focus on addressing the user's needs, incorporating any relevant documentation you found.
|
||||
11
.tessl/tiles/tessl/cli-setup/tile.json
Normal file
11
.tessl/tiles/tessl/cli-setup/tile.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "tessl/cli-setup",
|
||||
"private": false,
|
||||
"version": "0.70.0",
|
||||
"summary": "Tessl CLI MCP tool usage guidelines",
|
||||
"steering": {
|
||||
"query_library_docs": {
|
||||
"rules": "steering/query_library_docs.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
681
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/batches.md
Normal file
681
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/batches.md
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
# Message Batches
|
||||
|
||||
The Message Batches API allows you to send multiple message requests in a single batch, processing them asynchronously. Batches are cost-efficient for bulk operations and provide significant cost savings (50% off) compared to individual API calls.
|
||||
|
||||
## Overview
|
||||
|
||||
Message Batches enable:
|
||||
- Bulk processing of multiple independent message requests
|
||||
- Asynchronous result retrieval
|
||||
- 50% cost reduction compared to standard API
|
||||
- Processing of up to 10,000 requests per batch
|
||||
- 24-hour processing window with automatic expiration
|
||||
|
||||
## API Reference
|
||||
|
||||
```typescript { .api }
|
||||
class Batches extends APIResource {
|
||||
create(params: MessageBatchCreateParams): APIPromise<MessageBatch>;
|
||||
retrieve(messageBatchID: string): APIPromise<MessageBatch>;
|
||||
list(params?: MessageBatchListParams): MessageBatchesPage;
|
||||
delete(messageBatchID: string): APIPromise<DeletedMessageBatch>;
|
||||
cancel(messageBatchID: string): APIPromise<MessageBatch>;
|
||||
results(messageBatchID: string): AsyncIterable<MessageBatchIndividualResponse>;
|
||||
}
|
||||
```
|
||||
|
||||
Access via:
|
||||
|
||||
```typescript
|
||||
client.messages.batches.* // Standard API
|
||||
client.beta.messages.batches.* // Beta API
|
||||
```
|
||||
|
||||
## Creating a Batch
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.create(
|
||||
params: MessageBatchCreateParams
|
||||
): APIPromise<MessageBatch>;
|
||||
|
||||
interface MessageBatchCreateParams {
|
||||
requests: Array<{
|
||||
custom_id: string;
|
||||
params: MessageCreateParams;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface MessageBatch {
|
||||
id: string;
|
||||
type: 'message_batch';
|
||||
processing_status: 'in_progress' | 'canceling' | 'ended';
|
||||
request_counts: MessageBatchRequestCounts;
|
||||
ended_at: string | null;
|
||||
created_at: string;
|
||||
expires_at: string;
|
||||
archived_at: string | null;
|
||||
cancel_initiated_at: string | null;
|
||||
results_url: string | null;
|
||||
}
|
||||
|
||||
interface MessageBatchRequestCounts {
|
||||
processing: number;
|
||||
succeeded: number;
|
||||
errored: number;
|
||||
canceled: number;
|
||||
expired: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: [
|
||||
{
|
||||
custom_id: 'request-1',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What is the capital of France?',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
custom_id: 'request-2',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What is 2 + 2?',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
custom_id: 'request-3',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Explain quantum computing in simple terms.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Batch ID:', batch.id);
|
||||
console.log('Status:', batch.processing_status);
|
||||
console.log('Created at:', batch.created_at);
|
||||
console.log('Expires at:', batch.expires_at);
|
||||
```
|
||||
|
||||
### Custom IDs
|
||||
|
||||
Each request requires a unique `custom_id`:
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Meaningful IDs
|
||||
{
|
||||
custom_id: 'user-123-question-1',
|
||||
params: { /* ... */ }
|
||||
}
|
||||
|
||||
// ✅ Good: Sequential IDs
|
||||
{
|
||||
custom_id: 'batch-001-item-001',
|
||||
params: { /* ... */ }
|
||||
}
|
||||
|
||||
// ❌ Bad: Duplicate IDs
|
||||
{
|
||||
custom_id: 'request', // Used multiple times
|
||||
params: { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
Custom IDs are used to match requests with results and must be unique within a batch.
|
||||
|
||||
## Checking Batch Status
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.retrieve(
|
||||
messageBatchID: string
|
||||
): APIPromise<MessageBatch>;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const batch = await client.messages.batches.retrieve('batch_123');
|
||||
|
||||
console.log('Processing status:', batch.processing_status);
|
||||
console.log('Request counts:', batch.request_counts);
|
||||
// {
|
||||
// processing: 5,
|
||||
// succeeded: 10,
|
||||
// errored: 2,
|
||||
// canceled: 0,
|
||||
// expired: 0
|
||||
// }
|
||||
|
||||
if (batch.processing_status === 'ended') {
|
||||
console.log('Batch processing complete!');
|
||||
console.log('Results URL:', batch.results_url);
|
||||
}
|
||||
```
|
||||
|
||||
### Processing Status
|
||||
|
||||
```typescript
|
||||
type ProcessingStatus =
|
||||
| 'in_progress' // Batch is being processed
|
||||
| 'canceling' // Cancellation in progress
|
||||
| 'ended' // Processing complete (success/error/expired)
|
||||
;
|
||||
```
|
||||
|
||||
### Polling for Completion
|
||||
|
||||
```typescript
|
||||
async function waitForBatch(batchId: string): Promise<MessageBatch> {
|
||||
while (true) {
|
||||
const batch = await client.messages.batches.retrieve(batchId);
|
||||
|
||||
if (batch.processing_status === 'ended') {
|
||||
return batch;
|
||||
}
|
||||
|
||||
// Wait before polling again
|
||||
await new Promise(resolve => setTimeout(resolve, 60000)); // 1 minute
|
||||
}
|
||||
}
|
||||
|
||||
const batch = await client.messages.batches.create({ /* ... */ });
|
||||
console.log('Waiting for batch to complete...');
|
||||
const completed = await waitForBatch(batch.id);
|
||||
console.log('Batch completed!', completed.request_counts);
|
||||
```
|
||||
|
||||
## Retrieving Results
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.results(
|
||||
messageBatchID: string
|
||||
): AsyncIterable<MessageBatchIndividualResponse>;
|
||||
|
||||
interface MessageBatchIndividualResponse {
|
||||
custom_id: string;
|
||||
result: MessageBatchResult;
|
||||
}
|
||||
|
||||
type MessageBatchResult =
|
||||
| MessageBatchSucceededResult
|
||||
| MessageBatchErroredResult
|
||||
| MessageBatchCanceledResult
|
||||
| MessageBatchExpiredResult;
|
||||
|
||||
interface MessageBatchSucceededResult {
|
||||
type: 'succeeded';
|
||||
message: Message;
|
||||
}
|
||||
|
||||
interface MessageBatchErroredResult {
|
||||
type: 'errored';
|
||||
error: {
|
||||
type: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface MessageBatchCanceledResult {
|
||||
type: 'canceled';
|
||||
}
|
||||
|
||||
interface MessageBatchExpiredResult {
|
||||
type: 'expired';
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const batch = await client.messages.batches.create({ /* ... */ });
|
||||
|
||||
// Wait for completion
|
||||
await waitForBatch(batch.id);
|
||||
|
||||
// Retrieve results
|
||||
const results = await client.messages.batches.results(batch.id);
|
||||
|
||||
for await (const result of results) {
|
||||
console.log('Custom ID:', result.custom_id);
|
||||
|
||||
if (result.result.type === 'succeeded') {
|
||||
const message = result.result.message;
|
||||
console.log('Success:', message.content[0].text);
|
||||
} else if (result.result.type === 'errored') {
|
||||
console.error('Error:', result.result.error.message);
|
||||
} else if (result.result.type === 'canceled') {
|
||||
console.log('Request was canceled');
|
||||
} else if (result.result.type === 'expired') {
|
||||
console.log('Request expired');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Collecting Results
|
||||
|
||||
```typescript
|
||||
const batch = await client.messages.batches.create({ /* ... */ });
|
||||
await waitForBatch(batch.id);
|
||||
|
||||
const results = await client.messages.batches.results(batch.id);
|
||||
const allResults: MessageBatchIndividualResponse[] = [];
|
||||
|
||||
for await (const result of results) {
|
||||
allResults.push(result);
|
||||
}
|
||||
|
||||
console.log(`Processed ${allResults.length} results`);
|
||||
|
||||
// Group by result type
|
||||
const succeeded = allResults.filter(r => r.result.type === 'succeeded');
|
||||
const errored = allResults.filter(r => r.result.type === 'errored');
|
||||
|
||||
console.log(`Succeeded: ${succeeded.length}, Errored: ${errored.length}`);
|
||||
```
|
||||
|
||||
## Listing Batches
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.list(
|
||||
params?: MessageBatchListParams
|
||||
): MessageBatchesPage;
|
||||
|
||||
interface MessageBatchListParams {
|
||||
before_id?: string;
|
||||
after_id?: string;
|
||||
limit?: number; // Default: 20, max: 100
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
// List recent batches
|
||||
const batches = await client.messages.batches.list({
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
for (const batch of batches.data) {
|
||||
console.log('Batch:', batch.id);
|
||||
console.log('Status:', batch.processing_status);
|
||||
console.log('Counts:', batch.request_counts);
|
||||
}
|
||||
|
||||
// Auto-pagination
|
||||
for await (const batch of client.messages.batches.list({ limit: 20 })) {
|
||||
console.log(batch.id);
|
||||
}
|
||||
```
|
||||
|
||||
## Canceling a Batch
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.cancel(
|
||||
messageBatchID: string
|
||||
): APIPromise<MessageBatch>;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const batch = await client.messages.batches.create({ /* ... */ });
|
||||
|
||||
// Cancel the batch
|
||||
const canceled = await client.messages.batches.cancel(batch.id);
|
||||
console.log('Status:', canceled.processing_status); // 'canceling'
|
||||
|
||||
// Wait for cancellation to complete
|
||||
await waitForBatch(canceled.id);
|
||||
|
||||
// Check final status
|
||||
const final = await client.messages.batches.retrieve(canceled.id);
|
||||
console.log('Canceled requests:', final.request_counts.canceled);
|
||||
```
|
||||
|
||||
**Note**: Cancellation may take time. In-flight requests will complete, but pending requests will be canceled.
|
||||
|
||||
## Deleting a Batch
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.delete(
|
||||
messageBatchID: string
|
||||
): APIPromise<DeletedMessageBatch>;
|
||||
|
||||
interface DeletedMessageBatch {
|
||||
id: string;
|
||||
type: 'message_batch';
|
||||
deleted: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
// Delete a batch (must be ended first)
|
||||
const deleted = await client.messages.batches.delete('batch_123');
|
||||
console.log('Deleted:', deleted.deleted); // true
|
||||
```
|
||||
|
||||
**Important**: You can only delete batches that have `processing_status === 'ended'`. Archived batches are automatically deleted.
|
||||
|
||||
## Complete Workflow Example
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
// 1. Create batch
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: [
|
||||
{
|
||||
custom_id: 'analysis-1',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze this data: [1, 2, 3, 4, 5]',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
custom_id: 'analysis-2',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Summarize: The quick brown fox jumps over the lazy dog.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Created batch:', batch.id);
|
||||
|
||||
// 2. Poll for completion
|
||||
console.log('Waiting for completion...');
|
||||
let currentBatch = batch;
|
||||
while (currentBatch.processing_status !== 'ended') {
|
||||
await new Promise(resolve => setTimeout(resolve, 30000)); // 30 seconds
|
||||
currentBatch = await client.messages.batches.retrieve(batch.id);
|
||||
console.log('Status:', currentBatch.request_counts);
|
||||
}
|
||||
|
||||
// 3. Retrieve results
|
||||
console.log('Retrieving results...');
|
||||
const results = await client.messages.batches.results(batch.id);
|
||||
|
||||
const resultMap = new Map<string, MessageBatchIndividualResponse>();
|
||||
for await (const result of results) {
|
||||
resultMap.set(result.custom_id, result);
|
||||
}
|
||||
|
||||
// 4. Process results
|
||||
for (const [customId, result] of resultMap) {
|
||||
console.log(`\nResult for ${customId}:`);
|
||||
|
||||
if (result.result.type === 'succeeded') {
|
||||
console.log('Response:', result.result.message.content[0].text);
|
||||
} else {
|
||||
console.error('Failed:', result.result);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Cleanup (optional)
|
||||
// await client.messages.batches.delete(batch.id);
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Bulk Document Processing
|
||||
|
||||
```typescript
|
||||
const documents = await loadDocuments(); // Load from database
|
||||
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: documents.map((doc, index) => ({
|
||||
custom_id: `doc-${doc.id}`,
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'document',
|
||||
source: {
|
||||
type: 'base64',
|
||||
media_type: 'application/pdf',
|
||||
data: doc.content,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Summarize this document.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
});
|
||||
```
|
||||
|
||||
### Data Analysis Pipeline
|
||||
|
||||
```typescript
|
||||
const dataPoints = await fetchDataPoints();
|
||||
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: dataPoints.map((data) => ({
|
||||
custom_id: `analysis-${data.id}`,
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: `Analyze this data and provide insights: ${JSON.stringify(data)}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
await waitForBatch(batch.id);
|
||||
|
||||
const results = await client.messages.batches.results(batch.id);
|
||||
for await (const result of results) {
|
||||
if (result.result.type === 'succeeded') {
|
||||
await saveAnalysis(result.custom_id, result.result.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Translation Service
|
||||
|
||||
```typescript
|
||||
const textsToTranslate = [
|
||||
{ id: '1', text: 'Hello, world!', targetLang: 'Spanish' },
|
||||
{ id: '2', text: 'Good morning!', targetLang: 'French' },
|
||||
{ id: '3', text: 'Thank you!', targetLang: 'German' },
|
||||
];
|
||||
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: textsToTranslate.map((item) => ({
|
||||
custom_id: `translation-${item.id}`,
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: `Translate "${item.text}" to ${item.targetLang}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
});
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Batch Size**: Maximum 10,000 requests per batch
|
||||
- **Expiration**: Batches expire after 24 hours
|
||||
- **Processing Time**: No guaranteed completion time (typically minutes to hours)
|
||||
- **No Streaming**: Individual requests cannot stream results
|
||||
- **Rate Limits**: Subject to account-level rate limits
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
const results = await client.messages.batches.results(batchId);
|
||||
|
||||
for await (const result of results) {
|
||||
if (result.result.type === 'succeeded') {
|
||||
await processSuccess(result);
|
||||
} else if (result.result.type === 'errored') {
|
||||
await logError(result.custom_id, result.result.error);
|
||||
await retryRequest(result.custom_id);
|
||||
} else if (result.result.type === 'expired') {
|
||||
await handleExpiredRequest(result.custom_id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Request Chunking
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Process in chunks
|
||||
async function processBatches(requests: Request[]) {
|
||||
const CHUNK_SIZE = 5000;
|
||||
|
||||
for (let i = 0; i < requests.length; i += CHUNK_SIZE) {
|
||||
const chunk = requests.slice(i, i + CHUNK_SIZE);
|
||||
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: chunk.map((req, index) => ({
|
||||
custom_id: `chunk-${i / CHUNK_SIZE}-item-${index}`,
|
||||
params: req.params,
|
||||
})),
|
||||
});
|
||||
|
||||
await waitForBatch(batch.id);
|
||||
await processResults(batch.id);
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad: Exceeding limits
|
||||
const batch = await client.messages.batches.create({
|
||||
requests: allRequests.map(/* ... */), // Could be > 10,000
|
||||
});
|
||||
```
|
||||
|
||||
### Result Processing
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Stream and process results
|
||||
const results = await client.messages.batches.results(batchId);
|
||||
|
||||
for await (const result of results) {
|
||||
// Process immediately, don't accumulate
|
||||
await handleResult(result);
|
||||
}
|
||||
|
||||
// ❌ Bad: Loading all into memory
|
||||
const results = await client.messages.batches.results(batchId);
|
||||
const allResults = [];
|
||||
for await (const result of results) {
|
||||
allResults.push(result); // Can be large
|
||||
}
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```typescript
|
||||
async function monitorBatch(batchId: string) {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
const batch = await client.messages.batches.retrieve(batchId);
|
||||
|
||||
const elapsed = (Date.now() - startTime) / 1000 / 60; // minutes
|
||||
console.log(`[${elapsed.toFixed(1)}m] Status:`, batch.request_counts);
|
||||
|
||||
if (batch.processing_status === 'ended') {
|
||||
console.log('Batch complete!');
|
||||
return batch;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 60000)); // 1 min
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cost Optimization
|
||||
|
||||
Batches provide 50% cost savings:
|
||||
|
||||
```typescript
|
||||
// Standard API cost: $0.003 per 1K input tokens (example)
|
||||
// Batch API cost: $0.0015 per 1K input tokens (50% off)
|
||||
|
||||
// For 1000 requests with 500 input tokens each:
|
||||
// Standard: 1000 * 0.5K * $0.003 = $1.50
|
||||
// Batch: 1000 * 0.5K * $0.0015 = $0.75
|
||||
// Savings: $0.75 (50%)
|
||||
```
|
||||
|
||||
Use batches for:
|
||||
- Non-urgent bulk processing
|
||||
- Offline analysis
|
||||
- Data pipelines
|
||||
- Scheduled tasks
|
||||
|
||||
Use standard API for:
|
||||
- Real-time responses
|
||||
- User-facing applications
|
||||
- Time-sensitive requests
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Individual message creation
|
||||
- [Tools](./tools.md) - Using tools in batch requests
|
||||
- [Types](./types.md) - Batch type definitions
|
||||
678
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/beta-features.md
Normal file
678
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/beta-features.md
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
# Beta Features
|
||||
|
||||
The Anthropic SDK provides access to experimental and advanced features through the beta namespace. Beta features require specific beta headers and may change before general availability.
|
||||
|
||||
## Overview
|
||||
|
||||
Beta features include:
|
||||
- **Code Execution**: Execute Python and bash code in sandboxed environments
|
||||
- **Computer Use**: Bash, text editor, and computer control tools
|
||||
- **Extended Thinking**: Access Claude's reasoning process
|
||||
- **MCP (Model Context Protocol)**: External tool integration
|
||||
- **Memory Tools**: Persistent memory across conversations
|
||||
- **Web Search & Fetch**: Search the web and fetch content
|
||||
- **Context Management**: Advanced context window management
|
||||
- **Structured Outputs**: JSON output with automatic parsing
|
||||
- **Files API**: File upload and management
|
||||
- **Skills API**: Custom skill definitions
|
||||
|
||||
## Accessing Beta Features
|
||||
|
||||
Use the `client.beta` namespace and include appropriate beta headers:
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['code-execution-2025-05-22'], // Required beta header
|
||||
messages: [{ role: 'user', content: 'Execute Python code' }],
|
||||
});
|
||||
```
|
||||
|
||||
## Available Beta Headers
|
||||
|
||||
```typescript { .api }
|
||||
type AnthropicBeta =
|
||||
| 'message-batches-2024-09-24'
|
||||
| 'prompt-caching-2024-07-31'
|
||||
| 'computer-use-2024-10-22'
|
||||
| 'computer-use-2025-01-24'
|
||||
| 'pdfs-2024-09-25'
|
||||
| 'token-counting-2024-11-01'
|
||||
| 'token-efficient-tools-2025-02-19'
|
||||
| 'output-128k-2025-02-19'
|
||||
| 'files-api-2025-04-14'
|
||||
| 'mcp-client-2025-04-04'
|
||||
| 'dev-full-thinking-2025-05-14'
|
||||
| 'interleaved-thinking-2025-05-14'
|
||||
| 'code-execution-2025-05-22'
|
||||
| 'code-execution-2025-08-25'
|
||||
| 'extended-cache-ttl-2025-04-11'
|
||||
| 'context-1m-2025-08-07'
|
||||
| 'context-management-2025-06-27'
|
||||
| 'model-context-window-exceeded-2025-08-26'
|
||||
| 'skills-2025-10-02'
|
||||
;
|
||||
```
|
||||
|
||||
## Code Execution
|
||||
|
||||
Execute Python code in a sandboxed environment:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['code-execution-2025-05-22'],
|
||||
tools: [
|
||||
{
|
||||
name: 'code_execution',
|
||||
type: 'code_execution_20250522',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Calculate the factorial of 10 using Python.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Claude will use code_execution tool
|
||||
for (const block of message.content) {
|
||||
if (block.type === 'code_execution_result') {
|
||||
console.log('Output:', block.output);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Versions**:
|
||||
- `code_execution_20250522`: Version from 2025-05-22
|
||||
- `code_execution_20250825`: Version from 2025-08-25
|
||||
|
||||
## Computer Use Tools
|
||||
|
||||
Interact with computer interfaces:
|
||||
|
||||
### Bash Tool
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['computer-use-2025-01-24'],
|
||||
tools: [
|
||||
{
|
||||
name: 'bash',
|
||||
type: 'bash_20250124',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'List files in the current directory.',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Text Editor Tool
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['computer-use-2025-01-24'],
|
||||
tools: [
|
||||
{
|
||||
name: 'str_replace_editor',
|
||||
type: 'text_editor_20250728',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Create a file and write "Hello World" to it.',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
**Tool Versions**:
|
||||
- `bash_20241022`, `bash_20250124`: Bash execution
|
||||
- `text_editor_20241022`, `text_editor_20250124`, `text_editor_20250429`, `text_editor_20250728`: Text editor operations
|
||||
- `computer_use_20241022`, `computer_use_20250124`: Full computer control
|
||||
|
||||
## Extended Thinking
|
||||
|
||||
Access Claude's reasoning process:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 4096,
|
||||
betas: ['dev-full-thinking-2025-05-14'],
|
||||
thinking: {
|
||||
type: 'enabled',
|
||||
budget_tokens: 2000,
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Solve this complex problem: ...',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Access thinking blocks
|
||||
for (const block of message.content) {
|
||||
if (block.type === 'thinking') {
|
||||
console.log('Reasoning:', block.thinking);
|
||||
console.log('Signature:', block.signature);
|
||||
} else if (block.type === 'text') {
|
||||
console.log('Answer:', block.text);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Beta Headers**:
|
||||
- `dev-full-thinking-2025-05-14`: Full thinking traces
|
||||
- `interleaved-thinking-2025-05-14`: Interleaved thinking and responses
|
||||
|
||||
## Memory Tools
|
||||
|
||||
Persistent memory across conversations:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['memory-2025-08-18'],
|
||||
tools: [
|
||||
{
|
||||
name: 'memory',
|
||||
type: 'memory_20250818',
|
||||
commands: {
|
||||
create: {
|
||||
enabled: true,
|
||||
},
|
||||
view: {
|
||||
enabled: true,
|
||||
},
|
||||
delete: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Remember that my name is Alice.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Later conversation
|
||||
const message2 = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['memory-2025-08-18'],
|
||||
tools: [
|
||||
{
|
||||
name: 'memory',
|
||||
type: 'memory_20250818',
|
||||
commands: {
|
||||
view: { enabled: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What is my name?',
|
||||
},
|
||||
],
|
||||
});
|
||||
// Claude will recall: "Alice"
|
||||
```
|
||||
|
||||
**Memory Commands**:
|
||||
- `create`: Store new memory
|
||||
- `view`: Retrieve memory
|
||||
- `delete`: Remove memory
|
||||
- `insert`: Add content to memory
|
||||
- `str_replace`: Replace content in memory
|
||||
- `rename`: Rename memory
|
||||
|
||||
## Web Search & Fetch
|
||||
|
||||
Search the web and fetch content:
|
||||
|
||||
### Web Search
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['web-search-2025-03-05'],
|
||||
tools: [
|
||||
{
|
||||
name: 'web_search',
|
||||
type: 'web_search_20250305',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Search for recent AI news.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Claude will use web_search tool and return results
|
||||
for (const block of message.content) {
|
||||
if (block.type === 'web_search_result') {
|
||||
console.log('Query:', block.query);
|
||||
console.log('Results:', block.results);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Web Fetch
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['web-fetch-2025-09-10'],
|
||||
tools: [
|
||||
{
|
||||
name: 'web_fetch',
|
||||
type: 'web_fetch_20250910',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Fetch the content from https://example.com',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## MCP (Model Context Protocol)
|
||||
|
||||
Integrate external tools via MCP:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['mcp-client-2025-04-04'],
|
||||
mcp_servers: [
|
||||
{
|
||||
url: 'https://mcp-server.example.com',
|
||||
tools: ['*'], // Allow all tools from server
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Use external MCP tools.',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Context Management
|
||||
|
||||
Advanced context window management:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['context-management-2025-06-27'],
|
||||
context_management: {
|
||||
input_tokens_clear_at_least: 50000, // Clear when over 50k tokens
|
||||
tool_uses: {
|
||||
keep: 10, // Keep last 10 tool uses
|
||||
},
|
||||
thinking_turns: {
|
||||
keep: 'all', // Keep all thinking turns
|
||||
},
|
||||
},
|
||||
messages: longConversation,
|
||||
});
|
||||
```
|
||||
|
||||
**Configuration**:
|
||||
- `input_tokens_clear_at_least`: Threshold for clearing context
|
||||
- `tool_uses.keep`: Number of tool interactions to retain
|
||||
- `thinking_turns.keep`: How many thinking turns to keep (`'all'` or `'none'`)
|
||||
|
||||
## Structured Outputs
|
||||
|
||||
JSON output with automatic parsing:
|
||||
|
||||
```typescript
|
||||
import { betaZodOutputFormat } from '@anthropic-ai/sdk/helpers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
const outputFormat = betaZodOutputFormat(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
email: z.string().email(),
|
||||
})
|
||||
);
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['structured-outputs-2025-02-19'],
|
||||
output_format: {
|
||||
type: 'json_schema',
|
||||
schema: outputFormat.schema,
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Extract information from: John Doe, 30, john@example.com',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Parse output
|
||||
const parsed = outputFormat.parse(message.content[0].text);
|
||||
console.log(parsed.name); // "John Doe"
|
||||
console.log(parsed.age); // 30
|
||||
console.log(parsed.email); // "john@example.com"
|
||||
```
|
||||
|
||||
Or use JSON Schema:
|
||||
|
||||
```typescript
|
||||
import { betaJSONSchemaOutputFormat } from '@anthropic-ai/sdk/helpers/json-schema';
|
||||
|
||||
const outputFormat = betaJSONSchemaOutputFormat({
|
||||
type: 'object',
|
||||
properties: {
|
||||
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
|
||||
confidence: { type: 'number', minimum: 0, maximum: 1 },
|
||||
},
|
||||
required: ['sentiment', 'confidence'],
|
||||
});
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 512,
|
||||
betas: ['structured-outputs-2025-02-19'],
|
||||
output_format: {
|
||||
type: 'json_schema',
|
||||
schema: outputFormat.schema,
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze sentiment: This product is amazing!',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const parsed = outputFormat.parse(message.content[0].text);
|
||||
console.log(parsed.sentiment); // "positive"
|
||||
console.log(parsed.confidence); // 0.95
|
||||
```
|
||||
|
||||
## Prompt Caching
|
||||
|
||||
Cache frequently used prompts for reduced latency:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['prompt-caching-2024-07-31'],
|
||||
system: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'You are a helpful assistant.',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: largeKnowledgeBase,
|
||||
cache_control: {
|
||||
type: 'ephemeral',
|
||||
ttl: '1h', // Cache for 1 hour
|
||||
},
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Question about the knowledge base...',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Cache usage:', message.usage);
|
||||
// {
|
||||
// input_tokens: 1000,
|
||||
// cache_read_input_tokens: 50000, // Read from cache
|
||||
// output_tokens: 500
|
||||
// }
|
||||
```
|
||||
|
||||
**TTL Options**:
|
||||
- `5m`: 5 minutes (default)
|
||||
- `1h`: 1 hour (requires `extended-cache-ttl-2025-04-11` beta)
|
||||
|
||||
## Message Batches
|
||||
|
||||
Process multiple messages asynchronously:
|
||||
|
||||
```typescript
|
||||
const batch = await client.beta.messages.batches.create({
|
||||
betas: ['message-batches-2024-09-24'],
|
||||
requests: [
|
||||
{
|
||||
custom_id: 'request-1',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Task 1' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
custom_id: 'request-2',
|
||||
params: {
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Task 2' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Batch ID:', batch.id);
|
||||
```
|
||||
|
||||
See [batches.md](./batches.md) for complete documentation.
|
||||
|
||||
## Files API
|
||||
|
||||
Upload and manage files:
|
||||
|
||||
```typescript
|
||||
import { toFile } from '@anthropic-ai/sdk';
|
||||
import fs from 'fs';
|
||||
|
||||
const file = await client.beta.files.upload({
|
||||
file: await toFile(fs.createReadStream('document.pdf'), 'document.pdf', { type: 'application/pdf' }),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'document',
|
||||
source: {
|
||||
type: 'file',
|
||||
file_id: file.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Summarize this document.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
See [files.md](./files.md) for complete documentation.
|
||||
|
||||
## Skills API
|
||||
|
||||
Create and manage custom skills:
|
||||
|
||||
```typescript
|
||||
const skill = await client.beta.skills.create({
|
||||
name: 'data_analyzer',
|
||||
description: 'Analyze structured data',
|
||||
definition: {
|
||||
type: 'analysis',
|
||||
capabilities: ['statistics', 'visualization'],
|
||||
},
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['skills-2025-10-02'],
|
||||
skills: [
|
||||
{
|
||||
type: 'skill',
|
||||
skill_id: skill.id,
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze this data...',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
See [skills.md](./skills.md) for complete documentation.
|
||||
|
||||
## Combining Beta Features
|
||||
|
||||
Use multiple beta features together:
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 4096,
|
||||
betas: [
|
||||
'code-execution-2025-05-22',
|
||||
'dev-full-thinking-2025-05-14',
|
||||
'web-search-2025-03-05',
|
||||
],
|
||||
thinking: {
|
||||
type: 'enabled',
|
||||
budget_tokens: 2000,
|
||||
},
|
||||
tools: [
|
||||
{
|
||||
name: 'code_execution',
|
||||
type: 'code_execution_20250522',
|
||||
},
|
||||
{
|
||||
name: 'web_search',
|
||||
type: 'web_search_20250305',
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Search for recent data on topic X, then analyze it with Python.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Claude can use thinking, web search, and code execution together
|
||||
```
|
||||
|
||||
## Beta API Namespace
|
||||
|
||||
All beta features are accessed through the `client.beta` namespace:
|
||||
|
||||
```typescript
|
||||
client.beta.messages.* // Beta messages API
|
||||
client.beta.messages.batches.* // Beta message batches
|
||||
client.beta.models.* // Beta models API
|
||||
client.beta.files.* // Files API
|
||||
client.beta.skills.* // Skills API
|
||||
client.beta.skills.versions.* // Skill versions
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Feature Detection
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Check beta support
|
||||
const betaHeaders: AnthropicBeta[] = ['code-execution-2025-05-22'];
|
||||
|
||||
try {
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: betaHeaders,
|
||||
tools: [{ name: 'code_execution', type: 'code_execution_20250522' }],
|
||||
messages: [{ role: 'user', content: 'Execute code' }],
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.BadRequestError) {
|
||||
console.error('Beta feature not available');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migration Planning
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Prepare for GA migration
|
||||
const USE_BETA = process.env.USE_CODE_EXECUTION === 'true';
|
||||
|
||||
const message = await (USE_BETA ? client.beta.messages : client.messages).create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
...(USE_BETA && { betas: ['code-execution-2025-05-22'] }),
|
||||
messages: [{ role: 'user', content: 'Task' }],
|
||||
});
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Core message creation
|
||||
- [Tools](./tools.md) - Tool use and helpers
|
||||
- [Files](./files.md) - File upload and management
|
||||
- [Skills](./skills.md) - Custom skill definitions
|
||||
- [Batches](./batches.md) - Batch processing
|
||||
744
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/client.md
Normal file
744
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/client.md
Normal file
|
|
@ -0,0 +1,744 @@
|
|||
# Client Initialization and Configuration
|
||||
|
||||
The Anthropic client is the primary interface for interacting with the Anthropic API. It handles authentication, configuration, and provides access to all API resource endpoints.
|
||||
|
||||
## Import
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
```
|
||||
|
||||
CommonJS:
|
||||
|
||||
```typescript
|
||||
const Anthropic = require('@anthropic-ai/sdk');
|
||||
```
|
||||
|
||||
## Client Class
|
||||
|
||||
```typescript { .api }
|
||||
class Anthropic extends BaseAnthropic {
|
||||
constructor(options?: ClientOptions);
|
||||
|
||||
// Resource endpoints
|
||||
completions: Completions;
|
||||
messages: Messages;
|
||||
models: Models;
|
||||
beta: Beta;
|
||||
|
||||
// Instance methods
|
||||
withOptions(options: Partial<ClientOptions>): this;
|
||||
|
||||
// Static constants
|
||||
static HUMAN_PROMPT: string;
|
||||
static AI_PROMPT: string;
|
||||
static DEFAULT_TIMEOUT: number;
|
||||
}
|
||||
```
|
||||
|
||||
## ClientOptions
|
||||
|
||||
Complete configuration options for the Anthropic client.
|
||||
|
||||
```typescript { .api }
|
||||
interface ClientOptions {
|
||||
/**
|
||||
* API key for authentication.
|
||||
* Can be a static string or async function for dynamic credentials.
|
||||
* Defaults to process.env['ANTHROPIC_API_KEY']
|
||||
*/
|
||||
apiKey?: string | ApiKeySetter | null;
|
||||
|
||||
/**
|
||||
* Alternative authentication token.
|
||||
* Defaults to process.env['ANTHROPIC_AUTH_TOKEN']
|
||||
*/
|
||||
authToken?: string | null;
|
||||
|
||||
/**
|
||||
* Custom API base URL.
|
||||
* Defaults to process.env['ANTHROPIC_BASE_URL'] or 'https://api.anthropic.com'
|
||||
*/
|
||||
baseURL?: string | null;
|
||||
|
||||
/**
|
||||
* Request timeout in milliseconds.
|
||||
* Default: 600000 (10 minutes)
|
||||
*
|
||||
* For non-streaming requests with large max_tokens, timeout is dynamically
|
||||
* calculated: min(10 minutes, (60 * max_tokens) / 128000 hours)
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* Maximum number of retry attempts for failed requests.
|
||||
* Default: 2
|
||||
*
|
||||
* Retries occur for:
|
||||
* - Network errors (connection failures)
|
||||
* - 408 Request Timeout
|
||||
* - 409 Conflict
|
||||
* - 429 Rate Limit
|
||||
* - 5xx Server errors
|
||||
*/
|
||||
maxRetries?: number;
|
||||
|
||||
/**
|
||||
* Additional fetch options passed to all requests.
|
||||
* Can include headers, signal, credentials, etc.
|
||||
*/
|
||||
fetchOptions?: RequestInit;
|
||||
|
||||
/**
|
||||
* Custom fetch implementation.
|
||||
* If not provided, uses global fetch.
|
||||
*/
|
||||
fetch?: typeof fetch;
|
||||
|
||||
/**
|
||||
* Default headers included with every request.
|
||||
* Can be overridden per-request by setting header to null.
|
||||
*/
|
||||
defaultHeaders?: HeadersLike;
|
||||
|
||||
/**
|
||||
* Default query parameters included with every request.
|
||||
* Can be removed per-request by setting param to undefined.
|
||||
*/
|
||||
defaultQuery?: Record<string, string | undefined>;
|
||||
|
||||
/**
|
||||
* Enable client-side usage (browser environments).
|
||||
* Default: false
|
||||
*
|
||||
* WARNING: Enabling this in browsers exposes API credentials.
|
||||
* Only use with appropriate security mitigations.
|
||||
*/
|
||||
dangerouslyAllowBrowser?: boolean;
|
||||
|
||||
/**
|
||||
* Logging level for SDK operations.
|
||||
* Defaults to process.env['ANTHROPIC_LOG'] or 'warn'
|
||||
*/
|
||||
logLevel?: LogLevel;
|
||||
|
||||
/**
|
||||
* Custom logger instance.
|
||||
* Defaults to globalThis.console
|
||||
*/
|
||||
logger?: Logger;
|
||||
}
|
||||
```
|
||||
|
||||
### Supporting Types
|
||||
|
||||
```typescript { .api }
|
||||
// Dynamic API key function
|
||||
type ApiKeySetter = () => Promise<string>;
|
||||
|
||||
// Log levels (most to least verbose)
|
||||
type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
|
||||
|
||||
// Logger interface (compatible with console, pino, winston, etc.)
|
||||
interface Logger {
|
||||
debug(...args: any[]): void;
|
||||
info(...args: any[]): void;
|
||||
warn(...args: any[]): void;
|
||||
error(...args: any[]): void;
|
||||
}
|
||||
|
||||
// Header types
|
||||
type HeadersLike = Record<string, string | string[] | undefined> | Headers;
|
||||
```
|
||||
|
||||
## Basic Initialization
|
||||
|
||||
### Default Configuration
|
||||
|
||||
Uses environment variables for authentication:
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic();
|
||||
// Uses process.env.ANTHROPIC_API_KEY
|
||||
```
|
||||
|
||||
### Explicit API Key
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
apiKey: 'your-api-key-here',
|
||||
});
|
||||
```
|
||||
|
||||
### Dynamic API Key
|
||||
|
||||
For rotating credentials or runtime key fetching:
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
apiKey: async () => {
|
||||
// Fetch key from secure storage
|
||||
const key = await fetchKeyFromVault();
|
||||
return key;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The function is called before each request, allowing for:
|
||||
- Credential rotation
|
||||
- Token refresh
|
||||
- Runtime key management
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Custom Timeout
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
timeout: 30 * 1000, // 30 seconds
|
||||
});
|
||||
```
|
||||
|
||||
### Disable Retries
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
maxRetries: 0,
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Base URL
|
||||
|
||||
For proxies or custom endpoints:
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
baseURL: 'https://proxy.example.com/anthropic',
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Headers
|
||||
|
||||
Add headers to all requests:
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
defaultHeaders: {
|
||||
'X-Custom-Header': 'value',
|
||||
'User-Agent': 'MyApp/1.0',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Browser Usage (Not Recommended)
|
||||
|
||||
```typescript
|
||||
const client = new Anthropic({
|
||||
apiKey: 'your-api-key',
|
||||
dangerouslyAllowBrowser: true, // SECURITY WARNING
|
||||
});
|
||||
```
|
||||
|
||||
**WARNING**: This exposes your API key in client-side code. Only use for:
|
||||
- Internal tools with trusted users
|
||||
- Development/debugging with temporary keys
|
||||
- Environments where key exposure is acceptable
|
||||
|
||||
### Logging Configuration
|
||||
|
||||
```typescript
|
||||
// Set log level via environment
|
||||
// export ANTHROPIC_LOG=debug
|
||||
|
||||
// Or via options
|
||||
const client = new Anthropic({
|
||||
logLevel: 'debug', // Shows all HTTP requests/responses
|
||||
});
|
||||
|
||||
// Custom logger (pino example)
|
||||
import pino from 'pino';
|
||||
|
||||
const logger = pino();
|
||||
const client = new Anthropic({
|
||||
logger: logger.child({ component: 'anthropic-sdk' }),
|
||||
logLevel: 'debug',
|
||||
});
|
||||
```
|
||||
|
||||
Log levels:
|
||||
- `debug`: All requests/responses with headers and bodies (may expose sensitive data)
|
||||
- `info`: General informational messages
|
||||
- `warn`: Warnings and errors (default)
|
||||
- `error`: Only errors
|
||||
- `off`: No logging
|
||||
|
||||
### Custom Fetch Implementation
|
||||
|
||||
For Node.js proxies or custom network handling:
|
||||
|
||||
```typescript
|
||||
// Node.js with undici proxy
|
||||
import * as undici from 'undici';
|
||||
|
||||
const proxyAgent = new undici.ProxyAgent('http://proxy.example.com:8080');
|
||||
const client = new Anthropic({
|
||||
fetchOptions: {
|
||||
dispatcher: proxyAgent, // undici-specific
|
||||
},
|
||||
});
|
||||
|
||||
// Bun with built-in proxy support
|
||||
const client = new Anthropic({
|
||||
fetchOptions: {
|
||||
proxy: 'http://proxy.example.com:8080',
|
||||
},
|
||||
});
|
||||
|
||||
// Deno with HTTP client
|
||||
const httpClient = Deno.createHttpClient({
|
||||
proxy: { url: 'http://proxy.example.com:8080' },
|
||||
});
|
||||
const client = new Anthropic({
|
||||
fetchOptions: {
|
||||
client: httpClient,
|
||||
},
|
||||
});
|
||||
|
||||
// Custom fetch function
|
||||
const client = new Anthropic({
|
||||
fetch: async (url, init) => {
|
||||
// Add custom logic
|
||||
console.log('Fetching:', url);
|
||||
return fetch(url, init);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Instance Methods
|
||||
|
||||
### withOptions()
|
||||
|
||||
Create a new client with modified options while preserving existing configuration:
|
||||
|
||||
```typescript { .api }
|
||||
withOptions(options: Partial<ClientOptions>): Anthropic;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const originalClient = new Anthropic({
|
||||
apiKey: 'key-1',
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
// Create new client with different timeout
|
||||
const timeoutClient = originalClient.withOptions({
|
||||
timeout: 120000,
|
||||
});
|
||||
|
||||
// Create new client with different API key
|
||||
const secondClient = originalClient.withOptions({
|
||||
apiKey: 'key-2',
|
||||
});
|
||||
|
||||
// Original client unchanged
|
||||
console.log(originalClient.timeout); // 60000
|
||||
console.log(timeoutClient.timeout); // 120000
|
||||
```
|
||||
|
||||
Use cases:
|
||||
- Multi-tenant applications with different API keys
|
||||
- Different timeout requirements per use case
|
||||
- Testing with different configurations
|
||||
|
||||
## Resource Endpoints
|
||||
|
||||
The client provides access to all API resources:
|
||||
|
||||
```typescript { .api }
|
||||
client.messages // Messages API (primary conversational interface)
|
||||
client.completions // Legacy completions API
|
||||
client.models // Model information
|
||||
client.beta // Beta features namespace
|
||||
```
|
||||
|
||||
### Messages
|
||||
|
||||
```typescript
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
});
|
||||
```
|
||||
|
||||
See [messages.md](./messages.md)
|
||||
|
||||
### Models
|
||||
|
||||
```typescript
|
||||
const models = await client.models.list();
|
||||
const model = await client.models.retrieve('claude-sonnet-4-5-20250929');
|
||||
```
|
||||
|
||||
See [models.md](./models.md)
|
||||
|
||||
### Beta Features
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
betas: ['code-execution-2025-05-22'],
|
||||
tools: [{ name: 'code_execution', type: 'code_execution_20250522' }],
|
||||
});
|
||||
```
|
||||
|
||||
See [beta-features.md](./beta-features.md)
|
||||
|
||||
## Static Constants
|
||||
|
||||
Legacy prompt delimiters for the old text completions API:
|
||||
|
||||
```typescript { .api }
|
||||
Anthropic.HUMAN_PROMPT: string // '\n\nHuman:'
|
||||
Anthropic.AI_PROMPT: string // '\n\nAssistant:'
|
||||
Anthropic.DEFAULT_TIMEOUT: number // 600000 (10 minutes)
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const prompt = `${Anthropic.HUMAN_PROMPT} What is 2+2?${Anthropic.AI_PROMPT}`;
|
||||
|
||||
// Used with legacy completions API
|
||||
const completion = await client.completions.create({
|
||||
model: 'claude-2.1',
|
||||
prompt,
|
||||
max_tokens_to_sample: 100,
|
||||
});
|
||||
```
|
||||
|
||||
**Note**: These are for the legacy completions API. The modern Messages API uses structured message objects instead.
|
||||
|
||||
## Per-Request Options
|
||||
|
||||
Override client configuration for individual requests:
|
||||
|
||||
```typescript { .api }
|
||||
interface RequestOptions {
|
||||
timeout?: number;
|
||||
maxRetries?: number;
|
||||
headers?: HeadersLike;
|
||||
query?: Record<string, string | undefined>;
|
||||
signal?: AbortSignal;
|
||||
idempotencyKey?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
// Shorter timeout for this request
|
||||
const message = await client.messages.create(
|
||||
{
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Quick question' }],
|
||||
},
|
||||
{
|
||||
timeout: 10000, // 10 seconds
|
||||
maxRetries: 0,
|
||||
headers: {
|
||||
'X-Request-ID': 'custom-id',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Manual cancellation with AbortController
|
||||
const controller = new AbortController();
|
||||
const promise = client.messages.create(
|
||||
{ /* params */ },
|
||||
{ signal: controller.signal }
|
||||
);
|
||||
|
||||
// Cancel request after 5 seconds
|
||||
setTimeout(() => controller.abort(), 5000);
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The SDK reads configuration from environment variables:
|
||||
|
||||
```bash
|
||||
# Authentication
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
ANTHROPIC_AUTH_TOKEN=...
|
||||
|
||||
# Configuration
|
||||
ANTHROPIC_BASE_URL=https://custom-endpoint.example.com
|
||||
ANTHROPIC_LOG=debug
|
||||
|
||||
# Runtime-specific variables are also supported
|
||||
NODE_ENV=production
|
||||
```
|
||||
|
||||
Priority order:
|
||||
1. Explicit constructor options
|
||||
2. Environment variables
|
||||
3. Default values
|
||||
|
||||
## Error Handling
|
||||
|
||||
The client will throw errors when:
|
||||
|
||||
```typescript
|
||||
// No API key provided
|
||||
try {
|
||||
const client = new Anthropic(); // No apiKey, env var not set
|
||||
} catch (error) {
|
||||
// AnthropicError: Missing API Key
|
||||
}
|
||||
|
||||
// Browser usage without explicit permission
|
||||
try {
|
||||
const client = new Anthropic({ apiKey: 'key' }); // In browser
|
||||
} catch (error) {
|
||||
// AnthropicError: Browser usage not allowed
|
||||
}
|
||||
|
||||
// Invalid configuration
|
||||
try {
|
||||
const client = new Anthropic({
|
||||
maxRetries: -1, // Invalid
|
||||
});
|
||||
} catch (error) {
|
||||
// Configuration validation error
|
||||
}
|
||||
```
|
||||
|
||||
See [errors.md](./errors.md) for complete error handling documentation.
|
||||
|
||||
## TypeScript Support
|
||||
|
||||
The client is fully typed with TypeScript:
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
// All types are exported
|
||||
import type {
|
||||
ClientOptions,
|
||||
Message,
|
||||
MessageParam,
|
||||
MessageCreateParams,
|
||||
} from '@anthropic-ai/sdk';
|
||||
|
||||
const client: Anthropic = new Anthropic();
|
||||
|
||||
// Type inference works automatically
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
});
|
||||
|
||||
// message is typed as Anthropic.Message
|
||||
console.log(message.content[0].type); // TypeScript knows the shape
|
||||
```
|
||||
|
||||
## Runtime Compatibility
|
||||
|
||||
The SDK supports multiple JavaScript runtimes:
|
||||
|
||||
### Node.js
|
||||
|
||||
```typescript
|
||||
// Node.js 20+ (LTS or later)
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
const client = new Anthropic();
|
||||
```
|
||||
|
||||
### Deno
|
||||
|
||||
```typescript
|
||||
// Deno v1.28.0+
|
||||
import Anthropic from 'npm:@anthropic-ai/sdk';
|
||||
const client = new Anthropic();
|
||||
```
|
||||
|
||||
### Bun
|
||||
|
||||
```typescript
|
||||
// Bun 1.0+
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
const client = new Anthropic();
|
||||
```
|
||||
|
||||
### Edge Runtimes
|
||||
|
||||
```typescript
|
||||
// Cloudflare Workers
|
||||
export default {
|
||||
async fetch(request) {
|
||||
const client = new Anthropic({
|
||||
apiKey: env.ANTHROPIC_API_KEY,
|
||||
});
|
||||
// ...
|
||||
},
|
||||
};
|
||||
|
||||
// Vercel Edge Functions
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
export const config = { runtime: 'edge' };
|
||||
export default async function handler(req) {
|
||||
const client = new Anthropic();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Browsers
|
||||
|
||||
```typescript
|
||||
// NOT recommended - exposes API key
|
||||
const client = new Anthropic({
|
||||
apiKey: 'your-key', // Will be visible in browser
|
||||
dangerouslyAllowBrowser: true,
|
||||
});
|
||||
```
|
||||
|
||||
**For production browser apps**: Use a backend proxy to keep credentials secure.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Request ID Tracking
|
||||
|
||||
All responses include a request ID for debugging:
|
||||
|
||||
```typescript
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
console.log(message._request_id); // 'req_018EeWyXxfu5pfWkrYcMdjWG'
|
||||
|
||||
// Or access via withResponse()
|
||||
const { data, response, request_id } = await client.messages
|
||||
.create({ /* ... */ })
|
||||
.withResponse();
|
||||
|
||||
console.log(request_id); // Same ID
|
||||
console.log(response.headers.get('request-id'));
|
||||
```
|
||||
|
||||
### Raw Response Access
|
||||
|
||||
Access underlying HTTP response:
|
||||
|
||||
```typescript
|
||||
// Get response before body is parsed
|
||||
const response = await client.messages.create({ /* ... */ }).asResponse();
|
||||
console.log(response.status);
|
||||
console.log(response.headers.get('x-ratelimit-remaining'));
|
||||
|
||||
// Get both parsed data and response
|
||||
const { data, response, request_id } = await client.messages
|
||||
.create({ /* ... */ })
|
||||
.withResponse();
|
||||
|
||||
console.log(data.content); // Parsed message
|
||||
console.log(response.status); // 200
|
||||
```
|
||||
|
||||
### Keep-Alive Configuration
|
||||
|
||||
For long-running connections, TCP keep-alive is automatically configured when supported by the fetch implementation. This reduces idle connection timeouts on some networks.
|
||||
|
||||
Override via custom fetch options if needed.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Credential Management
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Use environment variables
|
||||
const client = new Anthropic(); // Reads from ANTHROPIC_API_KEY
|
||||
|
||||
// ✅ Good: Use credential manager
|
||||
const client = new Anthropic({
|
||||
apiKey: async () => await credentialManager.getKey(),
|
||||
});
|
||||
|
||||
// ❌ Bad: Hardcode keys
|
||||
const client = new Anthropic({
|
||||
apiKey: 'sk-ant-...', // Don't commit this!
|
||||
});
|
||||
```
|
||||
|
||||
### Timeout Configuration
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Set reasonable timeouts
|
||||
const client = new Anthropic({
|
||||
timeout: 120000, // 2 minutes for typical requests
|
||||
});
|
||||
|
||||
// ✅ Good: Use streaming for long operations
|
||||
const stream = await client.messages.stream({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 4000,
|
||||
messages: [{ role: 'user', content: 'Long task...' }],
|
||||
});
|
||||
|
||||
// ❌ Bad: Very short timeout without streaming
|
||||
const client = new Anthropic({
|
||||
timeout: 5000, // Too short for API processing
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Handle specific errors
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
console.error('API error:', error.status, error.message);
|
||||
console.error('Request ID:', error.requestID);
|
||||
} else if (error instanceof Anthropic.APIConnectionError) {
|
||||
console.error('Network error:', error.message);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Reuse
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Reuse client instance
|
||||
const client = new Anthropic();
|
||||
|
||||
async function processMany(inputs) {
|
||||
return Promise.all(
|
||||
inputs.map(input =>
|
||||
client.messages.create({ /* ... */ })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// ❌ Bad: Create new client per request
|
||||
async function processBad(input) {
|
||||
const client = new Anthropic(); // Wasteful
|
||||
return client.messages.create({ /* ... */ });
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Core conversational interface
|
||||
- [Streaming](./streaming.md) - Real-time response streaming
|
||||
- [Error Handling](./errors.md) - Comprehensive error reference
|
||||
- [Beta Features](./beta-features.md) - Experimental capabilities
|
||||
208
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/completions.md
Normal file
208
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/completions.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# Text Completions API (Legacy)
|
||||
|
||||
The Text Completions API is a legacy API for generating text completions using Claude models. This API is maintained for backwards compatibility, but the [Messages API](./messages.md) is recommended for new implementations.
|
||||
|
||||
**Note:** Future models and features will not be compatible with Text Completions. See the [migration guide](https://docs.anthropic.com/en/api/migrating-from-text-completions-to-messages) for guidance on migrating from Text Completions to Messages.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Create Completion
|
||||
|
||||
Creates a text completion using the legacy prompt format with `\n\nHuman:` and `\n\nAssistant:` conversational turns.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Create a Text Completion (Legacy).
|
||||
*
|
||||
* @param params - Completion parameters
|
||||
* @param options - Optional request options
|
||||
* @returns APIPromise resolving to a Completion or Stream<Completion>
|
||||
*/
|
||||
client.completions.create(
|
||||
params: CompletionCreateParams
|
||||
): APIPromise<Completion> | APIPromise<Stream<Completion>>;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
});
|
||||
|
||||
// Non-streaming completion
|
||||
const completion = await client.completions.create({
|
||||
max_tokens_to_sample: 256,
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
prompt: '\n\nHuman: Hello, world!\n\nAssistant:',
|
||||
});
|
||||
|
||||
console.log(completion.completion);
|
||||
|
||||
// Streaming completion
|
||||
const stream = await client.completions.create({
|
||||
max_tokens_to_sample: 256,
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
prompt: '\n\nHuman: Tell me a story\n\nAssistant:',
|
||||
stream: true,
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
console.log(chunk.completion);
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
### Completion
|
||||
|
||||
The response object returned by the Text Completions API.
|
||||
|
||||
```typescript { .api }
|
||||
interface Completion {
|
||||
/**
|
||||
* Unique object identifier.
|
||||
* The format and length of IDs may change over time.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The resulting completion up to and excluding the stop sequences.
|
||||
*/
|
||||
completion: string;
|
||||
|
||||
/**
|
||||
* The model that completed your prompt.
|
||||
* See https://docs.anthropic.com/en/docs/models-overview for details.
|
||||
*/
|
||||
model: Model;
|
||||
|
||||
/**
|
||||
* The reason that generation stopped.
|
||||
*
|
||||
* Possible values:
|
||||
* - "stop_sequence": reached a stop sequence (provided or built-in)
|
||||
* - "max_tokens": exceeded max_tokens_to_sample or model's maximum
|
||||
*/
|
||||
stop_reason: string | null;
|
||||
|
||||
/**
|
||||
* Object type. For Text Completions, this is always "completion".
|
||||
*/
|
||||
type: 'completion';
|
||||
}
|
||||
```
|
||||
|
||||
### CompletionCreateParams
|
||||
|
||||
Parameters for creating a text completion.
|
||||
|
||||
```typescript { .api }
|
||||
type CompletionCreateParams =
|
||||
| CompletionCreateParamsNonStreaming
|
||||
| CompletionCreateParamsStreaming;
|
||||
|
||||
interface CompletionCreateParamsBase {
|
||||
/**
|
||||
* The maximum number of tokens to generate before stopping.
|
||||
*
|
||||
* Note that models may stop before reaching this maximum.
|
||||
* This parameter only specifies the absolute maximum number of tokens to generate.
|
||||
*/
|
||||
max_tokens_to_sample: number;
|
||||
|
||||
/**
|
||||
* The model that will complete your prompt.
|
||||
* See https://docs.anthropic.com/en/docs/models-overview for details.
|
||||
*/
|
||||
model: Model;
|
||||
|
||||
/**
|
||||
* The prompt that you want Claude to complete.
|
||||
*
|
||||
* For proper response generation, format your prompt using alternating
|
||||
* `\n\nHuman:` and `\n\nAssistant:` conversational turns.
|
||||
*
|
||||
* Example: "\n\nHuman: {userQuestion}\n\nAssistant:"
|
||||
*
|
||||
* See https://docs.claude.com/en/api/prompt-validation and
|
||||
* https://docs.claude.com/en/docs/intro-to-prompting for more details.
|
||||
*/
|
||||
prompt: string;
|
||||
|
||||
/**
|
||||
* An object describing metadata about the request.
|
||||
*/
|
||||
metadata?: Metadata;
|
||||
|
||||
/**
|
||||
* Sequences that will cause the model to stop generating.
|
||||
*
|
||||
* Models stop on "\n\nHuman:" and may include additional built-in stop
|
||||
* sequences in the future. By providing stop_sequences, you may include
|
||||
* additional strings that will cause the model to stop generating.
|
||||
*/
|
||||
stop_sequences?: string[];
|
||||
|
||||
/**
|
||||
* Whether to incrementally stream the response using server-sent events.
|
||||
* See https://docs.claude.com/en/api/streaming for details.
|
||||
*/
|
||||
stream?: boolean;
|
||||
|
||||
/**
|
||||
* Amount of randomness injected into the response.
|
||||
*
|
||||
* Defaults to 1.0. Ranges from 0.0 to 1.0.
|
||||
* Use temperature closer to 0.0 for analytical/multiple choice tasks,
|
||||
* and closer to 1.0 for creative and generative tasks.
|
||||
*
|
||||
* Note that even with temperature of 0.0, results will not be fully deterministic.
|
||||
*/
|
||||
temperature?: number;
|
||||
|
||||
/**
|
||||
* Only sample from the top K options for each subsequent token.
|
||||
*
|
||||
* Used to remove "long tail" low probability responses.
|
||||
* See https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277
|
||||
*
|
||||
* Recommended for advanced use cases only. You usually only need to use temperature.
|
||||
*/
|
||||
top_k?: number;
|
||||
|
||||
/**
|
||||
* Use nucleus sampling.
|
||||
*
|
||||
* In nucleus sampling, we compute the cumulative distribution over all options
|
||||
* for each subsequent token in decreasing probability order and cut it off once
|
||||
* it reaches a particular probability specified by top_p.
|
||||
*
|
||||
* You should either alter temperature or top_p, but not both.
|
||||
*
|
||||
* Recommended for advanced use cases only. You usually only need to use temperature.
|
||||
*/
|
||||
top_p?: number;
|
||||
|
||||
/**
|
||||
* Optional header to specify the beta version(s) you want to use.
|
||||
*/
|
||||
betas?: AnthropicBeta[];
|
||||
}
|
||||
|
||||
interface CompletionCreateParamsNonStreaming extends CompletionCreateParamsBase {
|
||||
/**
|
||||
* Whether to incrementally stream the response using server-sent events.
|
||||
*/
|
||||
stream?: false;
|
||||
}
|
||||
|
||||
interface CompletionCreateParamsStreaming extends CompletionCreateParamsBase {
|
||||
/**
|
||||
* Whether to incrementally stream the response using server-sent events.
|
||||
*/
|
||||
stream: true;
|
||||
}
|
||||
```
|
||||
697
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/errors.md
Normal file
697
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/errors.md
Normal file
|
|
@ -0,0 +1,697 @@
|
|||
# Error Handling
|
||||
|
||||
The Anthropic SDK provides comprehensive error classes for different failure scenarios. All errors extend from the base `AnthropicError` class and provide detailed information for debugging and recovery.
|
||||
|
||||
## Error Hierarchy
|
||||
|
||||
```typescript { .api }
|
||||
AnthropicError
|
||||
├── APIError<TStatus, THeaders, TError>
|
||||
│ ├── BadRequestError (400)
|
||||
│ ├── AuthenticationError (401)
|
||||
│ ├── PermissionDeniedError (403)
|
||||
│ ├── NotFoundError (404)
|
||||
│ ├── ConflictError (409)
|
||||
│ ├── UnprocessableEntityError (422)
|
||||
│ ├── RateLimitError (429)
|
||||
│ └── InternalServerError (5xx)
|
||||
├── APIConnectionError
|
||||
│ └── APIConnectionTimeoutError
|
||||
└── APIUserAbortError
|
||||
```
|
||||
|
||||
## Base Error Class
|
||||
|
||||
```typescript { .api }
|
||||
class AnthropicError extends Error {
|
||||
readonly name: string;
|
||||
readonly message: string;
|
||||
readonly cause?: Error;
|
||||
}
|
||||
```
|
||||
|
||||
All SDK errors inherit from `AnthropicError`.
|
||||
|
||||
## API Errors
|
||||
|
||||
```typescript { .api }
|
||||
class APIError<TStatus = number, THeaders = Headers, TError = any> extends AnthropicError {
|
||||
readonly status: TStatus; // HTTP status code
|
||||
readonly headers: THeaders; // Response headers
|
||||
readonly error: TError; // Error details from API
|
||||
readonly requestID: string | null; // Request ID for debugging
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
console.error('Status:', error.status);
|
||||
console.error('Message:', error.message);
|
||||
console.error('Request ID:', error.requestID);
|
||||
console.error('Error details:', error.error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP Status Errors
|
||||
|
||||
### BadRequestError (400)
|
||||
|
||||
Invalid request parameters or malformed request.
|
||||
|
||||
```typescript { .api }
|
||||
class BadRequestError extends APIError<400> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Invalid parameter values
|
||||
- Missing required fields
|
||||
- Malformed JSON
|
||||
- Invalid model name
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const message = await client.messages.create({
|
||||
model: 'invalid-model',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.BadRequestError) {
|
||||
console.error('Invalid request:', error.message);
|
||||
// "model: invalid-model is not a valid model"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AuthenticationError (401)
|
||||
|
||||
Authentication failed - invalid or missing API key.
|
||||
|
||||
```typescript { .api }
|
||||
class AuthenticationError extends APIError<401> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Missing API key
|
||||
- Invalid API key
|
||||
- Expired API key
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const client = new Anthropic({ apiKey: 'invalid-key' });
|
||||
await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.AuthenticationError) {
|
||||
console.error('Authentication failed');
|
||||
// Check API key configuration
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PermissionDeniedError (403)
|
||||
|
||||
Insufficient permissions for the requested operation.
|
||||
|
||||
```typescript { .api }
|
||||
class PermissionDeniedError extends APIError<403> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Account doesn't have access to requested feature
|
||||
- API key lacks required permissions
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.beta.messages.create({
|
||||
betas: ['restricted-feature'],
|
||||
/* ... */
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.PermissionDeniedError) {
|
||||
console.error('Access denied to beta feature');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NotFoundError (404)
|
||||
|
||||
Requested resource not found.
|
||||
|
||||
```typescript { .api }
|
||||
class NotFoundError extends APIError<404> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Invalid resource ID
|
||||
- Resource was deleted
|
||||
- Typo in endpoint or ID
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const batch = await client.messages.batches.retrieve('nonexistent-id');
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.NotFoundError) {
|
||||
console.error('Batch not found');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ConflictError (409)
|
||||
|
||||
Request conflicts with current state.
|
||||
|
||||
```typescript { .api }
|
||||
class ConflictError extends APIError<409> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Duplicate resource creation
|
||||
- Conflicting operations
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.beta.skills.create({
|
||||
name: 'existing-skill', // Already exists
|
||||
/* ... */
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.ConflictError) {
|
||||
console.error('Skill already exists');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### UnprocessableEntityError (422)
|
||||
|
||||
Request is syntactically correct but semantically invalid.
|
||||
|
||||
```typescript { .api }
|
||||
class UnprocessableEntityError extends APIError<422> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Invalid parameter combination
|
||||
- Business logic validation failure
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [
|
||||
{ role: 'assistant', content: 'Cannot start with assistant' },
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.UnprocessableEntityError) {
|
||||
console.error('Invalid message structure');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### RateLimitError (429)
|
||||
|
||||
Rate limit exceeded.
|
||||
|
||||
```typescript { .api }
|
||||
class RateLimitError extends APIError<429> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Too many requests in time window
|
||||
- Exceeded tokens per minute
|
||||
- Exceeded requests per day
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.RateLimitError) {
|
||||
console.error('Rate limited');
|
||||
|
||||
// Get retry information from headers
|
||||
const retryAfter = error.headers.get('retry-after');
|
||||
const resetTime = error.headers.get('x-ratelimit-reset');
|
||||
|
||||
console.log(`Retry after: ${retryAfter} seconds`);
|
||||
console.log(`Resets at: ${new Date(parseInt(resetTime) * 1000)}`);
|
||||
|
||||
// Implement backoff
|
||||
await new Promise(resolve => setTimeout(resolve, parseInt(retryAfter) * 1000));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### InternalServerError (5xx)
|
||||
|
||||
Server-side error.
|
||||
|
||||
```typescript { .api }
|
||||
class InternalServerError extends APIError<500 | 502 | 503 | 504> {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Temporary server issues
|
||||
- Service overload
|
||||
- Gateway errors
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.InternalServerError) {
|
||||
console.error('Server error:', error.status);
|
||||
|
||||
// Retry with exponential backoff
|
||||
await retryWithBackoff(() => client.messages.create({ /* ... */ }));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network Errors
|
||||
|
||||
### APIConnectionError
|
||||
|
||||
Failed to connect to the API.
|
||||
|
||||
```typescript { .api }
|
||||
class APIConnectionError extends AnthropicError {
|
||||
readonly cause?: Error;
|
||||
}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Network connectivity issues
|
||||
- DNS resolution failure
|
||||
- Firewall blocking requests
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIConnectionError) {
|
||||
console.error('Network error:', error.message);
|
||||
console.error('Cause:', error.cause);
|
||||
|
||||
// Check network connectivity
|
||||
// Retry with exponential backoff
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### APIConnectionTimeoutError
|
||||
|
||||
Request timed out.
|
||||
|
||||
```typescript { .api }
|
||||
class APIConnectionTimeoutError extends APIConnectionError {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Request exceeded timeout
|
||||
- Slow network connection
|
||||
- Large request/response
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await client.messages.create(
|
||||
{
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 10000,
|
||||
messages: [{ role: 'user', content: 'Long task' }],
|
||||
},
|
||||
{ timeout: 30000 } // 30 seconds
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIConnectionTimeoutError) {
|
||||
console.error('Request timed out');
|
||||
|
||||
// Use streaming for long requests
|
||||
const stream = client.messages.stream({ /* ... */ });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### APIUserAbortError
|
||||
|
||||
User aborted the request.
|
||||
|
||||
```typescript { .api }
|
||||
class APIUserAbortError extends AnthropicError {}
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- User called `stream.abort()`
|
||||
- AbortController signal triggered
|
||||
- Stream break
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const stream = client.messages.stream({ /* ... */ });
|
||||
|
||||
// User decides to cancel
|
||||
setTimeout(() => stream.abort(), 5000);
|
||||
|
||||
try {
|
||||
await stream.done();
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIUserAbortError) {
|
||||
console.log('User canceled the request');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### Basic Try-Catch
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
console.log(message.content);
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
console.error(`API Error ${error.status}:`, error.message);
|
||||
} else if (error instanceof Anthropic.APIConnectionError) {
|
||||
console.error('Connection error:', error.message);
|
||||
} else {
|
||||
console.error('Unexpected error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Comprehensive Error Handling
|
||||
|
||||
```typescript
|
||||
async function createMessage(params: Anthropic.MessageCreateParams) {
|
||||
try {
|
||||
return await client.messages.create(params);
|
||||
} catch (error) {
|
||||
// HTTP errors
|
||||
if (error instanceof Anthropic.BadRequestError) {
|
||||
console.error('Invalid request parameters');
|
||||
throw new Error('Please check your request parameters');
|
||||
}
|
||||
|
||||
if (error instanceof Anthropic.AuthenticationError) {
|
||||
console.error('Authentication failed');
|
||||
throw new Error('Please check your API key');
|
||||
}
|
||||
|
||||
if (error instanceof Anthropic.RateLimitError) {
|
||||
const retryAfter = error.headers.get('retry-after');
|
||||
console.error(`Rate limited. Retry after ${retryAfter}s`);
|
||||
await sleep(parseInt(retryAfter) * 1000);
|
||||
return createMessage(params); // Retry
|
||||
}
|
||||
|
||||
if (error instanceof Anthropic.InternalServerError) {
|
||||
console.error('Server error, retrying...');
|
||||
await sleep(5000);
|
||||
return createMessage(params); // Retry
|
||||
}
|
||||
|
||||
// Network errors
|
||||
if (error instanceof Anthropic.APIConnectionTimeoutError) {
|
||||
console.error('Request timed out');
|
||||
throw new Error('Request took too long. Try using streaming.');
|
||||
}
|
||||
|
||||
if (error instanceof Anthropic.APIConnectionError) {
|
||||
console.error('Network error');
|
||||
throw new Error('Cannot reach Anthropic API. Check your connection.');
|
||||
}
|
||||
|
||||
// User abort
|
||||
if (error instanceof Anthropic.APIUserAbortError) {
|
||||
console.log('Request was canceled');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Unknown error
|
||||
console.error('Unexpected error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Retry with Exponential Backoff
|
||||
|
||||
```typescript
|
||||
async function retryWithBackoff<T>(
|
||||
fn: () => Promise<T>,
|
||||
maxRetries = 3,
|
||||
initialDelay = 1000
|
||||
): Promise<T> {
|
||||
let lastError: Error;
|
||||
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
|
||||
// Don't retry on client errors (4xx except 429)
|
||||
if (
|
||||
error instanceof Anthropic.BadRequestError ||
|
||||
error instanceof Anthropic.AuthenticationError ||
|
||||
error instanceof Anthropic.PermissionDeniedError ||
|
||||
error instanceof Anthropic.NotFoundError
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Retry on server errors, network errors, and rate limits
|
||||
if (
|
||||
error instanceof Anthropic.RateLimitError ||
|
||||
error instanceof Anthropic.InternalServerError ||
|
||||
error instanceof Anthropic.APIConnectionError
|
||||
) {
|
||||
const delay = initialDelay * Math.pow(2, attempt);
|
||||
console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unknown error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
// Usage
|
||||
const message = await retryWithBackoff(() =>
|
||||
client.messages.create({ /* ... */ })
|
||||
);
|
||||
```
|
||||
|
||||
### Request ID Logging
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
console.error('Error details:', {
|
||||
status: error.status,
|
||||
message: error.message,
|
||||
requestID: error.requestID, // Include in support requests
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Log for debugging
|
||||
logger.error('Anthropic API error', {
|
||||
requestId: error.requestID,
|
||||
status: error.status,
|
||||
error: error.error,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
```typescript
|
||||
async function getResponse(prompt: string): Promise<string> {
|
||||
try {
|
||||
// Try with Opus first
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-opus-4-5-20250514',
|
||||
max_tokens: 2048,
|
||||
messages: [{ role: 'user', content: prompt }],
|
||||
});
|
||||
return message.content[0].text;
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.RateLimitError) {
|
||||
console.warn('Opus rate limited, falling back to Sonnet');
|
||||
|
||||
// Fall back to Sonnet
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
messages: [{ role: 'user', content: prompt }],
|
||||
});
|
||||
return message.content[0].text;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Accessing Error Details
|
||||
|
||||
### Error Object Structure
|
||||
|
||||
```typescript
|
||||
catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
// Status code
|
||||
console.log(error.status); // 400, 401, 429, etc.
|
||||
|
||||
// Error message
|
||||
console.log(error.message); // Human-readable message
|
||||
|
||||
// Request ID
|
||||
console.log(error.requestID); // 'req_...'
|
||||
|
||||
// Response headers
|
||||
console.log(error.headers.get('x-ratelimit-remaining'));
|
||||
|
||||
// Detailed error from API
|
||||
console.log(error.error); // API error object
|
||||
|
||||
// Stack trace
|
||||
console.log(error.stack);
|
||||
|
||||
// Original cause (if wrapped)
|
||||
console.log(error.cause);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Error Object
|
||||
|
||||
```typescript
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
const apiError = error.error;
|
||||
|
||||
console.log('Type:', apiError.type);
|
||||
console.log('Message:', apiError.message);
|
||||
|
||||
// Error-specific fields
|
||||
if (apiError.type === 'invalid_request_error') {
|
||||
console.log('Invalid field:', apiError.field);
|
||||
console.log('Invalid value:', apiError.value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Specific Error Handling
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Handle specific errors
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.RateLimitError) {
|
||||
// Handle rate limiting
|
||||
} else if (error instanceof Anthropic.AuthenticationError) {
|
||||
// Handle auth issues
|
||||
} else if (error instanceof Anthropic.APIError) {
|
||||
// Handle other API errors
|
||||
} else {
|
||||
// Handle unexpected errors
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad: Generic catch-all
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
console.error('Error:', error); // Not helpful
|
||||
}
|
||||
```
|
||||
|
||||
### Error Logging
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Log useful context
|
||||
try {
|
||||
const message = await client.messages.create(params);
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.APIError) {
|
||||
logger.error('Anthropic API request failed', {
|
||||
requestId: error.requestID,
|
||||
status: error.status,
|
||||
message: error.message,
|
||||
params: { model: params.model, max_tokens: params.max_tokens },
|
||||
userId: currentUserId,
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
### User-Friendly Messages
|
||||
|
||||
```typescript
|
||||
// ✅ Good: User-friendly error messages
|
||||
try {
|
||||
const message = await client.messages.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.RateLimitError) {
|
||||
return {
|
||||
error: 'Too many requests. Please try again in a few moments.',
|
||||
retryAfter: error.headers.get('retry-after'),
|
||||
};
|
||||
} else if (error instanceof Anthropic.APIConnectionError) {
|
||||
return {
|
||||
error: 'Unable to connect to service. Please check your internet connection.',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
error: 'An unexpected error occurred. Please try again later.',
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Client Configuration](./client.md) - Timeout and retry settings
|
||||
- [Messages API](./messages.md) - Message creation errors
|
||||
- [Streaming](./streaming.md) - Stream error handling
|
||||
- [Types](./types.md) - Error type definitions
|
||||
491
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/files.md
Normal file
491
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/files.md
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
# Files API (Beta)
|
||||
|
||||
The Files API allows you to upload, manage, and download files for use with Claude. Files can be referenced in messages for document analysis, image processing, and other file-based operations.
|
||||
|
||||
## Overview
|
||||
|
||||
The Files API enables:
|
||||
- Upload files for use in conversations
|
||||
- Manage uploaded files (list, retrieve metadata, delete)
|
||||
- Download files when needed
|
||||
- Reference files across multiple messages
|
||||
- Support for images and documents (PDFs)
|
||||
|
||||
**Beta Feature**: Requires `betas: ['files-api-2025-04-14']`
|
||||
|
||||
## API Reference
|
||||
|
||||
```typescript { .api }
|
||||
class Files extends APIResource {
|
||||
upload(params: FileUploadParams): APIPromise<FileMetadata>;
|
||||
list(params?: FileListParams): FileMetadataPage;
|
||||
retrieveMetadata(fileID: string, params?: FileRetrieveMetadataParams): APIPromise<FileMetadata>;
|
||||
download(fileID: string, params?: FileDownloadParams): APIPromise<Response>;
|
||||
delete(fileID: string, params?: FileDeleteParams): APIPromise<DeletedFile>;
|
||||
}
|
||||
```
|
||||
|
||||
Access via:
|
||||
|
||||
```typescript
|
||||
client.beta.files.*
|
||||
```
|
||||
|
||||
## File Types
|
||||
|
||||
```typescript { .api }
|
||||
interface FileMetadata {
|
||||
id: string;
|
||||
type: 'file';
|
||||
filename: string;
|
||||
size: number; // Bytes
|
||||
created_at: string; // ISO 8601
|
||||
}
|
||||
|
||||
interface DeletedFile {
|
||||
id: string;
|
||||
type: 'file';
|
||||
deleted: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Uploading Files
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.upload(
|
||||
params: FileUploadParams
|
||||
): APIPromise<FileMetadata>;
|
||||
|
||||
interface FileUploadParams {
|
||||
file: File | Blob | ReadStream;
|
||||
betas: ['files-api-2025-04-14'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example - Node.js:**
|
||||
|
||||
```typescript
|
||||
import Anthropic, { toFile } from '@anthropic-ai/sdk';
|
||||
import fs from 'fs';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
// Upload from file system
|
||||
const file = await client.beta.files.upload({
|
||||
file: await toFile(
|
||||
fs.createReadStream('/path/to/document.pdf'),
|
||||
'document.pdf',
|
||||
{ type: 'application/pdf' }
|
||||
),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
console.log('File ID:', file.id);
|
||||
console.log('Filename:', file.filename);
|
||||
console.log('Size:', file.size, 'bytes');
|
||||
```
|
||||
|
||||
**Example - Browser:**
|
||||
|
||||
```typescript
|
||||
// Upload from File input
|
||||
const fileInput = document.querySelector('input[type="file"]');
|
||||
const uploadedFile = fileInput.files[0];
|
||||
|
||||
const file = await client.beta.files.upload({
|
||||
file: uploadedFile,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
```
|
||||
|
||||
**Example - Buffer:**
|
||||
|
||||
```typescript
|
||||
import { toFile } from '@anthropic-ai/sdk';
|
||||
|
||||
const buffer = Buffer.from('PDF content here...');
|
||||
|
||||
const file = await client.beta.files.upload({
|
||||
file: await toFile(buffer, 'document.pdf', { type: 'application/pdf' }),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
```
|
||||
|
||||
**Example - Fetch Response:**
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://example.com/document.pdf');
|
||||
|
||||
const file = await client.beta.files.upload({
|
||||
file: response,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
```
|
||||
|
||||
## Using Uploaded Files
|
||||
|
||||
Reference uploaded files in messages:
|
||||
|
||||
```typescript
|
||||
// Upload file first
|
||||
const file = await client.beta.files.upload({
|
||||
file: await toFile(fs.createReadStream('report.pdf'), 'report.pdf', { type: 'application/pdf' }),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
// Use file in message
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'document',
|
||||
source: {
|
||||
type: 'file',
|
||||
file_id: file.id, // Reference uploaded file
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Summarize this document.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
For images:
|
||||
|
||||
```typescript
|
||||
const image = await client.beta.files.upload({
|
||||
file: await toFile(fs.createReadStream('photo.jpg'), 'photo.jpg', { type: 'image/jpeg' }),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'file',
|
||||
file_id: image.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'What is in this image?',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Listing Files
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.list(
|
||||
params?: FileListParams
|
||||
): FileMetadataPage;
|
||||
|
||||
interface FileListParams {
|
||||
before_id?: string;
|
||||
after_id?: string;
|
||||
limit?: number; // Default: 20
|
||||
betas: ['files-api-2025-04-14'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const files = await client.beta.files.list({
|
||||
limit: 50,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
for (const file of files.data) {
|
||||
console.log(`${file.filename} - ${file.size} bytes - ${file.created_at}`);
|
||||
}
|
||||
|
||||
// Auto-pagination
|
||||
for await (const file of client.beta.files.list({ betas: ['files-api-2025-04-14'] })) {
|
||||
console.log(file.id);
|
||||
}
|
||||
```
|
||||
|
||||
## Retrieving File Metadata
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.retrieveMetadata(
|
||||
fileID: string,
|
||||
params?: FileRetrieveMetadataParams
|
||||
): APIPromise<FileMetadata>;
|
||||
|
||||
interface FileRetrieveMetadataParams {
|
||||
betas: ['files-api-2025-04-14'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const metadata = await client.beta.files.retrieveMetadata('file_abc123', {
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
console.log('Filename:', metadata.filename);
|
||||
console.log('Size:', metadata.size);
|
||||
console.log('Created:', metadata.created_at);
|
||||
```
|
||||
|
||||
## Downloading Files
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.download(
|
||||
fileID: string,
|
||||
params?: FileDownloadParams
|
||||
): APIPromise<Response>;
|
||||
|
||||
interface FileDownloadParams {
|
||||
betas: ['files-api-2025-04-14'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import fs from 'fs';
|
||||
import { pipeline } from 'stream/promises';
|
||||
|
||||
const response = await client.beta.files.download('file_abc123', {
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
// Save to file system (Node.js)
|
||||
await pipeline(
|
||||
response.body,
|
||||
fs.createWriteStream('/path/to/downloaded-file.pdf')
|
||||
);
|
||||
|
||||
// Or get as buffer
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
|
||||
// Browser: Trigger download
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'file.pdf';
|
||||
a.click();
|
||||
```
|
||||
|
||||
## Deleting Files
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.delete(
|
||||
fileID: string,
|
||||
params?: FileDeleteParams
|
||||
): APIPromise<DeletedFile>;
|
||||
|
||||
interface FileDeleteParams {
|
||||
betas: ['files-api-2025-04-14'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const deleted = await client.beta.files.delete('file_abc123', {
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
console.log('Deleted:', deleted.deleted); // true
|
||||
```
|
||||
|
||||
## Complete Workflow
|
||||
|
||||
```typescript
|
||||
import Anthropic, { toFile } from '@anthropic-ai/sdk';
|
||||
import fs from 'fs';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
// 1. Upload file
|
||||
console.log('Uploading file...');
|
||||
const file = await client.beta.files.upload({
|
||||
file: await toFile(
|
||||
fs.createReadStream('document.pdf'),
|
||||
'document.pdf',
|
||||
{ type: 'application/pdf' }
|
||||
),
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
console.log('Uploaded:', file.id);
|
||||
|
||||
// 2. Use in message
|
||||
console.log('Creating message...');
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'document',
|
||||
source: {
|
||||
type: 'file',
|
||||
file_id: file.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Extract the key points from this document.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Response:', message.content[0].text);
|
||||
|
||||
// 3. Cleanup
|
||||
console.log('Cleaning up...');
|
||||
await client.beta.files.delete(file.id, {
|
||||
betas: ['files-api-2025-04-14'],
|
||||
});
|
||||
|
||||
console.log('Done!');
|
||||
```
|
||||
|
||||
## File Reuse
|
||||
|
||||
Files can be referenced multiple times:
|
||||
|
||||
```typescript
|
||||
const file = await client.beta.files.upload({ /* ... */ });
|
||||
|
||||
// Use in multiple messages
|
||||
const message1 = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'document', source: { type: 'file', file_id: file.id } },
|
||||
{ type: 'text', text: 'Summarize this.' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
|
||||
const message2 = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['files-api-2025-04-14'],
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'document', source: { type: 'file', file_id: file.id } },
|
||||
{ type: 'text', text: 'What are the main topics?' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
|
||||
// Cleanup when done
|
||||
await client.beta.files.delete(file.id, { betas: ['files-api-2025-04-14'] });
|
||||
```
|
||||
|
||||
## Supported File Types
|
||||
|
||||
### Documents
|
||||
- PDF: `application/pdf`
|
||||
- Text: `text/plain`
|
||||
|
||||
### Images
|
||||
- JPEG: `image/jpeg`
|
||||
- PNG: `image/png`
|
||||
- GIF: `image/gif`
|
||||
- WebP: `image/webp`
|
||||
|
||||
## Limitations
|
||||
|
||||
- Maximum file size varies by type
|
||||
- Files are stored temporarily
|
||||
- Access requires beta header
|
||||
- Automatic cleanup after expiration
|
||||
|
||||
## Best Practices
|
||||
|
||||
### File Management
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Clean up after use
|
||||
const file = await client.beta.files.upload({ /* ... */ });
|
||||
try {
|
||||
const message = await client.beta.messages.create({ /* ... */ });
|
||||
// Use message
|
||||
} finally {
|
||||
await client.beta.files.delete(file.id, { betas: ['files-api-2025-04-14'] });
|
||||
}
|
||||
|
||||
// ❌ Bad: Leaving files around
|
||||
const file = await client.beta.files.upload({ /* ... */ });
|
||||
const message = await client.beta.messages.create({ /* ... */ });
|
||||
// File never deleted
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const file = await client.beta.files.upload({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.BadRequestError) {
|
||||
console.error('Invalid file:', error.message);
|
||||
} else if (error instanceof Anthropic.RateLimitError) {
|
||||
console.error('Rate limited, retry later');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
### File Validation
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Validate before upload
|
||||
async function uploadFile(filePath: string) {
|
||||
const stats = await fs.promises.stat(filePath);
|
||||
|
||||
if (stats.size > 10 * 1024 * 1024) { // 10MB
|
||||
throw new Error('File too large');
|
||||
}
|
||||
|
||||
const ext = path.extname(filePath);
|
||||
if (!['.pdf', '.jpg', '.png'].includes(ext)) {
|
||||
throw new Error('Unsupported file type');
|
||||
}
|
||||
|
||||
return client.beta.files.upload({ /* ... */ });
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Using files in messages
|
||||
- [Beta Features](./beta-features.md) - Other beta capabilities
|
||||
- [Types](./types.md) - File type definitions
|
||||
434
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/index.md
Normal file
434
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/index.md
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
# Anthropic TypeScript SDK
|
||||
|
||||
The official TypeScript library for the Anthropic API, providing comprehensive access to Claude AI models. This SDK offers strongly-typed interfaces, streaming support, tool execution helpers, batch processing, and extensive beta features including code execution, computer use, and memory management.
|
||||
|
||||
## Package Information
|
||||
|
||||
- **Package Name**: @anthropic-ai/sdk
|
||||
- **Package Type**: npm
|
||||
- **Language**: TypeScript
|
||||
- **Installation**: `npm install @anthropic-ai/sdk`
|
||||
|
||||
## Core Imports
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
```
|
||||
|
||||
CommonJS:
|
||||
|
||||
```javascript
|
||||
const Anthropic = require('@anthropic-ai/sdk');
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY, // This is the default
|
||||
});
|
||||
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello, Claude' }],
|
||||
});
|
||||
|
||||
console.log(message.content);
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The SDK is organized around several key components:
|
||||
|
||||
- **Client**: Main `Anthropic` class that initializes API access and provides resource endpoints
|
||||
- **Resources**: API endpoint groups (`messages`, `models`, `beta`)
|
||||
- **Streaming**: Enhanced streaming with `MessageStream` helper providing event-based API and message accumulation
|
||||
- **Tools**: Helper functions for defining and automatically executing tools using Zod or JSON Schema
|
||||
- **Pagination**: Automatic pagination support for list endpoints with async iteration
|
||||
- **Error Handling**: Comprehensive error class hierarchy for different HTTP status codes
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Client Initialization
|
||||
|
||||
Create and configure the Anthropic API client.
|
||||
|
||||
```typescript { .api }
|
||||
class Anthropic {
|
||||
constructor(options?: ClientOptions);
|
||||
}
|
||||
|
||||
interface ClientOptions {
|
||||
apiKey?: string | ((req: Request) => string) | null;
|
||||
authToken?: string | null;
|
||||
baseURL?: string | null;
|
||||
timeout?: number; // default: 600000 (10 minutes)
|
||||
maxRetries?: number; // default: 2
|
||||
fetchOptions?: RequestInit;
|
||||
fetch?: typeof fetch;
|
||||
defaultHeaders?: Record<string, string>;
|
||||
defaultQuery?: Record<string, string>;
|
||||
dangerouslyAllowBrowser?: boolean;
|
||||
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'off';
|
||||
logger?: Logger;
|
||||
}
|
||||
```
|
||||
|
||||
[Client Configuration](./client.md)
|
||||
|
||||
### Text Completions API (Legacy)
|
||||
|
||||
Legacy text completion interface using the `\n\nHuman:` and `\n\nAssistant:` prompt format. This API is maintained for backwards compatibility, but the Messages API is recommended for new implementations.
|
||||
|
||||
```typescript { .api }
|
||||
client.completions.create(
|
||||
params: CompletionCreateParams
|
||||
): APIPromise<Completion> | APIPromise<Stream<Completion>>;
|
||||
|
||||
interface Completion {
|
||||
id: string;
|
||||
completion: string;
|
||||
model: Model;
|
||||
stop_reason: string | null;
|
||||
type: 'completion';
|
||||
}
|
||||
```
|
||||
|
||||
[Text Completions API](./completions.md)
|
||||
|
||||
### Messages API
|
||||
|
||||
Core conversational interface for interacting with Claude models. Supports both streaming and non-streaming requests, tool use, prompt caching, and extended thinking.
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.create(
|
||||
params: MessageCreateParams
|
||||
): APIPromise<Message> | APIPromise<Stream<MessageStreamEvent>>;
|
||||
|
||||
interface MessageCreateParams {
|
||||
model: string; // e.g., 'claude-sonnet-4-5-20250929'
|
||||
max_tokens: number;
|
||||
messages: MessageParam[];
|
||||
stream?: boolean;
|
||||
system?: string | SystemBlockParam[];
|
||||
temperature?: number;
|
||||
top_k?: number;
|
||||
top_p?: number;
|
||||
stop_sequences?: string[];
|
||||
metadata?: Metadata;
|
||||
tools?: Tool[];
|
||||
tool_choice?: ToolChoice;
|
||||
}
|
||||
|
||||
interface MessageParam {
|
||||
role: 'user' | 'assistant';
|
||||
content: string | ContentBlockParam[];
|
||||
}
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
type: 'message';
|
||||
role: 'assistant';
|
||||
content: ContentBlock[];
|
||||
model: string;
|
||||
stop_reason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use' | 'server_tool_use' | null;
|
||||
stop_sequence: string | null;
|
||||
usage: Usage;
|
||||
}
|
||||
```
|
||||
|
||||
[Messages API](./messages.md)
|
||||
|
||||
### Streaming
|
||||
|
||||
Enhanced streaming capabilities with event-based API, automatic message accumulation, and helper methods.
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.stream(params: MessageStreamParams): MessageStream;
|
||||
|
||||
class MessageStream {
|
||||
on(event: string, listener: Function): this;
|
||||
once(event: string, listener: Function): this;
|
||||
off(event: string, listener: Function): this;
|
||||
emitted(event: string): Promise<any>;
|
||||
done(): Promise<void>;
|
||||
finalMessage(): Promise<Message>;
|
||||
finalText(): Promise<string>;
|
||||
abort(): void;
|
||||
toReadableStream(): ReadableStream;
|
||||
}
|
||||
```
|
||||
|
||||
Events: `connect`, `streamEvent`, `text`, `inputJson`, `citation`, `thinking`, `contentBlock`, `message`, `finalMessage`, `error`, `abort`, `end`
|
||||
|
||||
[Streaming](./streaming.md)
|
||||
|
||||
### Tool Use
|
||||
|
||||
Define and execute tools (function calling) with automatic validation and execution loop.
|
||||
|
||||
```typescript { .api }
|
||||
// Tool definition
|
||||
interface Tool {
|
||||
name: string;
|
||||
description: string;
|
||||
input_schema: {
|
||||
type: 'object';
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
// Tool helpers with Zod
|
||||
import { betaZodTool } from '@anthropic-ai/sdk/helpers/zod';
|
||||
|
||||
function betaZodTool<Schema extends ZodType>(options: {
|
||||
name: string;
|
||||
inputSchema: Schema;
|
||||
description: string;
|
||||
run: (input: z.infer<Schema>) => Promise<string | BetaToolResultContentBlockParam[]> | string | BetaToolResultContentBlockParam[];
|
||||
}): BetaRunnableTool<z.infer<Schema>>;
|
||||
|
||||
// Automatic tool execution
|
||||
client.beta.messages.toolRunner(
|
||||
params: BetaToolRunnerParams
|
||||
): BetaToolRunner;
|
||||
```
|
||||
|
||||
[Tool Use and Tool Helpers](./tools.md)
|
||||
|
||||
### Message Batches
|
||||
|
||||
Batch processing for multiple messages with asynchronous result retrieval.
|
||||
|
||||
```typescript { .api }
|
||||
client.messages.batches.create(
|
||||
params: MessageBatchCreateParams
|
||||
): APIPromise<MessageBatch>;
|
||||
|
||||
client.messages.batches.list(
|
||||
params?: MessageBatchListParams
|
||||
): MessageBatchesPage;
|
||||
|
||||
client.messages.batches.results(
|
||||
messageBatchID: string
|
||||
): AsyncIterable<MessageBatchIndividualResponse>;
|
||||
|
||||
interface MessageBatch {
|
||||
id: string;
|
||||
type: 'message_batch';
|
||||
processing_status: 'in_progress' | 'canceling' | 'ended';
|
||||
request_counts: MessageBatchRequestCounts;
|
||||
ended_at: string | null;
|
||||
created_at: string;
|
||||
expires_at: string;
|
||||
archived_at: string | null;
|
||||
cancel_initiated_at: string | null;
|
||||
results_url: string | null;
|
||||
}
|
||||
```
|
||||
|
||||
[Message Batches](./batches.md)
|
||||
|
||||
### Models API
|
||||
|
||||
Retrieve information about available Claude models.
|
||||
|
||||
```typescript { .api }
|
||||
client.models.retrieve(
|
||||
modelID: string,
|
||||
params?: ModelRetrieveParams
|
||||
): APIPromise<ModelInfo>;
|
||||
|
||||
client.models.list(
|
||||
params?: ModelListParams
|
||||
): ModelInfosPage;
|
||||
|
||||
interface ModelInfo {
|
||||
type: 'model';
|
||||
id: string;
|
||||
display_name: string;
|
||||
created_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
[Models API](./models.md)
|
||||
|
||||
### Files API (Beta)
|
||||
|
||||
Upload, manage, and download files for use with file-based tools and document processing.
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.files.upload(
|
||||
params: FileUploadParams
|
||||
): APIPromise<FileMetadata>;
|
||||
|
||||
client.beta.files.list(
|
||||
params?: FileListParams
|
||||
): FileMetadataPage;
|
||||
|
||||
client.beta.files.download(
|
||||
fileID: string,
|
||||
params?: FileDownloadParams
|
||||
): APIPromise<Response>;
|
||||
|
||||
interface FileMetadata {
|
||||
id: string;
|
||||
type: 'file';
|
||||
filename: string;
|
||||
size: number;
|
||||
created_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
[Files API](./files.md)
|
||||
|
||||
### Skills API (Beta)
|
||||
|
||||
Create and manage custom skills for extended capabilities.
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.create(
|
||||
params: SkillCreateParams
|
||||
): APIPromise<SkillCreateResponse>;
|
||||
|
||||
client.beta.skills.list(
|
||||
params?: SkillListParams
|
||||
): SkillListResponsesPageCursor;
|
||||
|
||||
client.beta.skills.retrieve(
|
||||
skillID: string,
|
||||
params?: SkillRetrieveParams
|
||||
): APIPromise<SkillRetrieveResponse>;
|
||||
```
|
||||
|
||||
[Skills API](./skills.md)
|
||||
|
||||
### Beta Features
|
||||
|
||||
Access experimental and advanced features through the beta namespace.
|
||||
|
||||
Beta features include:
|
||||
- **Code Execution**: Execute Python code in sandboxed environments
|
||||
- **Computer Use**: Control computer interactions (bash, text editor, computer use tools)
|
||||
- **Extended Thinking**: Access full thinking traces from Claude
|
||||
- **MCP (Model Context Protocol)**: External tool integration
|
||||
- **Memory Tools**: Persistent memory across conversations
|
||||
- **Web Search & Fetch**: Search the web and fetch content
|
||||
- **Context Management**: Advanced context window management
|
||||
- **Structured Outputs**: JSON output with automatic parsing
|
||||
|
||||
```typescript { .api }
|
||||
// Access beta features
|
||||
client.beta.messages.create(params: {
|
||||
...MessageCreateParams,
|
||||
betas: AnthropicBeta[];
|
||||
});
|
||||
|
||||
type AnthropicBeta =
|
||||
| 'message-batches-2024-09-24'
|
||||
| 'prompt-caching-2024-07-31'
|
||||
| 'computer-use-2025-01-24'
|
||||
| 'code-execution-2025-05-22'
|
||||
| 'files-api-2025-04-14'
|
||||
| 'mcp-client-2025-04-04'
|
||||
| 'skills-2025-10-02'
|
||||
| 'context-management-2025-06-27'
|
||||
// ... and more
|
||||
;
|
||||
```
|
||||
|
||||
[Beta Features](./beta-features.md)
|
||||
|
||||
### Error Handling
|
||||
|
||||
Comprehensive error classes for different failure scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
class AnthropicError extends Error {}
|
||||
class APIError extends AnthropicError {
|
||||
readonly status: number;
|
||||
readonly headers: Headers;
|
||||
readonly error: any;
|
||||
readonly requestID: string | null;
|
||||
}
|
||||
class BadRequestError extends APIError {} // 400
|
||||
class AuthenticationError extends APIError {} // 401
|
||||
class PermissionDeniedError extends APIError {} // 403
|
||||
class NotFoundError extends APIError {} // 404
|
||||
class UnprocessableEntityError extends APIError {} // 422
|
||||
class RateLimitError extends APIError {} // 429
|
||||
class InternalServerError extends APIError {} // 5xx
|
||||
class APIConnectionError extends AnthropicError {}
|
||||
class APIConnectionTimeoutError extends APIConnectionError {}
|
||||
class APIUserAbortError extends AnthropicError {}
|
||||
```
|
||||
|
||||
[Error Handling](./errors.md)
|
||||
|
||||
### Pagination
|
||||
|
||||
Automatic pagination support for list endpoints with async iteration.
|
||||
|
||||
```typescript { .api }
|
||||
// Auto-pagination with async iteration
|
||||
for await (const item of client.messages.batches.list({ limit: 20 })) {
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
// Manual pagination
|
||||
let page = await client.messages.batches.list({ limit: 20 });
|
||||
while (page.hasNextPage()) {
|
||||
page = await page.getNextPage();
|
||||
// process page.data
|
||||
}
|
||||
|
||||
// Page interface
|
||||
interface Page<Item> {
|
||||
data: Item[];
|
||||
has_more: boolean;
|
||||
first_id: string | null;
|
||||
last_id: string | null;
|
||||
hasNextPage(): boolean;
|
||||
getNextPage(): Promise<this>;
|
||||
}
|
||||
```
|
||||
|
||||
### File Uploads
|
||||
|
||||
Convert various input types to uploadable files.
|
||||
|
||||
```typescript { .api }
|
||||
import { toFile } from '@anthropic-ai/sdk';
|
||||
|
||||
function toFile(
|
||||
value: File | Blob | Response | ReadStream | Buffer | Uint8Array | ArrayBuffer,
|
||||
name?: string,
|
||||
options?: { type?: string }
|
||||
): Promise<FileLike>;
|
||||
```
|
||||
|
||||
### Request Metadata
|
||||
|
||||
Access response headers and request IDs for debugging.
|
||||
|
||||
```typescript { .api }
|
||||
// Get response metadata
|
||||
const { data, response, request_id } = await client.messages
|
||||
.create(params)
|
||||
.withResponse();
|
||||
|
||||
console.log(request_id); // For support requests
|
||||
|
||||
// Get raw response
|
||||
const response = await client.messages.create(params).asResponse();
|
||||
```
|
||||
|
||||
## Types Reference
|
||||
|
||||
For complete type definitions including all content blocks, tool types, streaming events, and beta-specific types, see:
|
||||
|
||||
[Types Reference](./types.md)
|
||||
1025
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/messages.md
Normal file
1025
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/messages.md
Normal file
File diff suppressed because it is too large
Load diff
351
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/models.md
Normal file
351
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/models.md
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
# Models API
|
||||
|
||||
The Models API provides information about available Claude models, including their capabilities, context windows, and creation dates.
|
||||
|
||||
## API Reference
|
||||
|
||||
```typescript { .api }
|
||||
class Models extends APIResource {
|
||||
retrieve(modelID: string, params?: ModelRetrieveParams): APIPromise<ModelInfo>;
|
||||
list(params?: ModelListParams): ModelInfosPage;
|
||||
}
|
||||
```
|
||||
|
||||
Access via:
|
||||
|
||||
```typescript
|
||||
client.models.* // Standard API
|
||||
client.beta.models.* // Beta API
|
||||
```
|
||||
|
||||
## Model Information
|
||||
|
||||
```typescript { .api }
|
||||
interface ModelInfo {
|
||||
type: 'model';
|
||||
id: string;
|
||||
display_name: string;
|
||||
created_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Retrieving Model Info
|
||||
|
||||
```typescript { .api }
|
||||
client.models.retrieve(
|
||||
modelID: string,
|
||||
params?: ModelRetrieveParams
|
||||
): APIPromise<ModelInfo>;
|
||||
|
||||
interface ModelRetrieveParams {
|
||||
betas?: AnthropicBeta[];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
const model = await client.models.retrieve('claude-sonnet-4-5-20250929');
|
||||
|
||||
console.log('Model ID:', model.id);
|
||||
console.log('Display name:', model.display_name);
|
||||
console.log('Created at:', model.created_at);
|
||||
```
|
||||
|
||||
## Listing Models
|
||||
|
||||
```typescript { .api }
|
||||
client.models.list(
|
||||
params?: ModelListParams
|
||||
): ModelInfosPage;
|
||||
|
||||
interface ModelListParams {
|
||||
before_id?: string;
|
||||
after_id?: string;
|
||||
limit?: number; // Default: 20, max: 1000
|
||||
betas?: AnthropicBeta[];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
// List all available models
|
||||
const models = await client.models.list();
|
||||
|
||||
for (const model of models.data) {
|
||||
console.log(`${model.display_name} (${model.id})`);
|
||||
}
|
||||
|
||||
// Auto-pagination
|
||||
for await (const model of client.models.list({ limit: 100 })) {
|
||||
console.log(model.id);
|
||||
}
|
||||
```
|
||||
|
||||
## Available Models
|
||||
|
||||
### Claude Opus 4.5
|
||||
|
||||
```typescript
|
||||
model: 'claude-opus-4-5-20250514'
|
||||
```
|
||||
|
||||
- **Capabilities**: Most capable model, excels at complex reasoning and analysis
|
||||
- **Context Window**: 200K tokens
|
||||
- **Use Cases**: Research, complex problem solving, detailed analysis
|
||||
|
||||
### Claude Sonnet 4.5
|
||||
|
||||
```typescript
|
||||
model: 'claude-sonnet-4-5-20250929'
|
||||
```
|
||||
|
||||
- **Capabilities**: Balance of intelligence and speed
|
||||
- **Context Window**: 200K tokens
|
||||
- **Use Cases**: Most applications, coding, writing, analysis
|
||||
|
||||
### Claude 3.5 Sonnet
|
||||
|
||||
```typescript
|
||||
model: 'claude-3-5-sonnet-20241022'
|
||||
```
|
||||
|
||||
- **Capabilities**: Previous generation, still highly capable
|
||||
- **Context Window**: 200K tokens
|
||||
- **Use Cases**: General purpose applications
|
||||
|
||||
### Claude 3.5 Haiku
|
||||
|
||||
```typescript
|
||||
model: 'claude-3-5-haiku-20241022'
|
||||
```
|
||||
|
||||
- **Capabilities**: Fast and efficient for simpler tasks
|
||||
- **Context Window**: 200K tokens
|
||||
- **Use Cases**: Quick responses, simple queries, high-throughput
|
||||
|
||||
### Claude 3 Family
|
||||
|
||||
```typescript
|
||||
model: 'claude-3-opus-20240229' // Most capable (previous generation)
|
||||
model: 'claude-3-sonnet-20240229' // Balanced (previous generation)
|
||||
model: 'claude-3-haiku-20240307' // Fast (previous generation)
|
||||
```
|
||||
|
||||
## Model Selection Guide
|
||||
|
||||
Choose based on your needs:
|
||||
|
||||
### Opus 4.5 - Maximum Capability
|
||||
```typescript
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-opus-4-5-20250514',
|
||||
max_tokens: 4096,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: 'Provide a detailed analysis of quantum computing trends...',
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
Use for:
|
||||
- Complex reasoning and analysis
|
||||
- Research and academic work
|
||||
- Detailed creative writing
|
||||
- Advanced coding tasks
|
||||
|
||||
### Sonnet 4.5 - Best All-Around
|
||||
```typescript
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: 'Help me debug this code...',
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
Use for:
|
||||
- Most applications
|
||||
- Coding and development
|
||||
- Content generation
|
||||
- Data analysis
|
||||
- Customer support
|
||||
|
||||
### Haiku 3.5 - Speed and Efficiency
|
||||
```typescript
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-3-5-haiku-20241022',
|
||||
max_tokens: 1024,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: 'What is 2+2?',
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
Use for:
|
||||
- Simple queries
|
||||
- High-throughput scenarios
|
||||
- Quick responses
|
||||
- Classification tasks
|
||||
- Simple data extraction
|
||||
|
||||
## Dynamic Model Selection
|
||||
|
||||
```typescript
|
||||
function selectModel(complexity: 'simple' | 'moderate' | 'complex'): string {
|
||||
switch (complexity) {
|
||||
case 'simple':
|
||||
return 'claude-3-5-haiku-20241022';
|
||||
case 'moderate':
|
||||
return 'claude-sonnet-4-5-20250929';
|
||||
case 'complex':
|
||||
return 'claude-opus-4-5-20250514';
|
||||
}
|
||||
}
|
||||
|
||||
const model = selectModel('moderate');
|
||||
const message = await client.messages.create({
|
||||
model,
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Task...' }],
|
||||
});
|
||||
```
|
||||
|
||||
## Beta Models
|
||||
|
||||
Access beta models with beta headers:
|
||||
|
||||
```typescript
|
||||
const model = await client.beta.models.retrieve(
|
||||
'claude-sonnet-4-5-20250929',
|
||||
{
|
||||
betas: ['extended-thinking-2025-05-14'],
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Model Deprecation
|
||||
|
||||
Models are deprecated over time. The SDK will warn when using deprecated models:
|
||||
|
||||
```typescript
|
||||
// Using deprecated model
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-2.1', // Deprecated model
|
||||
max_tokens: 1024,
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
});
|
||||
|
||||
// Console warning:
|
||||
// "The model 'claude-2.1' is deprecated and will reach end-of-life on 2024-12-31
|
||||
// Please migrate to a newer model. Visit https://docs.anthropic.com/..."
|
||||
```
|
||||
|
||||
## Context Windows
|
||||
|
||||
All current models support 200K token context windows:
|
||||
|
||||
```typescript
|
||||
// You can use large context
|
||||
const message = await client.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 4096,
|
||||
system: largeSystemPrompt, // Can be very large
|
||||
messages: longConversation, // Many turns
|
||||
});
|
||||
|
||||
// Count tokens first
|
||||
const count = await client.messages.countTokens({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
system: largeSystemPrompt,
|
||||
messages: longConversation,
|
||||
});
|
||||
|
||||
console.log('Input tokens:', count.input_tokens);
|
||||
// Ensure: count.input_tokens + max_tokens <= 200000
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Model Choice
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Choose based on task
|
||||
async function processTask(task: Task) {
|
||||
let model: string;
|
||||
|
||||
if (task.requiresDeepReasoning) {
|
||||
model = 'claude-opus-4-5-20250514';
|
||||
} else if (task.isSimple) {
|
||||
model = 'claude-3-5-haiku-20241022';
|
||||
} else {
|
||||
model = 'claude-sonnet-4-5-20250929'; // Default
|
||||
}
|
||||
|
||||
return client.messages.create({ model, /* ... */ });
|
||||
}
|
||||
|
||||
// ❌ Bad: Always using most expensive model
|
||||
async function processTask(task: Task) {
|
||||
return client.messages.create({
|
||||
model: 'claude-opus-4-5-20250514', // Overkill for simple tasks
|
||||
/* ... */
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Cost Optimization
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Use appropriate model for workload
|
||||
const simpleQueries = tasks.filter(t => t.complexity === 'simple');
|
||||
const complexQueries = tasks.filter(t => t.complexity === 'complex');
|
||||
|
||||
// Process simple with Haiku (cheaper)
|
||||
for (const task of simpleQueries) {
|
||||
await client.messages.create({
|
||||
model: 'claude-3-5-haiku-20241022',
|
||||
/* ... */
|
||||
});
|
||||
}
|
||||
|
||||
// Process complex with Opus (necessary)
|
||||
for (const task of complexQueries) {
|
||||
await client.messages.create({
|
||||
model: 'claude-opus-4-5-20250514',
|
||||
/* ... */
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Model Validation
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Validate model exists
|
||||
async function createMessage(model: string, content: string) {
|
||||
try {
|
||||
await client.models.retrieve(model);
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.NotFoundError) {
|
||||
console.error(`Model ${model} not found`);
|
||||
model = 'claude-sonnet-4-5-20250929'; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
return client.messages.create({ model, /* ... */ });
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Using models for message creation
|
||||
- [Batches](./batches.md) - Model selection in batches
|
||||
- [Client](./client.md) - Client configuration
|
||||
464
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/skills.md
Normal file
464
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/skills.md
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
# Skills API (Beta)
|
||||
|
||||
The Skills API allows you to create and manage custom skills that extend Claude's capabilities with reusable, versioned components.
|
||||
|
||||
## Overview
|
||||
|
||||
Skills enable:
|
||||
- Define reusable capabilities for Claude
|
||||
- Version management for skills
|
||||
- Organized skill libraries
|
||||
- Consistent behavior across conversations
|
||||
|
||||
**Beta Feature**: Requires `betas: ['skills-2025-10-02']`
|
||||
|
||||
## API Reference
|
||||
|
||||
```typescript { .api }
|
||||
class Skills extends APIResource {
|
||||
create(params: SkillCreateParams): APIPromise<SkillCreateResponse>;
|
||||
retrieve(skillID: string, params?: SkillRetrieveParams): APIPromise<SkillRetrieveResponse>;
|
||||
list(params?: SkillListParams): SkillListResponsesPageCursor;
|
||||
delete(skillID: string, params?: SkillDeleteParams): APIPromise<SkillDeleteResponse>;
|
||||
|
||||
// Sub-resource
|
||||
versions: Versions;
|
||||
}
|
||||
|
||||
class Versions extends APIResource {
|
||||
create(skillID: string, params: VersionCreateParams): APIPromise<VersionCreateResponse>;
|
||||
retrieve(skillID: string, versionID: string, params?: VersionRetrieveParams): APIPromise<VersionRetrieveResponse>;
|
||||
list(skillID: string, params?: VersionListParams): VersionListResponsesPageCursor;
|
||||
delete(skillID: string, versionID: string, params?: VersionDeleteParams): APIPromise<VersionDeleteResponse>;
|
||||
}
|
||||
```
|
||||
|
||||
Access via:
|
||||
|
||||
```typescript
|
||||
client.beta.skills.*
|
||||
client.beta.skills.versions.*
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```typescript { .api }
|
||||
interface SkillCreateResponse {
|
||||
id: string;
|
||||
type: 'skill';
|
||||
name: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface SkillRetrieveResponse {
|
||||
id: string;
|
||||
type: 'skill';
|
||||
name: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
versions: VersionListResponse[];
|
||||
}
|
||||
|
||||
interface SkillListResponse {
|
||||
id: string;
|
||||
type: 'skill';
|
||||
name: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface SkillDeleteResponse {
|
||||
id: string;
|
||||
type: 'skill';
|
||||
deleted: boolean;
|
||||
}
|
||||
|
||||
interface VersionCreateResponse {
|
||||
id: string;
|
||||
skill_id: string;
|
||||
version: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface VersionRetrieveResponse {
|
||||
id: string;
|
||||
skill_id: string;
|
||||
version: string;
|
||||
definition: any; // Skill definition
|
||||
created_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Skills
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.create(
|
||||
params: SkillCreateParams
|
||||
): APIPromise<SkillCreateResponse>;
|
||||
|
||||
interface SkillCreateParams {
|
||||
name: string;
|
||||
description: string;
|
||||
definition: any; // Skill implementation
|
||||
betas: ['skills-2025-10-02'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
const skill = await client.beta.skills.create({
|
||||
name: 'data_analyzer',
|
||||
description: 'Analyze structured data and provide insights',
|
||||
definition: {
|
||||
// Skill implementation details
|
||||
type: 'analysis',
|
||||
capabilities: ['statistics', 'visualization', 'trend_detection'],
|
||||
},
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
console.log('Skill ID:', skill.id);
|
||||
console.log('Name:', skill.name);
|
||||
```
|
||||
|
||||
## Using Skills in Messages
|
||||
|
||||
```typescript
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2048,
|
||||
betas: ['skills-2025-10-02'],
|
||||
skills: [
|
||||
{
|
||||
type: 'skill',
|
||||
skill_id: skill.id,
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze this dataset and provide insights.',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Retrieving Skills
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.retrieve(
|
||||
skillID: string,
|
||||
params?: SkillRetrieveParams
|
||||
): APIPromise<SkillRetrieveResponse>;
|
||||
|
||||
interface SkillRetrieveParams {
|
||||
betas: ['skills-2025-10-02'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const skill = await client.beta.skills.retrieve('skill_abc123', {
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
console.log('Skill:', skill.name);
|
||||
console.log('Description:', skill.description);
|
||||
console.log('Versions:', skill.versions.length);
|
||||
```
|
||||
|
||||
## Listing Skills
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.list(
|
||||
params?: SkillListParams
|
||||
): SkillListResponsesPageCursor;
|
||||
|
||||
interface SkillListParams {
|
||||
limit?: number;
|
||||
next_page?: string;
|
||||
betas: ['skills-2025-10-02'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const skills = await client.beta.skills.list({
|
||||
limit: 20,
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
for (const skill of skills.data) {
|
||||
console.log(`${skill.name}: ${skill.description}`);
|
||||
}
|
||||
|
||||
// Auto-pagination
|
||||
for await (const skill of client.beta.skills.list({ betas: ['skills-2025-10-02'] })) {
|
||||
console.log(skill.id);
|
||||
}
|
||||
```
|
||||
|
||||
## Deleting Skills
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.delete(
|
||||
skillID: string,
|
||||
params?: SkillDeleteParams
|
||||
): APIPromise<SkillDeleteResponse>;
|
||||
|
||||
interface SkillDeleteParams {
|
||||
betas: ['skills-2025-10-02'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const deleted = await client.beta.skills.delete('skill_abc123', {
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
console.log('Deleted:', deleted.deleted); // true
|
||||
```
|
||||
|
||||
## Version Management
|
||||
|
||||
Skills support versioning for controlled updates:
|
||||
|
||||
### Creating Versions
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.versions.create(
|
||||
skillID: string,
|
||||
params: VersionCreateParams
|
||||
): APIPromise<VersionCreateResponse>;
|
||||
|
||||
interface VersionCreateParams {
|
||||
version: string;
|
||||
definition: any;
|
||||
betas: ['skills-2025-10-02'];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
// Create new version
|
||||
const version = await client.beta.skills.versions.create('skill_abc123', {
|
||||
version: '2.0.0',
|
||||
definition: {
|
||||
// Updated skill definition
|
||||
type: 'analysis',
|
||||
capabilities: ['statistics', 'visualization', 'trend_detection', 'forecasting'],
|
||||
},
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
console.log('Version created:', version.version);
|
||||
```
|
||||
|
||||
### Retrieving Versions
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.versions.retrieve(
|
||||
skillID: string,
|
||||
versionID: string,
|
||||
params?: VersionRetrieveParams
|
||||
): APIPromise<VersionRetrieveResponse>;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const version = await client.beta.skills.versions.retrieve(
|
||||
'skill_abc123',
|
||||
'2.0.0',
|
||||
{ betas: ['skills-2025-10-02'] }
|
||||
);
|
||||
|
||||
console.log('Version:', version.version);
|
||||
console.log('Definition:', version.definition);
|
||||
```
|
||||
|
||||
### Listing Versions
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.versions.list(
|
||||
skillID: string,
|
||||
params?: VersionListParams
|
||||
): VersionListResponsesPageCursor;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const versions = await client.beta.skills.versions.list('skill_abc123', {
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
for (const version of versions.data) {
|
||||
console.log(`Version ${version.version} - ${version.created_at}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting Versions
|
||||
|
||||
```typescript { .api }
|
||||
client.beta.skills.versions.delete(
|
||||
skillID: string,
|
||||
versionID: string,
|
||||
params?: VersionDeleteParams
|
||||
): APIPromise<VersionDeleteResponse>;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
await client.beta.skills.versions.delete(
|
||||
'skill_abc123',
|
||||
'1.0.0',
|
||||
{ betas: ['skills-2025-10-02'] }
|
||||
);
|
||||
```
|
||||
|
||||
## Complete Workflow
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic();
|
||||
|
||||
// 1. Create skill
|
||||
const skill = await client.beta.skills.create({
|
||||
name: 'sentiment_analyzer',
|
||||
description: 'Analyze sentiment in text',
|
||||
definition: {
|
||||
type: 'text_analysis',
|
||||
models: ['sentiment'],
|
||||
},
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
// 2. Use skill
|
||||
const message = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['skills-2025-10-02'],
|
||||
skills: [
|
||||
{
|
||||
type: 'skill',
|
||||
skill_id: skill.id,
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze the sentiment of this review: "This product is amazing!"',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Analysis:', message.content[0].text);
|
||||
|
||||
// 3. Create new version
|
||||
const version = await client.beta.skills.versions.create(skill.id, {
|
||||
version: '1.1.0',
|
||||
definition: {
|
||||
type: 'text_analysis',
|
||||
models: ['sentiment', 'emotion'], // Added emotion detection
|
||||
},
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
// 4. Use specific version
|
||||
const message2 = await client.beta.messages.create({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1024,
|
||||
betas: ['skills-2025-10-02'],
|
||||
skills: [
|
||||
{
|
||||
type: 'skill',
|
||||
skill_id: skill.id,
|
||||
version: '1.1.0', // Specific version
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Analyze sentiment and emotions.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 5. Cleanup (optional)
|
||||
// await client.beta.skills.delete(skill.id, { betas: ['skills-2025-10-02'] });
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Skill Organization
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Descriptive names and organization
|
||||
const skills = {
|
||||
dataAnalysis: await client.beta.skills.create({
|
||||
name: 'data_analysis',
|
||||
description: 'Statistical analysis and visualization',
|
||||
definition: { /* ... */ },
|
||||
betas: ['skills-2025-10-02'],
|
||||
}),
|
||||
textSummarization: await client.beta.skills.create({
|
||||
name: 'text_summarization',
|
||||
description: 'Extract key points from documents',
|
||||
definition: { /* ... */ },
|
||||
betas: ['skills-2025-10-02'],
|
||||
}),
|
||||
};
|
||||
```
|
||||
|
||||
### Version Control
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Semantic versioning
|
||||
await client.beta.skills.versions.create(skillId, {
|
||||
version: '2.1.0', // major.minor.patch
|
||||
definition: { /* ... */ },
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
|
||||
// ❌ Bad: Unclear versioning
|
||||
await client.beta.skills.versions.create(skillId, {
|
||||
version: 'v2',
|
||||
definition: { /* ... */ },
|
||||
betas: ['skills-2025-10-02'],
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const skill = await client.beta.skills.create({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof Anthropic.BadRequestError) {
|
||||
console.error('Invalid skill definition');
|
||||
} else if (error instanceof Anthropic.ConflictError) {
|
||||
console.error('Skill with this name already exists');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Messages API](./messages.md) - Using skills in messages
|
||||
- [Tools](./tools.md) - Tool use and skills
|
||||
- [Beta Features](./beta-features.md) - Other beta capabilities
|
||||
1056
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/streaming.md
Normal file
1056
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/streaming.md
Normal file
File diff suppressed because it is too large
Load diff
1189
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/tools.md
Normal file
1189
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/tools.md
Normal file
File diff suppressed because it is too large
Load diff
1084
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/types.md
Normal file
1084
.tessl/tiles/tessl/npm-anthropic-ai--sdk/docs/types.md
Normal file
File diff suppressed because it is too large
Load diff
8
.tessl/tiles/tessl/npm-anthropic-ai--sdk/tile.json
Normal file
8
.tessl/tiles/tessl/npm-anthropic-ai--sdk/tile.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "tessl/npm-anthropic-ai--sdk",
|
||||
"version": "0.70.0",
|
||||
"docs": "docs/index.md",
|
||||
"describes": "pkg:npm/%40anthropic-ai/sdk@0.70.0",
|
||||
"summary": "The official TypeScript library for the Anthropic API, providing a comprehensive client for interacting with Claude AI models.",
|
||||
"private": false
|
||||
}
|
||||
|
|
@ -0,0 +1,601 @@
|
|||
# Authentication Flows
|
||||
|
||||
MSAL Node supports multiple OAuth 2.0 authentication flows, each designed for specific application scenarios and security requirements. This document covers all supported flows with their request types, usage patterns, and security considerations.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Authorization Code Flow
|
||||
|
||||
The most secure and recommended flow for most applications, using OAuth 2.0 authorization code grant with PKCE.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request to generate authorization URL (first step)
|
||||
*/
|
||||
type AuthorizationUrlRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** URI where the authorization server will redirect after user authorization */
|
||||
redirectUri: string;
|
||||
/** Prompt behavior for user interaction */
|
||||
prompt?: PromptValue;
|
||||
/** Account to use for authentication */
|
||||
account?: AccountInfo;
|
||||
/** Login hint for pre-filling username */
|
||||
loginHint?: string;
|
||||
/** Domain hint for federated authentication */
|
||||
domainHint?: string;
|
||||
/** Additional query parameters for the authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** PKCE code challenge for security */
|
||||
codeChallenge?: string;
|
||||
/** PKCE code challenge method */
|
||||
codeChallengeMethod?: string;
|
||||
/** State parameter for CSRF protection */
|
||||
state?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Response mode (query, fragment, form_post) */
|
||||
responseMode?: ResponseMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request to exchange authorization code for tokens (second step)
|
||||
*/
|
||||
type AuthorizationCodeRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** URI where the authorization server redirected after user authorization */
|
||||
redirectUri: string;
|
||||
/** Authorization code received from authorization server */
|
||||
code: string;
|
||||
/** State parameter for CSRF protection validation */
|
||||
state?: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** PKCE code verifier for security validation */
|
||||
codeVerifier?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prompt values for user interaction
|
||||
*/
|
||||
enum PromptValue {
|
||||
/** Force user to enter credentials */
|
||||
LOGIN = "login",
|
||||
/** Show account selection screen */
|
||||
SELECT_ACCOUNT = "select_account",
|
||||
/** Request user consent */
|
||||
CONSENT = "consent",
|
||||
/** No user interaction (silent) */
|
||||
NONE = "none"
|
||||
}
|
||||
|
||||
/**
|
||||
* Response modes for authorization response
|
||||
*/
|
||||
enum ResponseMode {
|
||||
/** Return parameters in URL query string */
|
||||
QUERY = "query",
|
||||
/** Return parameters in URL fragment */
|
||||
FRAGMENT = "fragment",
|
||||
/** Return parameters via HTTP POST */
|
||||
FORM_POST = "form_post"
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { PublicClientApplication, PromptValue } from "@azure/msal-node";
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
}
|
||||
});
|
||||
|
||||
// Step 1: Generate authorization URL
|
||||
const authCodeUrlParameters = {
|
||||
scopes: ["openid", "profile", "user.read"],
|
||||
redirectUri: "http://localhost:3000/redirect",
|
||||
prompt: PromptValue.SELECT_ACCOUNT,
|
||||
state: "random-state-value", // CSRF protection
|
||||
loginHint: "user@domain.com"
|
||||
};
|
||||
|
||||
const authUrl = await pca.getAuthCodeUrl(authCodeUrlParameters);
|
||||
|
||||
// Step 2: Exchange code for tokens (after user authorization)
|
||||
const tokenRequest = {
|
||||
code: "authorization-code-from-callback",
|
||||
scopes: ["openid", "profile", "user.read"],
|
||||
redirectUri: "http://localhost:3000/redirect",
|
||||
state: "random-state-value"
|
||||
};
|
||||
|
||||
const response = await pca.acquireTokenByCode(tokenRequest);
|
||||
```
|
||||
|
||||
### Client Credentials Flow
|
||||
|
||||
App-only authentication flow for server applications and daemon apps that don't require user interaction.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for client credentials flow (app-only authentication)
|
||||
*/
|
||||
type ClientCredentialRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Skip cache and force token acquisition from authority */
|
||||
skipCache?: boolean;
|
||||
/** Client assertion JWT for certificate-based authentication */
|
||||
clientAssertion?: string | (() => string);
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ConfidentialClientApplication } from "@azure/msal-node";
|
||||
|
||||
const cca = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
clientSecret: "your-client-secret", // Or use clientCertificate
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
}
|
||||
});
|
||||
|
||||
const clientCredentialRequest = {
|
||||
scopes: ["https://graph.microsoft.com/.default"],
|
||||
skipCache: false // Use cache if available
|
||||
};
|
||||
|
||||
const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
|
||||
|
||||
// Use token for Microsoft Graph API calls
|
||||
const graphData = await fetch("https://graph.microsoft.com/v1.0/users", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${response.accessToken}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Device Code Flow
|
||||
|
||||
Authentication flow for devices with limited input capabilities or no browser.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for device code flow authentication
|
||||
*/
|
||||
type DeviceCodeRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Callback function to display device code to user */
|
||||
deviceCodeCallback: (response: DeviceCodeResponse) => void;
|
||||
/** Flag to cancel the device code flow polling */
|
||||
cancel?: boolean;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional query parameters for the device authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Response containing device code information for user
|
||||
*/
|
||||
type DeviceCodeResponse = {
|
||||
/** Device code for internal polling */
|
||||
deviceCode: string;
|
||||
/** User-friendly code to display to user */
|
||||
userCode: string;
|
||||
/** URL where user should navigate to enter the user code */
|
||||
verificationUri: string;
|
||||
/** Complete verification URL including user code (optional) */
|
||||
verificationUriComplete?: string;
|
||||
/** Number of seconds the device code is valid */
|
||||
expiresIn: number;
|
||||
/** Minimum number of seconds to wait between polling requests */
|
||||
interval: number;
|
||||
/** Message to display to user */
|
||||
message: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
const deviceCodeRequest = {
|
||||
scopes: ["user.read"],
|
||||
deviceCodeCallback: (response) => {
|
||||
console.log("=== Device Code Authentication ===");
|
||||
console.log("Please open a web browser and navigate to:");
|
||||
console.log(response.verificationUri);
|
||||
console.log("Enter the following code:");
|
||||
console.log(response.userCode);
|
||||
console.log("Or visit this URL directly:");
|
||||
console.log(response.verificationUriComplete);
|
||||
console.log(`Code expires in ${response.expiresIn} seconds`);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByDeviceCode(deviceCodeRequest);
|
||||
if (response) {
|
||||
console.log("Authentication successful!");
|
||||
console.log("Access token:", response.accessToken);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Device code flow failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### On-Behalf-Of Flow
|
||||
|
||||
Flow for middle-tier services to exchange user tokens for tokens to downstream services.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for on-behalf-of flow for middle-tier services
|
||||
*/
|
||||
type OnBehalfOfRequest = {
|
||||
/** User assertion (JWT) from the upstream service */
|
||||
oboAssertion: string;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Skip cache and force token acquisition from authority */
|
||||
skipCache?: boolean;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Express.js middleware API that calls downstream services
|
||||
app.post("/api/user-data", async (req, res) => {
|
||||
const userToken = req.headers.authorization?.replace("Bearer ", "");
|
||||
|
||||
if (!userToken) {
|
||||
return res.status(401).json({ error: "No authorization token" });
|
||||
}
|
||||
|
||||
const oboRequest = {
|
||||
oboAssertion: userToken,
|
||||
scopes: ["https://graph.microsoft.com/user.read", "https://graph.microsoft.com/mail.read"]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenOnBehalfOf(oboRequest);
|
||||
|
||||
if (response) {
|
||||
// Call Microsoft Graph on behalf of the user
|
||||
const userProfile = await fetch("https://graph.microsoft.com/v1.0/me", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${response.accessToken}`
|
||||
}
|
||||
});
|
||||
|
||||
const userData = await userProfile.json();
|
||||
res.json(userData);
|
||||
} else {
|
||||
res.status(500).json({ error: "Failed to acquire OBO token" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("OBO flow failed:", error);
|
||||
res.status(500).json({ error: "Authentication failed" });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Silent Token Acquisition
|
||||
|
||||
Acquire tokens without user interaction using cached tokens or refresh tokens.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for silent token acquisition from cache
|
||||
*/
|
||||
type SilentFlowRequest = {
|
||||
/** Account to acquire token for */
|
||||
account: AccountInfo;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Force refresh from authority instead of using cache */
|
||||
forceRefresh?: boolean;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Get cached accounts
|
||||
const accounts = await pca.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
const silentRequest = {
|
||||
account: accounts[0],
|
||||
scopes: ["user.read"],
|
||||
forceRefresh: false
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenSilent(silentRequest);
|
||||
console.log("Token acquired silently:", response.accessToken);
|
||||
} catch (error) {
|
||||
if (error.errorCode === "interaction_required") {
|
||||
// Fall back to interactive flow
|
||||
console.log("Silent acquisition failed, user interaction required");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh Token Flow
|
||||
|
||||
Exchange refresh tokens for new access tokens.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for refresh token flow
|
||||
*/
|
||||
type RefreshTokenRequest = {
|
||||
/** Refresh token to exchange for new tokens */
|
||||
refreshToken: string;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Force cache refresh for migration scenarios */
|
||||
forceCache?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Refresh token stored from previous authentication
|
||||
const storedRefreshToken = getStoredRefreshToken();
|
||||
|
||||
const refreshRequest = {
|
||||
refreshToken: storedRefreshToken,
|
||||
scopes: ["user.read", "mail.read"]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByRefreshToken(refreshRequest);
|
||||
if (response) {
|
||||
console.log("Token refreshed successfully");
|
||||
// Update stored tokens
|
||||
storeTokens(response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Token refresh failed:", error);
|
||||
// May need to prompt user for re-authentication
|
||||
}
|
||||
```
|
||||
|
||||
### Interactive Flow
|
||||
|
||||
Browser-based interactive authentication for desktop applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for interactive token acquisition with browser
|
||||
*/
|
||||
type InteractiveRequest = {
|
||||
/** Function to open browser with authorization URL */
|
||||
openBrowser: (url: string) => Promise<void>;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes?: string[];
|
||||
/** HTML template for success page */
|
||||
successTemplate?: string;
|
||||
/** HTML template for error page */
|
||||
errorTemplate?: string;
|
||||
/** Window handle for desktop applications */
|
||||
windowHandle?: Buffer;
|
||||
/** Custom loopback client for handling redirects */
|
||||
loopbackClient?: ILoopbackClient;
|
||||
/** Additional query parameters for the authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** Prompt behavior for user interaction */
|
||||
prompt?: PromptValue;
|
||||
/** Login hint for pre-filling username */
|
||||
loginHint?: string;
|
||||
/** Domain hint for federated authentication */
|
||||
domainHint?: string;
|
||||
/** Account to use for authentication */
|
||||
account?: AccountInfo;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for custom loopback client implementations
|
||||
*/
|
||||
interface ILoopbackClient {
|
||||
/** Start listening for authorization response */
|
||||
listenForAuthCode(authCodeUrl: string, port?: number): Promise<string>;
|
||||
/** Get the redirect URI for the loopback server */
|
||||
getRedirectUri(): string;
|
||||
/** Close the loopback server */
|
||||
closeServer(): void;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { open } from "open"; // npm package for opening URLs
|
||||
|
||||
const interactiveRequest = {
|
||||
scopes: ["user.read", "mail.read"],
|
||||
openBrowser: async (url: string) => {
|
||||
console.log("Opening browser...");
|
||||
await open(url);
|
||||
},
|
||||
successTemplate: `
|
||||
<html>
|
||||
<body>
|
||||
<h1>Authentication Successful!</h1>
|
||||
<p>You can close this window and return to the application.</p>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
prompt: PromptValue.SELECT_ACCOUNT
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenInteractive(interactiveRequest);
|
||||
console.log("Interactive authentication successful!");
|
||||
console.log("User:", response.account?.username);
|
||||
} catch (error) {
|
||||
console.error("Interactive authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Username/Password Flow (Deprecated)
|
||||
|
||||
Resource Owner Password Credentials (ROPC) flow - deprecated for security reasons.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for username/password flow (deprecated)
|
||||
* @deprecated Use more secure flows like authorization code or device code
|
||||
*/
|
||||
type UsernamePasswordRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Username for authentication */
|
||||
username: string;
|
||||
/** Password for authentication */
|
||||
password: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example (Not Recommended):**
|
||||
|
||||
```typescript
|
||||
// ⚠️ This flow is deprecated and should be avoided for security reasons
|
||||
const usernamePasswordRequest = {
|
||||
scopes: ["user.read"],
|
||||
username: "user@domain.com",
|
||||
password: "user-password"
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByUsernamePassword(usernamePasswordRequest);
|
||||
// This flow bypasses multi-factor authentication and modern security features
|
||||
} catch (error) {
|
||||
console.error("Username/password authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
## Flow Selection Guide
|
||||
|
||||
### Public Client Applications
|
||||
|
||||
- **Authorization Code Flow**: Web apps, SPAs with backend
|
||||
- **Interactive Flow**: Desktop applications
|
||||
- **Device Code Flow**: IoT devices, CLI tools, smart TVs
|
||||
- **Silent Flow**: Token refresh for all scenarios
|
||||
|
||||
### Confidential Client Applications
|
||||
|
||||
- **Client Credentials Flow**: Daemon apps, background services
|
||||
- **Authorization Code Flow**: Web applications with server-side code
|
||||
- **On-Behalf-Of Flow**: Middle-tier APIs calling downstream services
|
||||
- **Silent Flow**: Token refresh and cache utilization
|
||||
|
||||
### Security Considerations
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Security best practices for different flows
|
||||
*/
|
||||
type SecurityConsiderations = {
|
||||
/** Always use PKCE for authorization code flow */
|
||||
usePKCE: boolean;
|
||||
/** Validate state parameter for CSRF protection */
|
||||
validateState: boolean;
|
||||
/** Use secure storage for refresh tokens */
|
||||
secureTokenStorage: boolean;
|
||||
/** Implement proper token caching */
|
||||
implementCaching: boolean;
|
||||
/** Use certificate-based authentication when possible */
|
||||
useCertificates: boolean;
|
||||
/** Avoid username/password flow */
|
||||
avoidROPC: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Recommended Flow Selection:**
|
||||
|
||||
1. **Server Applications**: Client Credentials Flow
|
||||
2. **Web Applications**: Authorization Code Flow
|
||||
3. **Desktop Applications**: Interactive Flow or Authorization Code Flow
|
||||
4. **Mobile Applications**: Authorization Code Flow with PKCE
|
||||
5. **IoT/CLI Applications**: Device Code Flow
|
||||
6. **Middle-tier APIs**: On-Behalf-Of Flow
|
||||
7. **Background Services**: Client Credentials Flow
|
||||
|
||||
**Flows to Avoid:**
|
||||
- Username/Password Flow (deprecated and insecure)
|
||||
- Implicit Flow (less secure than authorization code with PKCE)
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
# Confidential Client Applications
|
||||
|
||||
Confidential Client Applications are designed for applications that can securely store client secrets, such as server applications, web APIs, and daemon applications. These applications support advanced authentication flows including client credentials, on-behalf-of, and certificate-based authentication.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### ConfidentialClientApplication Class
|
||||
|
||||
Main class for creating confidential client applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Confidential client application class implementing IConfidentialClientApplication interface
|
||||
* Suitable for applications that can securely store client secrets
|
||||
*/
|
||||
class ConfidentialClientApplication implements IConfidentialClientApplication {
|
||||
constructor(configuration: Configuration);
|
||||
|
||||
/** Creates the URL of the authorization request */
|
||||
getAuthCodeUrl(request: AuthorizationUrlRequest): Promise<string>;
|
||||
|
||||
/** Acquires a token by exchanging the authorization code received from the first step of OAuth 2.0 Authorization Code Flow */
|
||||
acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
|
||||
|
||||
/** Acquires a token silently when a user specifies the account the token is requested for */
|
||||
acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Acquires a token by exchanging the refresh token provided for a new set of tokens */
|
||||
acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Acquires tokens from the authority for the application (not for an end user) */
|
||||
acquireTokenByClientCredential(request: ClientCredentialRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Acquires tokens from the authority for the application */
|
||||
acquireTokenOnBehalfOf(request: OnBehalfOfRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/**
|
||||
* Acquires tokens with password grant by exchanging client applications username and password for credentials
|
||||
* @deprecated - Use a more secure flow instead
|
||||
*/
|
||||
acquireTokenByUsernamePassword(request: UsernamePasswordRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Gets the token cache for the application */
|
||||
getTokenCache(): TokenCache;
|
||||
|
||||
/** Returns the logger instance */
|
||||
getLogger(): Logger;
|
||||
|
||||
/** Replaces the default logger set in configurations with new Logger with new configurations */
|
||||
setLogger(logger: Logger): void;
|
||||
|
||||
/** Clear the cache */
|
||||
clearCache(): void;
|
||||
|
||||
/** This extensibility point is meant for Azure SDK to enhance Managed Identity support */
|
||||
SetAppTokenProvider(provider: IAppTokenProvider): void;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ConfidentialClientApplication } from "@azure/msal-node";
|
||||
|
||||
// With client secret
|
||||
const cca = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
clientSecret: "your-client-secret",
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
}
|
||||
});
|
||||
|
||||
// With client certificate
|
||||
const ccaWithCert = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
clientCertificate: {
|
||||
thumbprintSha256: "cert-thumbprint-sha256",
|
||||
privateKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
|
||||
x5c: "base64-encoded-certificate"
|
||||
},
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Client Credentials Flow
|
||||
|
||||
OAuth 2.0 client credentials flow for app-only authentication without user interaction.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for client credentials flow (app-only authentication)
|
||||
*/
|
||||
type ClientCredentialRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Skip cache and force token acquisition from authority */
|
||||
skipCache?: boolean;
|
||||
/** Client assertion JWT for certificate-based authentication */
|
||||
clientAssertion?: string | (() => string);
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Basic client credentials flow
|
||||
const clientCredentialRequest = {
|
||||
scopes: ["https://graph.microsoft.com/.default"]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
|
||||
if (response) {
|
||||
console.log("Access token:", response.accessToken);
|
||||
console.log("Token expires on:", response.expiresOn);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Client credential authentication failed:", error);
|
||||
}
|
||||
|
||||
// With custom client assertion for certificate rotation
|
||||
const clientCredentialWithAssertion = {
|
||||
scopes: ["https://graph.microsoft.com/.default"],
|
||||
clientAssertion: () => {
|
||||
// Generate JWT assertion dynamically
|
||||
return generateClientAssertion();
|
||||
},
|
||||
skipCache: true // Always get fresh token
|
||||
};
|
||||
|
||||
const response = await cca.acquireTokenByClientCredential(clientCredentialWithAssertion);
|
||||
```
|
||||
|
||||
### On-Behalf-Of Flow
|
||||
|
||||
OAuth 2.0 on-behalf-of flow for middle-tier services to act on behalf of users.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for on-behalf-of flow for middle-tier services
|
||||
*/
|
||||
type OnBehalfOfRequest = {
|
||||
/** User assertion (JWT) from the upstream service */
|
||||
oboAssertion: string;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Skip cache and force token acquisition from authority */
|
||||
skipCache?: boolean;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Middleware service receiving user token and calling downstream API
|
||||
app.post("/api/data", async (req, res) => {
|
||||
const userToken = req.headers.authorization?.replace("Bearer ", "");
|
||||
|
||||
if (!userToken) {
|
||||
return res.status(401).json({ error: "No token provided" });
|
||||
}
|
||||
|
||||
const oboRequest = {
|
||||
oboAssertion: userToken,
|
||||
scopes: ["https://graph.microsoft.com/user.read"]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenOnBehalfOf(oboRequest);
|
||||
if (response) {
|
||||
// Use the access token to call Microsoft Graph on behalf of user
|
||||
const graphResponse = await callMicrosoftGraph(response.accessToken);
|
||||
res.json(graphResponse);
|
||||
} else {
|
||||
res.status(500).json({ error: "Failed to acquire token" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("OBO flow failed:", error);
|
||||
res.status(500).json({ error: "Authentication failed" });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Authorization Code Flow (Server-Side)
|
||||
|
||||
Server-side authorization code flow for web applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Same types as public client but with confidential client capabilities
|
||||
*/
|
||||
type AuthorizationUrlRequest = {
|
||||
scopes: string[];
|
||||
redirectUri: string;
|
||||
prompt?: PromptValue;
|
||||
state?: string;
|
||||
loginHint?: string;
|
||||
domainHint?: string;
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
authority?: string;
|
||||
correlationId?: string;
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
type AuthorizationCodeRequest = {
|
||||
scopes: string[];
|
||||
redirectUri: string;
|
||||
code: string;
|
||||
state?: string;
|
||||
authority?: string;
|
||||
correlationId?: string;
|
||||
claims?: string;
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Express.js web application example
|
||||
app.get("/auth", async (req, res) => {
|
||||
const authCodeUrlParameters = {
|
||||
scopes: ["user.read"],
|
||||
redirectUri: "https://your-app.com/redirect",
|
||||
state: generateState(), // Generate CSRF protection state
|
||||
};
|
||||
|
||||
try {
|
||||
const authUrl = await cca.getAuthCodeUrl(authCodeUrlParameters);
|
||||
res.redirect(authUrl);
|
||||
} catch (error) {
|
||||
console.error("Failed to generate auth URL:", error);
|
||||
res.status(500).send("Authentication initiation failed");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/redirect", async (req, res) => {
|
||||
const { code, state } = req.query;
|
||||
|
||||
// Validate state parameter for CSRF protection
|
||||
if (!validateState(state)) {
|
||||
return res.status(400).send("Invalid state parameter");
|
||||
}
|
||||
|
||||
const tokenRequest = {
|
||||
code: code as string,
|
||||
scopes: ["user.read"],
|
||||
redirectUri: "https://your-app.com/redirect",
|
||||
state: state as string
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenByCode(tokenRequest);
|
||||
|
||||
// Store tokens securely (e.g., encrypted session)
|
||||
req.session.accessToken = response.accessToken;
|
||||
req.session.account = response.account;
|
||||
|
||||
res.redirect("/dashboard");
|
||||
} catch (error) {
|
||||
console.error("Token exchange failed:", error);
|
||||
res.status(500).send("Authentication failed");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Silent Token Acquisition
|
||||
|
||||
Silent token acquisition for confidential clients with refresh token support.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for silent token acquisition
|
||||
*/
|
||||
type SilentFlowRequest = {
|
||||
/** Account to acquire token for (optional for confidential clients) */
|
||||
account?: AccountInfo;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Force refresh from authority instead of using cache */
|
||||
forceRefresh?: boolean;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request for refresh token flow
|
||||
*/
|
||||
type RefreshTokenRequest = {
|
||||
/** Refresh token to exchange for new tokens */
|
||||
refreshToken: string;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Force cache refresh for migration scenarios */
|
||||
forceCache?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Silent token acquisition
|
||||
const silentRequest = {
|
||||
scopes: ["user.read"],
|
||||
forceRefresh: false
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenSilent(silentRequest);
|
||||
if (response) {
|
||||
console.log("Token acquired silently:", response.accessToken);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Silent acquisition failed, may need user interaction");
|
||||
}
|
||||
|
||||
// Refresh token flow
|
||||
const refreshRequest = {
|
||||
refreshToken: storedRefreshToken,
|
||||
scopes: ["user.read", "mail.read"]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenByRefreshToken(refreshRequest);
|
||||
if (response) {
|
||||
console.log("Token refreshed successfully:", response.accessToken);
|
||||
// Update stored tokens
|
||||
updateStoredTokens(response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Token refresh failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Username/Password Flow (Deprecated)
|
||||
|
||||
Resource Owner Password Credentials (ROPC) flow - deprecated for security reasons.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for username/password flow (deprecated)
|
||||
* @deprecated Use more secure flows like authorization code or device code
|
||||
*/
|
||||
type UsernamePasswordRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Username for authentication */
|
||||
username: string;
|
||||
/** Password for authentication */
|
||||
password: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example (Not Recommended):**
|
||||
|
||||
```typescript
|
||||
// This flow is deprecated and should be avoided
|
||||
const usernamePasswordRequest = {
|
||||
scopes: ["user.read"],
|
||||
username: "user@domain.com",
|
||||
password: "user-password"
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await cca.acquireTokenByUsernamePassword(usernamePasswordRequest);
|
||||
if (response) {
|
||||
console.log("Access token:", response.accessToken);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Username/password authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Azure SDK Integration
|
||||
|
||||
App token provider integration for Azure SDK services.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Interface for app token provider (Azure SDK integration)
|
||||
*/
|
||||
interface IAppTokenProvider {
|
||||
getAppTokenProviderParameters(appTokenProviderParameters: AppTokenProviderParameters): AppTokenProviderResult;
|
||||
}
|
||||
|
||||
type AppTokenProviderParameters = {
|
||||
scopes: string[];
|
||||
correlationId?: string;
|
||||
tenantId?: string;
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
type AppTokenProviderResult = {
|
||||
accessToken: string;
|
||||
expiresOnTimestamp: number;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Custom app token provider for Azure SDK
|
||||
class CustomAppTokenProvider implements IAppTokenProvider {
|
||||
getAppTokenProviderParameters(params: AppTokenProviderParameters): AppTokenProviderResult {
|
||||
// Custom token acquisition logic
|
||||
return {
|
||||
accessToken: "custom-token",
|
||||
expiresOnTimestamp: Date.now() + 3600000 // 1 hour
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Set the app token provider
|
||||
const tokenProvider = new CustomAppTokenProvider();
|
||||
cca.SetAppTokenProvider(tokenProvider);
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Certificate-based Authentication
|
||||
|
||||
```typescript { .api }
|
||||
type NodeAuthOptions = {
|
||||
clientId: string;
|
||||
authority?: string;
|
||||
clientCertificate?: {
|
||||
/** SHA-256 thumbprint of the certificate */
|
||||
thumbprintSha256?: string;
|
||||
/**
|
||||
* @deprecated Use thumbprintSha256 instead
|
||||
* SHA-1 thumbprint for backwards compatibility
|
||||
*/
|
||||
thumbprint?: string;
|
||||
/** PEM encoded private key */
|
||||
privateKey: string;
|
||||
/** Base64 encoded X.509 certificate chain */
|
||||
x5c?: string;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Client Assertion Configuration
|
||||
|
||||
```typescript { .api }
|
||||
type ClientAssertionCallback = () => string;
|
||||
|
||||
type NodeAuthOptions = {
|
||||
clientId: string;
|
||||
authority?: string;
|
||||
/** Client assertion JWT or callback function that returns JWT */
|
||||
clientAssertion?: string | ClientAssertionCallback;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Dynamic client assertion generation
|
||||
const ccaWithAssertion = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
clientAssertion: () => {
|
||||
// Generate JWT assertion with current timestamp and certificate
|
||||
return generateJWTAssertion({
|
||||
clientId: "your-client-id",
|
||||
audience: "https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token",
|
||||
certificate: loadCurrentCertificate()
|
||||
});
|
||||
},
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
}
|
||||
});
|
||||
```
|
||||
746
.tessl/tiles/tessl/npm-azure--msal-node/docs/configuration.md
Normal file
746
.tessl/tiles/tessl/npm-azure--msal-node/docs/configuration.md
Normal file
|
|
@ -0,0 +1,746 @@
|
|||
# Configuration System
|
||||
|
||||
MSAL Node provides a comprehensive hierarchical configuration system that allows fine-grained control over authentication, caching, networking, logging, and telemetry. The configuration system supports both public and confidential client applications with specialized options for different deployment scenarios.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Main Configuration
|
||||
|
||||
Core configuration structure for MSAL applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Main configuration object for MSAL applications
|
||||
*/
|
||||
type Configuration = {
|
||||
/** Authentication configuration (required) */
|
||||
auth: NodeAuthOptions;
|
||||
/** Broker configuration for native authentication */
|
||||
broker?: BrokerOptions;
|
||||
/** Cache configuration options */
|
||||
cache?: CacheOptions;
|
||||
/** System-level configuration */
|
||||
system?: NodeSystemOptions;
|
||||
/** Telemetry configuration */
|
||||
telemetry?: NodeTelemetryOptions;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { PublicClientApplication, LogLevel } from "@azure/msal-node";
|
||||
|
||||
const config: Configuration = {
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
},
|
||||
cache: {
|
||||
cachePlugin: // your cache plugin
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Info,
|
||||
loggerCallback: (level, message) => console.log(message)
|
||||
},
|
||||
networkClient: // custom network client
|
||||
},
|
||||
telemetry: {
|
||||
application: {
|
||||
appName: "MyApp",
|
||||
appVersion: "1.0.0"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pca = new PublicClientApplication(config);
|
||||
```
|
||||
|
||||
### Authentication Configuration
|
||||
|
||||
Core authentication settings including client credentials, authority, and protocol options.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Authentication configuration options
|
||||
*/
|
||||
type NodeAuthOptions = {
|
||||
/** Application (client) ID from app registration */
|
||||
clientId: string;
|
||||
/** Authority URL - defaults to https://login.microsoftonline.com/common */
|
||||
authority?: string;
|
||||
/** Client secret for confidential client applications */
|
||||
clientSecret?: string;
|
||||
/** Client assertion JWT or callback function for certificate-based auth */
|
||||
clientAssertion?: string | ClientAssertionCallback;
|
||||
/** Client certificate configuration for certificate-based auth */
|
||||
clientCertificate?: {
|
||||
/**
|
||||
* @deprecated Use thumbprintSha256 property instead
|
||||
* SHA-1 thumbprint for backwards compatibility with older ADFS versions
|
||||
*/
|
||||
thumbprint?: string;
|
||||
/** SHA-256 thumbprint of the certificate (recommended) */
|
||||
thumbprintSha256?: string;
|
||||
/** PEM encoded private key */
|
||||
privateKey: string;
|
||||
/** X.509 certificate chain (optional) */
|
||||
x5c?: string;
|
||||
};
|
||||
/** Known authorities for B2C and ADFS scenarios */
|
||||
knownAuthorities?: Array<string>;
|
||||
/** Cloud discovery metadata for custom clouds */
|
||||
cloudDiscoveryMetadata?: string;
|
||||
/** Authority metadata for custom authorities */
|
||||
authorityMetadata?: string;
|
||||
/** Client capabilities for conditional access */
|
||||
clientCapabilities?: Array<string>;
|
||||
/** Protocol mode (AAD or OIDC) */
|
||||
protocolMode?: ProtocolMode;
|
||||
/** Azure cloud configuration options */
|
||||
azureCloudOptions?: AzureCloudOptions;
|
||||
/** Skip authority metadata cache for testing */
|
||||
skipAuthorityMetadataCache?: boolean;
|
||||
/**
|
||||
* @deprecated This flag is deprecated and will be removed in the next major version
|
||||
* All extra query params will be encoded by default
|
||||
*/
|
||||
encodeExtraQueryParams?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function type for client assertions
|
||||
*/
|
||||
type ClientAssertionCallback = () => string;
|
||||
|
||||
/**
|
||||
* Protocol modes supported by MSAL
|
||||
*/
|
||||
enum ProtocolMode {
|
||||
/** Azure Active Directory protocol */
|
||||
AAD = "AAD",
|
||||
/** OpenID Connect protocol */
|
||||
OIDC = "OIDC"
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Examples:**
|
||||
|
||||
```typescript
|
||||
// Basic public client configuration
|
||||
const publicClientAuth: NodeAuthOptions = {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
};
|
||||
|
||||
// Confidential client with client secret
|
||||
const confidentialClientAuth: NodeAuthOptions = {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
clientSecret: "your-client-secret",
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
};
|
||||
|
||||
// Certificate-based authentication
|
||||
const certificateAuth: NodeAuthOptions = {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
clientCertificate: {
|
||||
thumbprintSha256: "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99",
|
||||
privateKey: `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
|
||||
-----END PRIVATE KEY-----`,
|
||||
x5c: "base64-encoded-certificate-chain"
|
||||
},
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
};
|
||||
|
||||
// Dynamic client assertion
|
||||
const dynamicAssertionAuth: NodeAuthOptions = {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
clientAssertion: () => {
|
||||
// Generate JWT assertion dynamically
|
||||
return generateJWTAssertion();
|
||||
},
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
};
|
||||
|
||||
// B2C configuration
|
||||
const b2cAuth: NodeAuthOptions = {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
authority: "https://yourtenant.b2clogin.com/yourtenant.onmicrosoft.com/B2C_1_signupsignin1",
|
||||
knownAuthorities: ["yourtenant.b2clogin.com"],
|
||||
protocolMode: ProtocolMode.OIDC
|
||||
};
|
||||
```
|
||||
|
||||
### Azure Cloud Configuration
|
||||
|
||||
Configuration for different Azure cloud environments.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Azure cloud instance enumeration
|
||||
*/
|
||||
enum AzureCloudInstance {
|
||||
/** No specific cloud (use authority as-is) */
|
||||
None = "",
|
||||
/** Azure Public Cloud */
|
||||
AzurePublic = "https://login.microsoftonline.com",
|
||||
/** Azure US Government Cloud */
|
||||
AzurePpope = "https://login.microsoftonline.us",
|
||||
/** Azure China Cloud */
|
||||
AzureChina = "https://login.chinacloudapi.cn",
|
||||
/** Azure Germany Cloud */
|
||||
AzureGermany = "https://login.microsoftonline.de"
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure cloud configuration options
|
||||
*/
|
||||
type AzureCloudOptions = {
|
||||
/** Azure cloud instance */
|
||||
azureCloudInstance: AzureCloudInstance;
|
||||
/** Tenant ID or domain */
|
||||
tenant: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Azure US Government configuration
|
||||
const govCloudAuth: NodeAuthOptions = {
|
||||
clientId: "your-client-id",
|
||||
azureCloudOptions: {
|
||||
azureCloudInstance: AzureCloudInstance.AzurePpope,
|
||||
tenant: "your-tenant-id"
|
||||
}
|
||||
};
|
||||
|
||||
// Azure China configuration
|
||||
const chinaCloudAuth: NodeAuthOptions = {
|
||||
clientId: "your-client-id",
|
||||
azureCloudOptions: {
|
||||
azureCloudInstance: AzureCloudInstance.AzureChina,
|
||||
tenant: "your-tenant-id"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### System Configuration
|
||||
|
||||
System-level configuration for networking, logging, and operational settings.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* System configuration options
|
||||
*/
|
||||
type NodeSystemOptions = {
|
||||
/** Logger configuration */
|
||||
loggerOptions?: LoggerOptions;
|
||||
/** Custom network client implementation */
|
||||
networkClient?: INetworkModule;
|
||||
/** Proxy URL for network requests */
|
||||
proxyUrl?: string;
|
||||
/** Custom HTTP agent options */
|
||||
customAgentOptions?: http.AgentOptions | https.AgentOptions;
|
||||
/** Disable internal request retries */
|
||||
disableInternalRetries?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Logger configuration options
|
||||
*/
|
||||
type LoggerOptions = {
|
||||
/** Callback function for log messages */
|
||||
loggerCallback?: (level: LogLevel, message: string, containsPii: boolean) => void;
|
||||
/** Enable PII (personally identifiable information) logging */
|
||||
piiLoggingEnabled?: boolean;
|
||||
/** Minimum log level to output */
|
||||
logLevel?: LogLevel;
|
||||
/** Correlation ID for log correlation */
|
||||
correlationId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
enum LogLevel {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Info = 2,
|
||||
Verbose = 3,
|
||||
Trace = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* Network module interface for custom HTTP clients
|
||||
*/
|
||||
interface INetworkModule {
|
||||
/** Send HTTP request */
|
||||
sendGetRequestAsync<T>(
|
||||
url: string,
|
||||
options?: NetworkRequestOptions,
|
||||
cancellationToken?: number
|
||||
): Promise<NetworkResponse<T>>;
|
||||
|
||||
/** Send HTTP POST request */
|
||||
sendPostRequestAsync<T>(
|
||||
url: string,
|
||||
options?: NetworkRequestOptions
|
||||
): Promise<NetworkResponse<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network request options
|
||||
*/
|
||||
type NetworkRequestOptions = {
|
||||
/** Request headers */
|
||||
headers?: Record<string, string>;
|
||||
/** Request body */
|
||||
body?: string;
|
||||
/** Request timeout in milliseconds */
|
||||
timeout?: number;
|
||||
/** Proxy URL */
|
||||
proxyUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Network response structure
|
||||
*/
|
||||
type NetworkResponse<T> = {
|
||||
/** Response headers */
|
||||
headers: Record<string, string>;
|
||||
/** Response body */
|
||||
body: T;
|
||||
/** HTTP status code */
|
||||
status: number;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Examples:**
|
||||
|
||||
```typescript
|
||||
import https from "https";
|
||||
|
||||
// Custom logger configuration
|
||||
const systemWithLogger: NodeSystemOptions = {
|
||||
loggerOptions: {
|
||||
loggerCallback: (level, message, containsPii) => {
|
||||
if (!containsPii) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const levelName = LogLevel[level];
|
||||
console.log(`[${timestamp}] ${levelName}: ${message}`);
|
||||
}
|
||||
},
|
||||
logLevel: LogLevel.Verbose,
|
||||
piiLoggingEnabled: false
|
||||
}
|
||||
};
|
||||
|
||||
// Proxy configuration
|
||||
const systemWithProxy: NodeSystemOptions = {
|
||||
proxyUrl: "http://proxy.company.com:8080",
|
||||
customAgentOptions: {
|
||||
timeout: 30000,
|
||||
keepAlive: true
|
||||
}
|
||||
};
|
||||
|
||||
// Custom HTTPS agent with certificate validation
|
||||
const systemWithCustomAgent: NodeSystemOptions = {
|
||||
customAgentOptions: {
|
||||
ca: [fs.readFileSync("./ca-cert.pem")],
|
||||
rejectUnauthorized: true,
|
||||
timeout: 10000
|
||||
} as https.AgentOptions
|
||||
};
|
||||
|
||||
// Disable retries for testing
|
||||
const systemWithoutRetries: NodeSystemOptions = {
|
||||
disableInternalRetries: true,
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Trace,
|
||||
loggerCallback: (level, message) => console.log(message)
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Cache Configuration
|
||||
|
||||
Configuration options for token caching behavior.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Cache configuration options
|
||||
*/
|
||||
type CacheOptions = {
|
||||
/** Cache plugin for custom storage backends */
|
||||
cachePlugin?: ICachePlugin;
|
||||
/**
|
||||
* @deprecated claims-based-caching functionality will be removed in the next version
|
||||
*/
|
||||
claimsBasedCachingEnabled?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache plugin interface
|
||||
*/
|
||||
interface ICachePlugin {
|
||||
/** Called before cache access - load cache data */
|
||||
beforeCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
|
||||
/** Called after cache access - persist cache data if changed */
|
||||
afterCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// File-based cache plugin
|
||||
class FileCachePlugin implements ICachePlugin {
|
||||
private cacheFilePath: string;
|
||||
|
||||
constructor(cacheFilePath: string) {
|
||||
this.cacheFilePath = cacheFilePath;
|
||||
}
|
||||
|
||||
async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
try {
|
||||
const cacheData = await fs.readFile(this.cacheFilePath, "utf8");
|
||||
await cacheContext.tokenCache.deserialize(cacheData);
|
||||
} catch (error) {
|
||||
// Cache file doesn't exist - start with empty cache
|
||||
}
|
||||
}
|
||||
|
||||
async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
if (cacheContext.hasChanged) {
|
||||
const serializedCache = await cacheContext.tokenCache.serialize();
|
||||
await fs.writeFile(this.cacheFilePath, serializedCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cacheConfig: CacheOptions = {
|
||||
cachePlugin: new FileCachePlugin("./token-cache.json")
|
||||
};
|
||||
```
|
||||
|
||||
### Broker Configuration
|
||||
|
||||
Configuration for native broker integration (Windows, macOS, Linux).
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Broker configuration options
|
||||
* Note: These options are only available for PublicClientApplications using Authorization Code Flow
|
||||
*/
|
||||
type BrokerOptions = {
|
||||
/** Native broker plugin implementation */
|
||||
nativeBrokerPlugin?: INativeBrokerPlugin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Native broker plugin interface
|
||||
*/
|
||||
interface INativeBrokerPlugin {
|
||||
/** Check if broker is available on current platform */
|
||||
isBrokerAvailable(): Promise<boolean>;
|
||||
/** Acquire token using native broker */
|
||||
acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult>;
|
||||
/** Acquire token interactively using native broker */
|
||||
acquireTokenInteractive(request: InteractiveRequest): Promise<AuthenticationResult>;
|
||||
/** Get accounts from native broker */
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
/** Remove account from native broker */
|
||||
removeAccount(account: AccountInfo): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Broker configuration (requires msal-node-extensions package)
|
||||
import { NativeBrokerPlugin } from "msal-node-extensions";
|
||||
|
||||
const brokerConfig: BrokerOptions = {
|
||||
nativeBrokerPlugin: new NativeBrokerPlugin()
|
||||
};
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id"
|
||||
},
|
||||
broker: brokerConfig
|
||||
});
|
||||
```
|
||||
|
||||
### Telemetry Configuration
|
||||
|
||||
Configuration for application telemetry and usage analytics.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Telemetry configuration options
|
||||
*/
|
||||
type NodeTelemetryOptions = {
|
||||
/** Application telemetry information */
|
||||
application?: ApplicationTelemetry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Application telemetry information
|
||||
*/
|
||||
type ApplicationTelemetry = {
|
||||
/** Application name */
|
||||
appName: string;
|
||||
/** Application version */
|
||||
appVersion: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
const telemetryConfig: NodeTelemetryOptions = {
|
||||
application: {
|
||||
appName: "MyNodeApp",
|
||||
appVersion: "2.1.0"
|
||||
}
|
||||
};
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id"
|
||||
},
|
||||
telemetry: telemetryConfig
|
||||
});
|
||||
```
|
||||
|
||||
### Managed Identity Configuration
|
||||
|
||||
Specialized configuration for Azure Managed Identity applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Configuration for managed identity applications
|
||||
*/
|
||||
type ManagedIdentityConfiguration = {
|
||||
/** Client capabilities for conditional access */
|
||||
clientCapabilities?: Array<string>;
|
||||
/** Parameters for user-assigned managed identity */
|
||||
managedIdentityIdParams?: ManagedIdentityIdParams;
|
||||
/** System configuration options */
|
||||
system?: NodeSystemOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters for user-assigned managed identity
|
||||
*/
|
||||
type ManagedIdentityIdParams = {
|
||||
/** Client ID of the user-assigned managed identity */
|
||||
userAssignedClientId?: string;
|
||||
/** Resource ID of the user-assigned managed identity */
|
||||
userAssignedResourceId?: string;
|
||||
/** Object ID of the user-assigned managed identity */
|
||||
userAssignedObjectId?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ManagedIdentityApplication } from "@azure/msal-node";
|
||||
|
||||
// System-assigned managed identity
|
||||
const systemAssignedConfig: ManagedIdentityConfiguration = {};
|
||||
|
||||
// User-assigned managed identity by client ID
|
||||
const userAssignedConfig: ManagedIdentityConfiguration = {
|
||||
managedIdentityIdParams: {
|
||||
userAssignedClientId: "12345678-1234-1234-1234-123456789012"
|
||||
},
|
||||
clientCapabilities: ["CP1"], // Conditional access capability
|
||||
system: {
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Info,
|
||||
loggerCallback: (level, message) => console.log(message)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mia = new ManagedIdentityApplication(userAssignedConfig);
|
||||
```
|
||||
|
||||
## Configuration Validation and Defaults
|
||||
|
||||
### Default Values
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Default configuration values applied automatically
|
||||
*/
|
||||
const DEFAULT_AUTH_OPTIONS: Required<NodeAuthOptions> = {
|
||||
clientId: "", // Must be provided
|
||||
authority: "https://login.microsoftonline.com/common",
|
||||
clientSecret: "",
|
||||
clientAssertion: "",
|
||||
clientCertificate: {
|
||||
thumbprint: "",
|
||||
thumbprintSha256: "",
|
||||
privateKey: "",
|
||||
x5c: ""
|
||||
},
|
||||
knownAuthorities: [],
|
||||
cloudDiscoveryMetadata: "",
|
||||
authorityMetadata: "",
|
||||
clientCapabilities: [],
|
||||
protocolMode: ProtocolMode.AAD,
|
||||
azureCloudOptions: {
|
||||
azureCloudInstance: AzureCloudInstance.None,
|
||||
tenant: ""
|
||||
},
|
||||
skipAuthorityMetadataCache: false,
|
||||
encodeExtraQueryParams: false
|
||||
};
|
||||
|
||||
const DEFAULT_SYSTEM_OPTIONS: Required<NodeSystemOptions> = {
|
||||
loggerOptions: {
|
||||
loggerCallback: () => {}, // No-op by default
|
||||
piiLoggingEnabled: false,
|
||||
logLevel: LogLevel.Info
|
||||
},
|
||||
networkClient: new HttpClient(),
|
||||
proxyUrl: "",
|
||||
customAgentOptions: {},
|
||||
disableInternalRetries: false
|
||||
};
|
||||
```
|
||||
|
||||
### Configuration Validation
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Configuration validation function (internal)
|
||||
*/
|
||||
function buildAppConfiguration(config: Configuration): NodeConfiguration;
|
||||
|
||||
/**
|
||||
* Validation rules applied during configuration
|
||||
*/
|
||||
type ConfigurationValidation = {
|
||||
/** Client ID is required and must be valid GUID */
|
||||
clientIdRequired: boolean;
|
||||
/** Certificate thumbprint validation (SHA-1 or SHA-256 required) */
|
||||
certificateThumbprintRequired: boolean;
|
||||
/** Known authorities format validation */
|
||||
knownAuthoritiesFormat: boolean;
|
||||
/** Protocol mode compatibility */
|
||||
protocolModeCompatibility: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Configuration with validation
|
||||
try {
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "invalid-client-id" // This will cause validation error
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Configuration validation failed:", error.message);
|
||||
}
|
||||
|
||||
// Valid configuration
|
||||
const validConfig: Configuration = {
|
||||
auth: {
|
||||
clientId: "12345678-1234-1234-1234-123456789012",
|
||||
authority: "https://login.microsoftonline.com/common",
|
||||
clientCapabilities: ["CP1", "CP2"]
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Warning,
|
||||
loggerCallback: (level, message, containsPii) => {
|
||||
if (!containsPii && level <= LogLevel.Warning) {
|
||||
console.warn(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pca = new PublicClientApplication(validConfig);
|
||||
```
|
||||
|
||||
## Environment-Specific Configurations
|
||||
|
||||
### Development Configuration
|
||||
|
||||
```typescript
|
||||
const developmentConfig: Configuration = {
|
||||
auth: {
|
||||
clientId: "dev-client-id",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Verbose,
|
||||
loggerCallback: (level, message) => console.log(message),
|
||||
piiLoggingEnabled: false
|
||||
},
|
||||
disableInternalRetries: false
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Production Configuration
|
||||
|
||||
```typescript
|
||||
const productionConfig: Configuration = {
|
||||
auth: {
|
||||
clientId: process.env.MSAL_CLIENT_ID!,
|
||||
clientSecret: process.env.MSAL_CLIENT_SECRET,
|
||||
authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Error,
|
||||
loggerCallback: (level, message, containsPii) => {
|
||||
if (!containsPii) {
|
||||
// Log to production logging system
|
||||
logger.log(level, message);
|
||||
}
|
||||
}
|
||||
},
|
||||
proxyUrl: process.env.HTTPS_PROXY,
|
||||
disableInternalRetries: false
|
||||
},
|
||||
cache: {
|
||||
cachePlugin: new ProductionCachePlugin()
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Testing Configuration
|
||||
|
||||
```typescript
|
||||
const testConfig: Configuration = {
|
||||
auth: {
|
||||
clientId: "test-client-id",
|
||||
authority: "https://login.microsoftonline.com/common",
|
||||
skipAuthorityMetadataCache: true
|
||||
},
|
||||
system: {
|
||||
disableInternalRetries: true,
|
||||
loggerOptions: {
|
||||
logLevel: LogLevel.Error,
|
||||
loggerCallback: () => {} // Silent in tests
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
653
.tessl/tiles/tessl/npm-azure--msal-node/docs/error-handling.md
Normal file
653
.tessl/tiles/tessl/npm-azure--msal-node/docs/error-handling.md
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
# Error Handling
|
||||
|
||||
MSAL Node provides comprehensive error handling with specific error classes, detailed error codes, and structured error information. The error system helps developers diagnose authentication issues and implement appropriate error recovery strategies.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Error Class Hierarchy
|
||||
|
||||
MSAL Node uses a hierarchical error system with specific error classes for different scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Base authentication error class (from @azure/msal-common)
|
||||
*/
|
||||
class AuthError extends Error {
|
||||
/** Error code identifier */
|
||||
errorCode: string;
|
||||
/** Detailed error message */
|
||||
errorMessage: string;
|
||||
/** Sub-error code for additional context */
|
||||
subError: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId: string;
|
||||
|
||||
constructor(errorCode: string, errorMessage?: string, suberror?: string);
|
||||
|
||||
/** Create error message with error codes */
|
||||
createPostRequestFailed(endpoint: string, serverErrorMsg: string): string;
|
||||
/** Create error message for GET request failures */
|
||||
createGetRequestFailed(endpoint: string, serverErrorMsg: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-side authentication errors (from @azure/msal-common)
|
||||
*/
|
||||
class ClientAuthError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client configuration errors (from @azure/msal-common)
|
||||
*/
|
||||
class ClientConfigurationError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side authentication errors (from @azure/msal-common)
|
||||
*/
|
||||
class ServerError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage?: string, subError?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interaction required errors (from @azure/msal-common)
|
||||
*/
|
||||
class InteractionRequiredAuthError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage?: string, subError?: string);
|
||||
}
|
||||
```
|
||||
|
||||
### Node-Specific Errors
|
||||
|
||||
MSAL Node provides additional error classes for Node.js-specific scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Node.js specific authentication errors
|
||||
*/
|
||||
class NodeAuthError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage?: string);
|
||||
|
||||
/** Create error for invalid loopback server address type */
|
||||
static createInvalidLoopbackAddressTypeError(): NodeAuthError;
|
||||
/** Create error when unable to load redirect URL */
|
||||
static createUnableToLoadRedirectUrlError(): NodeAuthError;
|
||||
/** Create error when no auth code is found in response */
|
||||
static createNoAuthCodeInResponseError(): NodeAuthError;
|
||||
/** Create error when no loopback server exists */
|
||||
static createNoLoopbackServerExistsError(): NodeAuthError;
|
||||
/** Create error when loopback server already exists */
|
||||
static createLoopbackServerAlreadyExistsError(): NodeAuthError;
|
||||
/** Create error for loopback server timeout */
|
||||
static createLoopbackServerTimeoutError(): NodeAuthError;
|
||||
/** Create error for invalid state parameter */
|
||||
static createStateNotFoundError(): NodeAuthError;
|
||||
/** Create error when client certificate thumbprint is missing */
|
||||
static createThumbprintMissingError(): NodeAuthError;
|
||||
/** Create error when redirect URI is not supported */
|
||||
static createRedirectUriNotSupportedError(): NodeAuthError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Managed Identity specific errors
|
||||
*/
|
||||
class ManagedIdentityError extends AuthError {
|
||||
constructor(errorCode: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create managed identity errors
|
||||
*/
|
||||
function createManagedIdentityError(errorCode: string): ManagedIdentityError;
|
||||
```
|
||||
|
||||
### Error Codes
|
||||
|
||||
Comprehensive error codes for different error scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Base authentication error codes
|
||||
*/
|
||||
const AuthErrorCodes = {
|
||||
/** Unexpected error occurred */
|
||||
unexpectedError: "unexpected_error",
|
||||
/** POST request failed */
|
||||
postRequestFailed: "post_request_failed",
|
||||
/** GET request failed */
|
||||
get_request_failed: "get_request_failed",
|
||||
/** Network request failed */
|
||||
networkError: "network_error",
|
||||
/** Request timeout */
|
||||
requestTimeout: "request_timeout",
|
||||
/** Invalid JSON in response */
|
||||
jsonParseError: "json_parse_error",
|
||||
/** Invalid authority */
|
||||
invalidAuthority: "invalid_authority"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Client authentication error codes
|
||||
*/
|
||||
const ClientAuthErrorCodes = {
|
||||
/** Multiple matching accounts found */
|
||||
multipleMatchingAccounts: "multiple_matching_accounts",
|
||||
/** Multiple matching tokens found */
|
||||
multipleMatchingTokens: "multiple_matching_tokens",
|
||||
/** No matching account found */
|
||||
noAccountFound: "no_account_found",
|
||||
/** No matching token found */
|
||||
noTokenFound: "no_token_found",
|
||||
/** Token request cannot be made */
|
||||
tokenRequestCannotBeMade: "token_request_cannot_be_made",
|
||||
/** Silent token request failed */
|
||||
silentTokenRequestFailure: "silent_token_request_failure"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Client configuration error codes
|
||||
*/
|
||||
const ClientConfigurationErrorCodes = {
|
||||
/** Invalid client ID */
|
||||
invalidClientId: "invalid_client_id",
|
||||
/** Invalid authority */
|
||||
invalidAuthority: "invalid_authority",
|
||||
/** Invalid redirect URI */
|
||||
invalidRedirectUri: "invalid_redirect_uri",
|
||||
/** Invalid request */
|
||||
invalidRequest: "invalid_request",
|
||||
/** Invalid scope */
|
||||
invalidScope: "invalid_scope",
|
||||
/** Invalid claims */
|
||||
invalidClaims: "invalid_claims"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Interaction required error codes
|
||||
*/
|
||||
const InteractionRequiredAuthErrorCodes = {
|
||||
/** User interaction required */
|
||||
interactionRequired: "interaction_required",
|
||||
/** User consent required */
|
||||
consentRequired: "consent_required",
|
||||
/** User login required */
|
||||
loginRequired: "login_required",
|
||||
/** Account selection required */
|
||||
accountSelectionRequired: "account_selection_required",
|
||||
/** Basic action required */
|
||||
basicActionRequired: "basic_action_required",
|
||||
/** Additional action required */
|
||||
additionalActionRequired: "additional_action_required"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Node-specific error codes
|
||||
*/
|
||||
const NodeAuthErrorCodes = {
|
||||
/** Invalid loopback server address type */
|
||||
invalidLoopbackAddressType: "invalid_loopback_server_address_type",
|
||||
/** Unable to load redirect URL */
|
||||
unableToLoadRedirectUrl: "unable_to_load_redirectUrl",
|
||||
/** No auth code in response */
|
||||
noAuthCodeInResponse: "no_auth_code_in_response",
|
||||
/** No loopback server exists */
|
||||
noLoopbackServerExists: "no_loopback_server_exists",
|
||||
/** Loopback server already exists */
|
||||
loopbackServerAlreadyExists: "loopback_server_already_exists",
|
||||
/** Loopback server timeout */
|
||||
loopbackServerTimeout: "loopback_server_timeout",
|
||||
/** State parameter not found */
|
||||
stateNotFound: "state_not_found",
|
||||
/** Thumbprint missing from client certificate */
|
||||
thumbprintMissing: "thumbprint_missing_from_client_certificate",
|
||||
/** Redirect URI not supported */
|
||||
redirectUriNotSupported: "redirect_uri_not_supported"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Managed Identity error codes
|
||||
*/
|
||||
const ManagedIdentityErrorCodes = {
|
||||
/** Invalid file extension */
|
||||
invalidFileExtension: "invalid_file_extension",
|
||||
/** Invalid file path */
|
||||
invalidFilePath: "invalid_file_path",
|
||||
/** Invalid managed identity ID type */
|
||||
invalidManagedIdentityIdType: "invalid_managed_identity_id_type",
|
||||
/** Invalid secret */
|
||||
invalidSecret: "invalid_secret",
|
||||
/** Missing client ID */
|
||||
missingId: "missing_client_id",
|
||||
/** Network unavailable */
|
||||
networkUnavailable: "network_unavailable",
|
||||
/** Platform not supported */
|
||||
platformNotSupported: "platform_not_supported",
|
||||
/** Unable to create Azure Arc */
|
||||
unableToCreateAzureArc: "unable_to_create_azure_arc",
|
||||
/** Unable to create Cloud Shell */
|
||||
unableToCreateCloudShell: "unable_to_create_cloud_shell",
|
||||
/** Unable to create source */
|
||||
unableToCreateSource: "unable_to_create_source",
|
||||
/** Unable to read secret file */
|
||||
unableToReadSecretFile: "unable_to_read_secret_file",
|
||||
/** URL parse error */
|
||||
urlParseError: "url_parse_error",
|
||||
/** User assigned not available at runtime */
|
||||
userAssignedNotAvailableAtRuntime: "user_assigned_not_available_at_runtime",
|
||||
/** WWW-Authenticate header missing */
|
||||
wwwAuthenticateHeaderMissing: "www_authenticate_header_missing",
|
||||
/** WWW-Authenticate header unsupported format */
|
||||
wwwAuthenticateHeaderUnsupportedFormat: "www_authenticate_header_unsupported_format",
|
||||
/** Environment variable URL malformed - Azure Pod Identity Authority Host */
|
||||
azurePodIdentityAuthorityHostUrlMalformed: "azure_pod_identity_authority_host_url_malformed",
|
||||
/** Environment variable URL malformed - Identity Endpoint */
|
||||
identityEndpointUrlMalformed: "identity_endpoint_url_malformed",
|
||||
/** Environment variable URL malformed - IMDS Endpoint */
|
||||
imdsEndpointUrlMalformed: "imds_endpoint_url_malformed",
|
||||
/** Environment variable URL malformed - MSI Endpoint */
|
||||
msiEndpointUrlMalformed: "msi_endpoint_url_malformed"
|
||||
} as const;
|
||||
```
|
||||
|
||||
### Error Handling Patterns
|
||||
|
||||
Common error handling patterns and best practices.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Error handling utility functions
|
||||
*/
|
||||
type ErrorHandlingUtils = {
|
||||
/** Check if error is retryable */
|
||||
isRetryableError(error: AuthError): boolean;
|
||||
/** Check if error requires user interaction */
|
||||
requiresInteraction(error: AuthError): boolean;
|
||||
/** Extract user-friendly error message */
|
||||
getUserFriendlyMessage(error: AuthError): string;
|
||||
/** Check if error is due to network issues */
|
||||
isNetworkError(error: AuthError): boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Examples:**
|
||||
|
||||
```typescript
|
||||
import {
|
||||
AuthError,
|
||||
ClientAuthError,
|
||||
InteractionRequiredAuthError,
|
||||
NodeAuthError,
|
||||
ManagedIdentityError
|
||||
} from "@azure/msal-node";
|
||||
|
||||
// Basic error handling
|
||||
try {
|
||||
const response = await pca.acquireTokenSilent(silentRequest);
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
console.error("Authentication error:", error.errorCode, error.errorMessage);
|
||||
console.error("Correlation ID:", error.correlationId);
|
||||
} else {
|
||||
console.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprehensive error handling
|
||||
async function handleAuthenticationError(error: unknown): Promise<void> {
|
||||
if (error instanceof InteractionRequiredAuthError) {
|
||||
console.log("User interaction required, falling back to interactive flow");
|
||||
// Implement interactive flow fallback
|
||||
} else if (error instanceof ClientAuthError) {
|
||||
switch (error.errorCode) {
|
||||
case "no_account_found":
|
||||
console.log("No cached accounts found, prompting for login");
|
||||
break;
|
||||
case "multiple_matching_accounts":
|
||||
console.log("Multiple accounts found, prompting for account selection");
|
||||
break;
|
||||
default:
|
||||
console.error("Client authentication error:", error.errorMessage);
|
||||
}
|
||||
} else if (error instanceof NodeAuthError) {
|
||||
switch (error.errorCode) {
|
||||
case "loopback_server_timeout":
|
||||
console.error("Interactive authentication timed out");
|
||||
break;
|
||||
case "state_not_found":
|
||||
console.error("Invalid state parameter - possible CSRF attack");
|
||||
break;
|
||||
default:
|
||||
console.error("Node authentication error:", error.errorMessage);
|
||||
}
|
||||
} else if (error instanceof ManagedIdentityError) {
|
||||
switch (error.errorCode) {
|
||||
case "platform_not_supported":
|
||||
console.error("Managed Identity not available on this platform");
|
||||
break;
|
||||
case "network_request_failed":
|
||||
console.error("Failed to connect to managed identity endpoint");
|
||||
break;
|
||||
default:
|
||||
console.error("Managed Identity error:", error.errorMessage);
|
||||
}
|
||||
} else if (error instanceof AuthError) {
|
||||
console.error("General authentication error:", error.errorCode, error.errorMessage);
|
||||
} else {
|
||||
console.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Silent Flow Error Handling
|
||||
|
||||
Specific error handling for silent token acquisition scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Silent flow error handling pattern
|
||||
*/
|
||||
async function acquireTokenWithFallback(
|
||||
silentRequest: SilentFlowRequest,
|
||||
interactiveRequest: InteractiveRequest
|
||||
): Promise<AuthenticationResult> {
|
||||
try {
|
||||
// Try silent acquisition first
|
||||
return await pca.acquireTokenSilent(silentRequest);
|
||||
} catch (error) {
|
||||
if (error instanceof InteractionRequiredAuthError) {
|
||||
// Silent acquisition failed, user interaction required
|
||||
console.log("Silent acquisition failed, using interactive flow");
|
||||
return await pca.acquireTokenInteractive(interactiveRequest);
|
||||
} else if (error instanceof ClientAuthError && error.errorCode === "no_account_found") {
|
||||
// No cached accounts, need to authenticate
|
||||
console.log("No cached accounts found, using interactive flow");
|
||||
return await pca.acquireTokenInteractive(interactiveRequest);
|
||||
} else {
|
||||
// Other errors should be handled or re-thrown
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Network Error Handling
|
||||
|
||||
Handling network-related errors and implementing retry logic.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Network error handling with retry logic
|
||||
*/
|
||||
async function acquireTokenWithRetry(
|
||||
request: ClientCredentialRequest,
|
||||
maxRetries: number = 3
|
||||
): Promise<AuthenticationResult | null> {
|
||||
let lastError: AuthError;
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await cca.acquireTokenByClientCredential(request);
|
||||
} catch (error) {
|
||||
lastError = error as AuthError;
|
||||
|
||||
if (error instanceof AuthError) {
|
||||
// Check if error is retryable
|
||||
if (isRetryableError(error) && attempt < maxRetries) {
|
||||
const delayMs = Math.pow(2, attempt) * 1000; // Exponential backoff
|
||||
console.log(`Attempt ${attempt} failed, retrying in ${delayMs}ms`);
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-retryable error or max retries reached
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
function isRetryableError(error: AuthError): boolean {
|
||||
const retryableErrors = [
|
||||
"network_error",
|
||||
"request_timeout",
|
||||
"post_request_failed",
|
||||
"get_request_failed"
|
||||
];
|
||||
|
||||
return retryableErrors.includes(error.errorCode);
|
||||
}
|
||||
```
|
||||
|
||||
### Device Code Flow Error Handling
|
||||
|
||||
Specific error handling for device code flow scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Device code flow error handling
|
||||
*/
|
||||
async function deviceCodeFlowWithErrorHandling(): Promise<AuthenticationResult | null> {
|
||||
const deviceCodeRequest = {
|
||||
scopes: ["user.read"],
|
||||
deviceCodeCallback: (response) => {
|
||||
console.log("Please visit:", response.verificationUri);
|
||||
console.log("And enter code:", response.userCode);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByDeviceCode(deviceCodeRequest);
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
switch (error.errorCode) {
|
||||
case "authorization_pending":
|
||||
console.log("User has not completed authentication yet");
|
||||
break;
|
||||
case "slow_down":
|
||||
console.log("Polling too frequently, slowing down");
|
||||
break;
|
||||
case "expired_token":
|
||||
console.log("Device code has expired, please try again");
|
||||
break;
|
||||
case "access_denied":
|
||||
console.log("User denied authorization");
|
||||
break;
|
||||
default:
|
||||
console.error("Device code flow error:", error.errorMessage);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Recovery Strategies
|
||||
|
||||
Implementation patterns for error recovery and user experience.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Comprehensive error recovery strategy
|
||||
*/
|
||||
class AuthenticationService {
|
||||
private pca: PublicClientApplication;
|
||||
|
||||
constructor(config: Configuration) {
|
||||
this.pca = new PublicClientApplication(config);
|
||||
}
|
||||
|
||||
async acquireToken(scopes: string[]): Promise<AuthenticationResult> {
|
||||
const accounts = await this.pca.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
// Try silent acquisition first
|
||||
try {
|
||||
return await this.pca.acquireTokenSilent({
|
||||
account: accounts[0],
|
||||
scopes: scopes
|
||||
});
|
||||
} catch (error) {
|
||||
return await this.handleSilentFlowError(error, scopes);
|
||||
}
|
||||
} else {
|
||||
// No accounts cached, use interactive flow
|
||||
return await this.acquireTokenInteractively(scopes);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSilentFlowError(
|
||||
error: unknown,
|
||||
scopes: string[]
|
||||
): Promise<AuthenticationResult> {
|
||||
if (error instanceof InteractionRequiredAuthError) {
|
||||
// Token expired or consent required
|
||||
return await this.acquireTokenInteractively(scopes);
|
||||
} else if (error instanceof ClientAuthError) {
|
||||
if (error.errorCode === "no_token_found") {
|
||||
// No cached token, use interactive flow
|
||||
return await this.acquireTokenInteractively(scopes);
|
||||
}
|
||||
} else if (error instanceof AuthError && this.isNetworkError(error)) {
|
||||
// Network error, implement retry with backoff
|
||||
await this.delay(1000);
|
||||
return await this.acquireToken(scopes);
|
||||
}
|
||||
|
||||
// Re-throw unhandled errors
|
||||
throw error;
|
||||
}
|
||||
|
||||
private async acquireTokenInteractively(scopes: string[]): Promise<AuthenticationResult> {
|
||||
return await this.pca.acquireTokenInteractive({
|
||||
scopes: scopes,
|
||||
openBrowser: async (url: string) => {
|
||||
const { open } = await import("open");
|
||||
await open(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isNetworkError(error: AuthError): boolean {
|
||||
const networkErrorCodes = [
|
||||
"network_error",
|
||||
"request_timeout",
|
||||
"post_request_failed",
|
||||
"get_request_failed"
|
||||
];
|
||||
return networkErrorCodes.includes(error.errorCode);
|
||||
}
|
||||
|
||||
private delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Logging and Monitoring
|
||||
|
||||
Error logging and monitoring best practices.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Error logging and monitoring integration
|
||||
*/
|
||||
class ErrorLogger {
|
||||
static logAuthError(error: AuthError, context: string): void {
|
||||
const errorData = {
|
||||
timestamp: new Date().toISOString(),
|
||||
context: context,
|
||||
errorCode: error.errorCode,
|
||||
errorMessage: error.errorMessage,
|
||||
correlationId: error.correlationId,
|
||||
subError: error.subError,
|
||||
stack: error.stack
|
||||
};
|
||||
|
||||
// Log to monitoring system
|
||||
console.error("Authentication Error:", JSON.stringify(errorData, null, 2));
|
||||
|
||||
// Send to monitoring service (e.g., Application Insights, DataDog)
|
||||
// this.sendToMonitoring(errorData);
|
||||
}
|
||||
|
||||
static logNetworkError(error: AuthError, endpoint: string): void {
|
||||
const networkErrorData = {
|
||||
timestamp: new Date().toISOString(),
|
||||
endpoint: endpoint,
|
||||
errorCode: error.errorCode,
|
||||
errorMessage: error.errorMessage,
|
||||
correlationId: error.correlationId
|
||||
};
|
||||
|
||||
console.error("Network Error:", JSON.stringify(networkErrorData, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in error handling
|
||||
try {
|
||||
const response = await pca.acquireTokenSilent(request);
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
ErrorLogger.logAuthError(error, "silent_token_acquisition");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Error Scenarios
|
||||
|
||||
### Token Expiration
|
||||
|
||||
```typescript
|
||||
// Handle expired tokens
|
||||
if (error.errorCode === "token_expired" || error.errorCode === "interaction_required") {
|
||||
// Token has expired, need to refresh or re-authenticate
|
||||
console.log("Token expired, attempting refresh");
|
||||
}
|
||||
```
|
||||
|
||||
### Account Selection
|
||||
|
||||
```typescript
|
||||
// Handle multiple accounts
|
||||
if (error.errorCode === "multiple_matching_accounts") {
|
||||
const accounts = await pca.getAllAccounts();
|
||||
// Present account selection UI to user
|
||||
console.log("Multiple accounts found:", accounts.map(a => a.username));
|
||||
}
|
||||
```
|
||||
|
||||
### Network Connectivity
|
||||
|
||||
```typescript
|
||||
// Handle network issues
|
||||
if (error.errorCode === "network_error" || error.errorCode === "request_timeout") {
|
||||
console.log("Network connectivity issue, check internet connection");
|
||||
// Implement offline mode or retry logic
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Issues
|
||||
|
||||
```typescript
|
||||
// Handle configuration errors
|
||||
if (error instanceof ClientConfigurationError) {
|
||||
console.error("Configuration error - check client ID, authority, and other settings");
|
||||
console.error("Error details:", error.errorMessage);
|
||||
}
|
||||
```
|
||||
|
||||
### Platform-Specific Issues
|
||||
|
||||
```typescript
|
||||
// Handle managed identity platform issues
|
||||
if (error instanceof ManagedIdentityError && error.errorCode === "platform_not_supported") {
|
||||
console.error("Managed Identity not available - running outside Azure environment");
|
||||
// Fallback to other authentication methods
|
||||
}
|
||||
```
|
||||
475
.tessl/tiles/tessl/npm-azure--msal-node/docs/index.md
Normal file
475
.tessl/tiles/tessl/npm-azure--msal-node/docs/index.md
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
# MSAL Node
|
||||
|
||||
Microsoft Authentication Library for Node (MSAL Node) is a comprehensive TypeScript library that enables Node.js applications to authenticate users and acquire tokens using the Microsoft Identity platform. It supports multiple OAuth 2.0 authentication flows for both public and confidential client applications, enabling authentication with Azure AD work and school accounts, Microsoft personal accounts, and social identity providers through Azure AD B2C.
|
||||
|
||||
## Package Information
|
||||
|
||||
- **Package Name**: @azure/msal-node
|
||||
- **Package Type**: npm
|
||||
- **Language**: TypeScript
|
||||
- **Installation**: `npm install @azure/msal-node`
|
||||
|
||||
## Core Imports
|
||||
|
||||
```typescript
|
||||
import {
|
||||
PublicClientApplication,
|
||||
ConfidentialClientApplication,
|
||||
ManagedIdentityApplication,
|
||||
type Configuration,
|
||||
type AuthenticationResult
|
||||
} from "@azure/msal-node";
|
||||
```
|
||||
|
||||
For CommonJS:
|
||||
|
||||
```javascript
|
||||
const {
|
||||
PublicClientApplication,
|
||||
ConfidentialClientApplication,
|
||||
ManagedIdentityApplication
|
||||
} = require("@azure/msal-node");
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Public Client (Desktop/Mobile Apps)
|
||||
|
||||
```typescript
|
||||
import { PublicClientApplication } from "@azure/msal-node";
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
}
|
||||
});
|
||||
|
||||
// Authorization code flow
|
||||
const authCodeUrlParameters = {
|
||||
scopes: ["user.read"],
|
||||
redirectUri: "http://localhost:3000/redirect"
|
||||
};
|
||||
|
||||
const authUrl = await pca.getAuthCodeUrl(authCodeUrlParameters);
|
||||
// Redirect user to authUrl, get code from callback
|
||||
|
||||
const tokenRequest = {
|
||||
code: "authorization-code-from-callback",
|
||||
scopes: ["user.read"],
|
||||
redirectUri: "http://localhost:3000/redirect"
|
||||
};
|
||||
|
||||
const response = await pca.acquireTokenByCode(tokenRequest);
|
||||
console.log(response.accessToken);
|
||||
```
|
||||
|
||||
### Confidential Client (Server Applications)
|
||||
|
||||
```typescript
|
||||
import { ConfidentialClientApplication } from "@azure/msal-node";
|
||||
|
||||
const cca = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
clientSecret: "your-client-secret",
|
||||
authority: "https://login.microsoftonline.com/your-tenant-id"
|
||||
}
|
||||
});
|
||||
|
||||
// Client credentials flow
|
||||
const clientCredentialRequest = {
|
||||
scopes: ["https://graph.microsoft.com/.default"]
|
||||
};
|
||||
|
||||
const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
|
||||
console.log(response.accessToken);
|
||||
```
|
||||
|
||||
### Managed Identity (Azure Resources)
|
||||
|
||||
```typescript
|
||||
import { ManagedIdentityApplication } from "@azure/msal-node";
|
||||
|
||||
const mia = new ManagedIdentityApplication({});
|
||||
|
||||
const tokenRequest = {
|
||||
resource: "https://graph.microsoft.com/"
|
||||
};
|
||||
|
||||
const response = await mia.acquireToken(tokenRequest);
|
||||
console.log(response.accessToken);
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
MSAL Node is built around several key components:
|
||||
|
||||
- **Client Applications**: Different application types (Public, Confidential, Managed Identity) providing specialized authentication flows
|
||||
- **Request/Response System**: Strongly-typed request objects for each OAuth 2.0 flow with comprehensive response handling
|
||||
- **Token Cache**: In-memory and distributed cache system for storing tokens with serialization support
|
||||
- **Configuration System**: Hierarchical configuration with authentication, system, cache, and telemetry options
|
||||
- **Error Handling**: Comprehensive error classes with specific error codes for different scenarios
|
||||
- **Cryptographic Provider**: Secure cryptographic operations including PKCE, JWT signatures, and certificate handling
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Public Client Applications
|
||||
|
||||
Core functionality for applications that cannot securely store client secrets, including desktop apps, mobile apps, and single-page applications.
|
||||
|
||||
```typescript { .api }
|
||||
interface IPublicClientApplication {
|
||||
getAuthCodeUrl(request: AuthorizationUrlRequest): Promise<string>;
|
||||
acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
|
||||
acquireTokenInteractive(request: InteractiveRequest): Promise<AuthenticationResult>;
|
||||
acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult>;
|
||||
acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenByDeviceCode(request: DeviceCodeRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenByUsernamePassword(request: UsernamePasswordRequest): Promise<AuthenticationResult | null>;
|
||||
getTokenCache(): TokenCache;
|
||||
getLogger(): Logger;
|
||||
setLogger(logger: Logger): void;
|
||||
clearCache(): void;
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
signOut(request: SignOutRequest): Promise<void>;
|
||||
}
|
||||
|
||||
class PublicClientApplication implements IPublicClientApplication {
|
||||
constructor(configuration: Configuration);
|
||||
}
|
||||
```
|
||||
|
||||
[Public Client Applications](./public-client.md)
|
||||
|
||||
### Confidential Client Applications
|
||||
|
||||
Advanced functionality for applications that can securely store client secrets, including server applications, web APIs, and daemon applications.
|
||||
|
||||
```typescript { .api }
|
||||
interface IConfidentialClientApplication {
|
||||
getAuthCodeUrl(request: AuthorizationUrlRequest): Promise<string>;
|
||||
acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
|
||||
acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenByClientCredential(request: ClientCredentialRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenOnBehalfOf(request: OnBehalfOfRequest): Promise<AuthenticationResult | null>;
|
||||
acquireTokenByUsernamePassword(request: UsernamePasswordRequest): Promise<AuthenticationResult | null>;
|
||||
getTokenCache(): TokenCache;
|
||||
getLogger(): Logger;
|
||||
setLogger(logger: Logger): void;
|
||||
clearCache(): void;
|
||||
SetAppTokenProvider(provider: IAppTokenProvider): void;
|
||||
}
|
||||
|
||||
class ConfidentialClientApplication implements IConfidentialClientApplication {
|
||||
constructor(configuration: Configuration);
|
||||
}
|
||||
```
|
||||
|
||||
[Confidential Client Applications](./confidential-client.md)
|
||||
|
||||
### Managed Identity Applications
|
||||
|
||||
Azure Managed Identity authentication for applications running on Azure resources like VMs, App Service, and Function Apps.
|
||||
|
||||
```typescript { .api }
|
||||
class ManagedIdentityApplication {
|
||||
constructor(configuration?: ManagedIdentityConfiguration);
|
||||
acquireToken(request: ManagedIdentityRequestParams): Promise<AuthenticationResult | null>;
|
||||
getManagedIdentitySource(): ManagedIdentitySourceNames;
|
||||
}
|
||||
|
||||
type ManagedIdentityConfiguration = {
|
||||
clientCapabilities?: Array<string>;
|
||||
managedIdentityIdParams?: ManagedIdentityIdParams;
|
||||
system?: NodeSystemOptions;
|
||||
};
|
||||
|
||||
type ManagedIdentityIdParams = {
|
||||
userAssignedClientId?: string;
|
||||
userAssignedResourceId?: string;
|
||||
userAssignedObjectId?: string;
|
||||
};
|
||||
```
|
||||
|
||||
[Managed Identity Applications](./managed-identity.md)
|
||||
|
||||
### Authentication Flows
|
||||
|
||||
Complete set of OAuth 2.0 authentication flows with strongly-typed request objects and comprehensive configuration options.
|
||||
|
||||
```typescript { .api }
|
||||
type AuthorizationUrlRequest = {
|
||||
scopes: string[];
|
||||
redirectUri: string;
|
||||
prompt?: PromptValue;
|
||||
account?: AccountInfo;
|
||||
loginHint?: string;
|
||||
domainHint?: string;
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
};
|
||||
|
||||
type AuthorizationCodeRequest = {
|
||||
scopes: string[];
|
||||
redirectUri: string;
|
||||
code: string;
|
||||
state?: string;
|
||||
authority?: string;
|
||||
correlationId?: string;
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
type ClientCredentialRequest = {
|
||||
scopes: string[];
|
||||
authority?: string;
|
||||
clientAssertion?: string | (() => string);
|
||||
skipCache?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
[Authentication Flows](./authentication-flows.md)
|
||||
|
||||
### Token Cache Management
|
||||
|
||||
Comprehensive token caching system with in-memory storage, serialization support, and distributed cache capabilities.
|
||||
|
||||
```typescript { .api }
|
||||
interface ITokenCache {
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
getAccountByHomeId(homeAccountId: string): Promise<AccountInfo | null>;
|
||||
getAccountByLocalId(localAccountId: string): Promise<AccountInfo | null>;
|
||||
removeAccount(account: AccountInfo): Promise<void>;
|
||||
}
|
||||
|
||||
class TokenCache implements ITokenCache, ISerializableTokenCache {
|
||||
// Cache management methods
|
||||
}
|
||||
|
||||
type CacheOptions = {
|
||||
cachePlugin?: ICachePlugin;
|
||||
claimsBasedCachingEnabled?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
[Token Cache Management](./token-cache.md)
|
||||
|
||||
### Configuration System
|
||||
|
||||
Hierarchical configuration system supporting authentication options, system settings, cache configuration, and telemetry.
|
||||
|
||||
```typescript { .api }
|
||||
type Configuration = {
|
||||
auth: NodeAuthOptions;
|
||||
broker?: BrokerOptions;
|
||||
cache?: CacheOptions;
|
||||
system?: NodeSystemOptions;
|
||||
telemetry?: NodeTelemetryOptions;
|
||||
};
|
||||
|
||||
type NodeAuthOptions = {
|
||||
clientId: string;
|
||||
authority?: string;
|
||||
clientSecret?: string;
|
||||
clientAssertion?: string | ClientAssertionCallback;
|
||||
clientCertificate?: {
|
||||
thumbprintSha256?: string;
|
||||
privateKey: string;
|
||||
x5c?: string;
|
||||
};
|
||||
knownAuthorities?: Array<string>;
|
||||
protocolMode?: ProtocolMode;
|
||||
};
|
||||
```
|
||||
|
||||
[Configuration System](./configuration.md)
|
||||
|
||||
### Error Handling
|
||||
|
||||
Comprehensive error handling with specific error classes and detailed error codes for different authentication scenarios.
|
||||
|
||||
```typescript { .api }
|
||||
class NodeAuthError extends AuthError {
|
||||
static createStateNotFoundError(): NodeAuthError;
|
||||
static createLoopbackServerTimeoutError(): NodeAuthError;
|
||||
}
|
||||
|
||||
class ManagedIdentityError extends AuthError {
|
||||
// Managed Identity specific errors
|
||||
}
|
||||
|
||||
type AuthErrorCodes = {
|
||||
unexpectedError: string;
|
||||
postRequestFailed: string;
|
||||
getRequestFailed: string;
|
||||
// Additional error codes...
|
||||
};
|
||||
```
|
||||
|
||||
[Error Handling](./error-handling.md)
|
||||
|
||||
### Specialized Client Classes
|
||||
|
||||
Low-level client classes for specific OAuth 2.0 flows, providing direct access to individual authentication methods.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* OAuth 2.0 client credential flow client
|
||||
*/
|
||||
class ClientCredentialClient extends BaseClient {
|
||||
constructor(configuration: ClientConfiguration, appTokenProvider?: IAppTokenProvider);
|
||||
acquireToken(request: CommonClientCredentialRequest): Promise<AuthenticationResult | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth 2.0 device code flow client
|
||||
*/
|
||||
class DeviceCodeClient extends BaseClient {
|
||||
constructor(configuration: ClientConfiguration);
|
||||
acquireToken(request: CommonDeviceCodeRequest): Promise<AuthenticationResult | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth 2.0 on-behalf-of flow client
|
||||
*/
|
||||
class OnBehalfOfClient extends BaseClient {
|
||||
constructor(configuration: ClientConfiguration);
|
||||
acquireToken(request: CommonOnBehalfOfRequest): Promise<AuthenticationResult | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth 2.0 username/password flow client
|
||||
*/
|
||||
class UsernamePasswordClient extends BaseClient {
|
||||
constructor(configuration: ClientConfiguration);
|
||||
acquireToken(request: CommonUsernamePasswordRequest): Promise<AuthenticationResult | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base client application class
|
||||
*/
|
||||
abstract class ClientApplication {
|
||||
protected constructor(configuration: Configuration);
|
||||
getTokenCache(): TokenCache;
|
||||
getLogger(): Logger;
|
||||
setLogger(logger: Logger): void;
|
||||
clearCache(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### Cryptographic Provider
|
||||
|
||||
Cryptographic operations for authentication flows including PKCE, JWT signatures, and certificate handling.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Cryptographic provider for Node.js environments
|
||||
*/
|
||||
class CryptoProvider {
|
||||
/** Generate UUID v4 */
|
||||
createNewGuid(): string;
|
||||
/** Generate PKCE code verifier */
|
||||
generatePkceCodes(): PkceCodes;
|
||||
/** Generate SHA256 hash */
|
||||
sha256Digest(inputString: string): Promise<string>;
|
||||
/** Sign JWT with private key */
|
||||
signJwt(payload: object, privateKey: string): Promise<string>;
|
||||
/** Verify JWT signature */
|
||||
verifyJwt(token: string, publicKey: string): Promise<object>;
|
||||
}
|
||||
|
||||
type PkceCodes = {
|
||||
verifier: string;
|
||||
challenge: string;
|
||||
};
|
||||
```
|
||||
|
||||
### Client Assertion
|
||||
|
||||
Certificate-based authentication support for confidential client applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Client assertion for certificate-based authentication
|
||||
*/
|
||||
class ClientAssertion {
|
||||
constructor(assertion: string);
|
||||
getJwt(): string;
|
||||
static fromCertificate(privateKey: string, thumbprint: string, audience: string): ClientAssertion;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Types
|
||||
|
||||
### Authentication Results
|
||||
|
||||
```typescript { .api }
|
||||
type AuthenticationResult = {
|
||||
authority: string;
|
||||
uniqueId: string;
|
||||
tenantId: string;
|
||||
scopes: string[];
|
||||
account: AccountInfo | null;
|
||||
idToken: string;
|
||||
idTokenClaims: IdTokenClaims;
|
||||
accessToken: string;
|
||||
fromCache: boolean;
|
||||
expiresOn: Date | null;
|
||||
correlationId: string;
|
||||
requestId?: string;
|
||||
};
|
||||
|
||||
type AccountInfo = {
|
||||
homeAccountId: string;
|
||||
environment: string;
|
||||
tenantId: string;
|
||||
username: string;
|
||||
localAccountId: string;
|
||||
name?: string;
|
||||
idTokenClaims?: IdTokenClaims;
|
||||
};
|
||||
```
|
||||
|
||||
### Protocol Types
|
||||
|
||||
```typescript { .api }
|
||||
enum PromptValue {
|
||||
LOGIN = "login",
|
||||
SELECT_ACCOUNT = "select_account",
|
||||
CONSENT = "consent",
|
||||
NONE = "none"
|
||||
}
|
||||
|
||||
enum ResponseMode {
|
||||
QUERY = "query",
|
||||
FRAGMENT = "fragment",
|
||||
FORM_POST = "form_post"
|
||||
}
|
||||
|
||||
enum ProtocolMode {
|
||||
AAD = "AAD",
|
||||
OIDC = "OIDC"
|
||||
}
|
||||
|
||||
enum AzureCloudInstance {
|
||||
None = "",
|
||||
AzurePublic = "https://login.microsoftonline.com",
|
||||
AzurePpope = "https://login.microsoftonline.us",
|
||||
AzureChina = "https://login.chinacloudapi.cn",
|
||||
AzureGermany = "https://login.microsoftonline.de"
|
||||
}
|
||||
|
||||
type ClientAssertionCallback = () => string;
|
||||
|
||||
const ManagedIdentitySourceNames = {
|
||||
IMDS: "IMDS",
|
||||
APP_SERVICE: "APP_SERVICE",
|
||||
AZURE_ARC: "AZURE_ARC",
|
||||
CLOUD_SHELL: "CLOUD_SHELL",
|
||||
SERVICE_FABRIC: "SERVICE_FABRIC",
|
||||
MACHINE_LEARNING: "MACHINE_LEARNING",
|
||||
DEFAULT_TO_IMDS: "DEFAULT_TO_IMDS"
|
||||
} as const;
|
||||
|
||||
type ManagedIdentitySourceNames = typeof ManagedIdentitySourceNames[keyof typeof ManagedIdentitySourceNames];
|
||||
```
|
||||
438
.tessl/tiles/tessl/npm-azure--msal-node/docs/managed-identity.md
Normal file
438
.tessl/tiles/tessl/npm-azure--msal-node/docs/managed-identity.md
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
# Managed Identity Applications
|
||||
|
||||
Managed Identity Applications provide authentication for applications running on Azure resources like Virtual Machines, App Service, Function Apps, and other Azure services. This eliminates the need to store credentials in code by leveraging Azure's managed identity infrastructure.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### ManagedIdentityApplication Class
|
||||
|
||||
Main class for Azure Managed Identity authentication.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Managed Identity application class for Azure resources
|
||||
* Automatically detects and uses the appropriate managed identity source
|
||||
*/
|
||||
class ManagedIdentityApplication {
|
||||
constructor(configuration: ManagedIdentityConfiguration);
|
||||
|
||||
/**
|
||||
* Acquires a token using Azure Managed Identity
|
||||
* Automatically detects the managed identity source (IMDS, App Service, etc.)
|
||||
*/
|
||||
acquireToken(request: ManagedIdentityRequestParams): Promise<AuthenticationResult | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for managed identity applications
|
||||
*/
|
||||
type ManagedIdentityConfiguration = {
|
||||
/** Client capabilities for conditional access */
|
||||
clientCapabilities?: Array<string>;
|
||||
/** Parameters for user-assigned managed identity */
|
||||
managedIdentityIdParams?: ManagedIdentityIdParams;
|
||||
/** System configuration options */
|
||||
system?: NodeSystemOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters for user-assigned managed identity
|
||||
*/
|
||||
type ManagedIdentityIdParams = {
|
||||
/** Client ID of the user-assigned managed identity */
|
||||
userAssignedClientId?: string;
|
||||
/** Resource ID of the user-assigned managed identity */
|
||||
userAssignedResourceId?: string;
|
||||
/** Object ID of the user-assigned managed identity */
|
||||
userAssignedObjectId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request parameters for managed identity token acquisition
|
||||
*/
|
||||
type ManagedIdentityRequestParams = {
|
||||
/** Target resource URI for the token */
|
||||
resource: string;
|
||||
/** Claims for conditional access */
|
||||
claims?: string;
|
||||
/** Force refresh instead of using cached token */
|
||||
forceRefresh?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ManagedIdentityApplication } from "@azure/msal-node";
|
||||
|
||||
// System-assigned managed identity (default)
|
||||
const mia = new ManagedIdentityApplication({});
|
||||
|
||||
const tokenRequest = {
|
||||
resource: "https://graph.microsoft.com/"
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await mia.acquireToken(tokenRequest);
|
||||
if (response) {
|
||||
console.log("Access token:", response.accessToken);
|
||||
console.log("Token expires on:", response.expiresOn);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Managed identity authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### System-Assigned Managed Identity
|
||||
|
||||
Default configuration that uses the system-assigned managed identity of the Azure resource.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Basic configuration for system-assigned managed identity
|
||||
* No additional parameters needed - automatically uses the system-assigned identity
|
||||
*/
|
||||
type SystemAssignedConfig = ManagedIdentityConfiguration;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// System-assigned managed identity (simplest configuration)
|
||||
const mia = new ManagedIdentityApplication({});
|
||||
|
||||
// Acquire token for Microsoft Graph
|
||||
const graphToken = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/"
|
||||
});
|
||||
|
||||
// Acquire token for Azure Resource Manager
|
||||
const armToken = await mia.acquireToken({
|
||||
resource: "https://management.azure.com/"
|
||||
});
|
||||
|
||||
// Acquire token for Key Vault
|
||||
const keyVaultToken = await mia.acquireToken({
|
||||
resource: "https://vault.azure.net/"
|
||||
});
|
||||
```
|
||||
|
||||
### User-Assigned Managed Identity
|
||||
|
||||
Configuration for using a specific user-assigned managed identity.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Configuration for user-assigned managed identity
|
||||
* Specify identity using one of: clientId, resourceId, or objectId
|
||||
*/
|
||||
type UserAssignedConfig = {
|
||||
managedIdentityIdParams: ManagedIdentityIdParams;
|
||||
system?: NodeSystemOptions;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Examples:**
|
||||
|
||||
```typescript
|
||||
// User-assigned managed identity by client ID
|
||||
const miaByClientId = new ManagedIdentityApplication({
|
||||
managedIdentityIdParams: {
|
||||
userAssignedClientId: "12345678-1234-1234-1234-123456789012"
|
||||
}
|
||||
});
|
||||
|
||||
// User-assigned managed identity by resource ID
|
||||
const miaByResourceId = new ManagedIdentityApplication({
|
||||
managedIdentityIdParams: {
|
||||
userAssignedResourceId: "/subscriptions/sub-id/resourceGroups/rg-name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/identity-name"
|
||||
}
|
||||
});
|
||||
|
||||
// User-assigned managed identity by object ID
|
||||
const miaByObjectId = new ManagedIdentityApplication({
|
||||
managedIdentityIdParams: {
|
||||
userAssignedObjectId: "87654321-4321-4321-4321-210987654321"
|
||||
}
|
||||
});
|
||||
|
||||
const tokenRequest = {
|
||||
resource: "https://graph.microsoft.com/"
|
||||
};
|
||||
|
||||
const response = await miaByClientId.acquireToken(tokenRequest);
|
||||
```
|
||||
|
||||
### Supported Azure Services
|
||||
|
||||
Managed Identity automatically detects and works with various Azure services:
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Managed Identity source names (for reference)
|
||||
* These are automatically detected - no manual configuration needed
|
||||
*/
|
||||
const ManagedIdentitySourceNames = {
|
||||
/** Azure Instance Metadata Service (VMs, VMSS) */
|
||||
IMDS: "IMDS",
|
||||
/** Azure App Service and Function Apps */
|
||||
APP_SERVICE: "APP_SERVICE",
|
||||
/** Azure Arc enabled servers */
|
||||
AZURE_ARC: "AZURE_ARC",
|
||||
/** Azure Cloud Shell */
|
||||
CLOUD_SHELL: "CLOUD_SHELL",
|
||||
/** Azure Service Fabric */
|
||||
SERVICE_FABRIC: "SERVICE_FABRIC",
|
||||
/** Azure Machine Learning */
|
||||
MACHINE_LEARNING: "MACHINE_LEARNING",
|
||||
/** Default fallback to IMDS */
|
||||
DEFAULT_TO_IMDS: "DEFAULT_TO_IMDS"
|
||||
} as const;
|
||||
```
|
||||
|
||||
**Supported Azure Services:**
|
||||
|
||||
- **Azure Virtual Machines**: Uses IMDS endpoint
|
||||
- **Azure App Service**: Uses environment variables and MSI endpoint
|
||||
- **Azure Function Apps**: Uses environment variables and MSI endpoint
|
||||
- **Azure Container Instances**: Uses IMDS endpoint
|
||||
- **Azure Kubernetes Service**: Uses IMDS endpoint
|
||||
- **Azure Service Fabric**: Uses Service Fabric endpoint
|
||||
- **Azure Arc enabled servers**: Uses Arc-specific endpoint
|
||||
- **Azure Cloud Shell**: Uses Cloud Shell environment
|
||||
- **Azure Machine Learning**: Uses ML-specific endpoint
|
||||
|
||||
### Token Caching and Refresh
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request with cache control options
|
||||
*/
|
||||
type ManagedIdentityRequestWithCaching = {
|
||||
/** Target resource URI for the token */
|
||||
resource: string;
|
||||
/** Force refresh instead of using cached token */
|
||||
forceRefresh?: boolean;
|
||||
/** Claims for conditional access scenarios */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
const mia = new ManagedIdentityApplication({});
|
||||
|
||||
// Use cached token if available and valid
|
||||
const cachedResponse = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/",
|
||||
forceRefresh: false
|
||||
});
|
||||
|
||||
// Force refresh from managed identity endpoint
|
||||
const freshResponse = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/",
|
||||
forceRefresh: true
|
||||
});
|
||||
|
||||
// With conditional access claims
|
||||
const claimsResponse = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/",
|
||||
claims: JSON.stringify({
|
||||
"access_token": {
|
||||
"acrs": {
|
||||
"essential": true,
|
||||
"values": ["urn:microsoft:req1"]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Managed Identity specific error class
|
||||
*/
|
||||
class ManagedIdentityError extends AuthError {
|
||||
constructor(errorCode: string, errorMessage: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common managed identity error codes
|
||||
*/
|
||||
const ManagedIdentityErrorCodes = {
|
||||
/** Network request failed */
|
||||
NETWORK_REQUEST_FAILED: "network_request_failed",
|
||||
/** Invalid resource format */
|
||||
INVALID_RESOURCE: "invalid_resource",
|
||||
/** Managed identity not available on current platform */
|
||||
PLATFORM_NOT_SUPPORTED: "platform_not_supported",
|
||||
/** Environment variables malformed */
|
||||
MALFORMED_ENVIRONMENT_VARIABLE: "malformed_environment_variable",
|
||||
/** Timeout waiting for response */
|
||||
REQUEST_TIMEOUT: "request_timeout",
|
||||
/** File system access error */
|
||||
FILE_NOT_FOUND: "file_not_found"
|
||||
} as const;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ManagedIdentityError } from "@azure/msal-node";
|
||||
|
||||
try {
|
||||
const response = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/"
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ManagedIdentityError) {
|
||||
switch (error.errorCode) {
|
||||
case "platform_not_supported":
|
||||
console.error("Managed Identity not supported on this platform");
|
||||
break;
|
||||
case "network_request_failed":
|
||||
console.error("Network error accessing managed identity endpoint");
|
||||
break;
|
||||
case "invalid_resource":
|
||||
console.error("Invalid resource URI provided");
|
||||
break;
|
||||
default:
|
||||
console.error("Managed Identity error:", error.message);
|
||||
}
|
||||
} else {
|
||||
console.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Advanced managed identity configuration with system options
|
||||
*/
|
||||
type AdvancedManagedIdentityConfiguration = {
|
||||
/** Client capabilities for conditional access */
|
||||
clientCapabilities?: Array<string>;
|
||||
/** User-assigned identity parameters */
|
||||
managedIdentityIdParams?: ManagedIdentityIdParams;
|
||||
/** System configuration */
|
||||
system?: {
|
||||
/** Logger configuration */
|
||||
loggerOptions?: LoggerOptions;
|
||||
/** Custom network client */
|
||||
networkClient?: INetworkModule;
|
||||
/** Proxy URL for network requests */
|
||||
proxyUrl?: string;
|
||||
/** Custom HTTP agent options */
|
||||
customAgentOptions?: http.AgentOptions | https.AgentOptions;
|
||||
/** Disable internal request retries */
|
||||
disableInternalRetries?: boolean;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { ManagedIdentityApplication, LogLevel } from "@azure/msal-node";
|
||||
|
||||
const mia = new ManagedIdentityApplication({
|
||||
managedIdentityIdParams: {
|
||||
userAssignedClientId: "12345678-1234-1234-1234-123456789012"
|
||||
},
|
||||
clientCapabilities: ["CP1"], // Conditional access capability
|
||||
system: {
|
||||
loggerOptions: {
|
||||
loggerCallback: (level, message, containsPii) => {
|
||||
if (!containsPii) {
|
||||
console.log(`[${level}] ${message}`);
|
||||
}
|
||||
},
|
||||
logLevel: LogLevel.Verbose,
|
||||
piiLoggingEnabled: false
|
||||
},
|
||||
proxyUrl: "http://proxy.company.com:8080",
|
||||
disableInternalRetries: false
|
||||
}
|
||||
});
|
||||
|
||||
const response = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/",
|
||||
claims: JSON.stringify({
|
||||
"access_token": {
|
||||
"xms_cc": {
|
||||
"values": ["CP1"]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Accessing Microsoft Graph
|
||||
|
||||
```typescript
|
||||
const mia = new ManagedIdentityApplication({});
|
||||
|
||||
const graphToken = await mia.acquireToken({
|
||||
resource: "https://graph.microsoft.com/"
|
||||
});
|
||||
|
||||
// Use token to call Microsoft Graph
|
||||
const response = await fetch("https://graph.microsoft.com/v1.0/me", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${graphToken.accessToken}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Accessing Azure Resource Manager
|
||||
|
||||
```typescript
|
||||
const armToken = await mia.acquireToken({
|
||||
resource: "https://management.azure.com/"
|
||||
});
|
||||
|
||||
// Use token for Azure Resource Manager APIs
|
||||
const subscriptions = await fetch("https://management.azure.com/subscriptions?api-version=2020-01-01", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${armToken.accessToken}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Accessing Azure Key Vault
|
||||
|
||||
```typescript
|
||||
const keyVaultToken = await mia.acquireToken({
|
||||
resource: "https://vault.azure.net/"
|
||||
});
|
||||
|
||||
// Use token for Key Vault operations
|
||||
const secret = await fetch("https://my-vault.vault.azure.net/secrets/my-secret?api-version=7.2", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${keyVaultToken.accessToken}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Accessing Azure Storage
|
||||
|
||||
```typescript
|
||||
const storageToken = await mia.acquireToken({
|
||||
resource: "https://storage.azure.com/"
|
||||
});
|
||||
|
||||
// Use token for Azure Storage operations
|
||||
const blobs = await fetch("https://myaccount.blob.core.windows.net/mycontainer?restype=container&comp=list", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${storageToken.accessToken}`,
|
||||
"x-ms-version": "2020-04-08"
|
||||
}
|
||||
});
|
||||
```
|
||||
421
.tessl/tiles/tessl/npm-azure--msal-node/docs/public-client.md
Normal file
421
.tessl/tiles/tessl/npm-azure--msal-node/docs/public-client.md
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
# Public Client Applications
|
||||
|
||||
Public Client Applications are designed for applications that cannot securely store client secrets, such as desktop applications, mobile apps, and single-page applications. These applications rely on user interaction for authentication and use flows like authorization code with PKCE, device code, and interactive authentication.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### PublicClientApplication Class
|
||||
|
||||
Main class for creating public client applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Public client application class implementing IPublicClientApplication interface
|
||||
* Suitable for applications that cannot securely store client secrets
|
||||
*/
|
||||
class PublicClientApplication implements IPublicClientApplication {
|
||||
constructor(configuration: Configuration);
|
||||
|
||||
/** Creates the URL of the authorization request */
|
||||
getAuthCodeUrl(request: AuthorizationUrlRequest): Promise<string>;
|
||||
|
||||
/** Acquires a token by exchanging the authorization code received from the first step of OAuth 2.0 Authorization Code Flow */
|
||||
acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
|
||||
|
||||
/** Acquires a token interactively */
|
||||
acquireTokenInteractive(request: InteractiveRequest): Promise<AuthenticationResult>;
|
||||
|
||||
/** Acquires a token silently when a user specifies the account the token is requested for */
|
||||
acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult>;
|
||||
|
||||
/** Acquires a token by exchanging the refresh token provided for a new set of tokens */
|
||||
acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Acquires a token from the authority using OAuth2.0 device code flow */
|
||||
acquireTokenByDeviceCode(request: DeviceCodeRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/**
|
||||
* Acquires tokens with password grant by exchanging client applications username and password for credentials
|
||||
* @deprecated - Use a more secure flow instead
|
||||
*/
|
||||
acquireTokenByUsernamePassword(request: UsernamePasswordRequest): Promise<AuthenticationResult | null>;
|
||||
|
||||
/** Gets the token cache for the application */
|
||||
getTokenCache(): TokenCache;
|
||||
|
||||
/** Returns the logger instance */
|
||||
getLogger(): Logger;
|
||||
|
||||
/** Replaces the default logger set in configurations with new Logger with new configurations */
|
||||
setLogger(logger: Logger): void;
|
||||
|
||||
/** Clear the cache */
|
||||
clearCache(): void;
|
||||
|
||||
/** Gets all cached accounts */
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
|
||||
/** Removes cache artifacts associated with the given account */
|
||||
signOut(request: SignOutRequest): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { PublicClientApplication } from "@azure/msal-node";
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id",
|
||||
authority: "https://login.microsoftonline.com/common"
|
||||
},
|
||||
cache: {
|
||||
cachePlugin: // optional cache plugin
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
loggerCallback: (level, message) => {
|
||||
console.log(message);
|
||||
},
|
||||
piiLoggingEnabled: false,
|
||||
logLevel: LogLevel.Info
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Authorization Code Flow
|
||||
|
||||
Standard OAuth 2.0 authorization code flow with PKCE for secure authentication.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request to generate authorization URL (first step of authorization code flow)
|
||||
*/
|
||||
type AuthorizationUrlRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** URI where the authorization server will redirect after user authorization */
|
||||
redirectUri: string;
|
||||
/** Prompt behavior for user interaction */
|
||||
prompt?: PromptValue;
|
||||
/** Account to use for authentication */
|
||||
account?: AccountInfo;
|
||||
/** Login hint for pre-filling username */
|
||||
loginHint?: string;
|
||||
/** Domain hint for federated authentication */
|
||||
domainHint?: string;
|
||||
/** Additional query parameters for the authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** PKCE code challenge for security */
|
||||
codeChallenge?: string;
|
||||
/** PKCE code challenge method */
|
||||
codeChallengeMethod?: string;
|
||||
/** State parameter for CSRF protection */
|
||||
state?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request to exchange authorization code for tokens (second step of authorization code flow)
|
||||
*/
|
||||
type AuthorizationCodeRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** URI where the authorization server redirected after user authorization */
|
||||
redirectUri: string;
|
||||
/** Authorization code received from authorization server */
|
||||
code: string;
|
||||
/** State parameter for CSRF protection validation */
|
||||
state?: string;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
/** Additional parameters for token request */
|
||||
tokenQueryParameters?: Record<string, string>;
|
||||
/** PKCE code verifier for security validation */
|
||||
codeVerifier?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Step 1: Generate authorization URL
|
||||
const authCodeUrlParameters = {
|
||||
scopes: ["user.read", "mail.read"],
|
||||
redirectUri: "http://localhost:3000/redirect",
|
||||
prompt: "select_account" as PromptValue
|
||||
};
|
||||
|
||||
const authUrl = await pca.getAuthCodeUrl(authCodeUrlParameters);
|
||||
console.log("Navigate to:", authUrl);
|
||||
|
||||
// Step 2: Exchange authorization code for tokens
|
||||
const tokenRequest = {
|
||||
code: "authorization-code-from-callback", // Extract from callback URL
|
||||
scopes: ["user.read", "mail.read"],
|
||||
redirectUri: "http://localhost:3000/redirect"
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByCode(tokenRequest);
|
||||
console.log("Access token:", response.accessToken);
|
||||
console.log("Account:", response.account);
|
||||
} catch (error) {
|
||||
console.error("Token acquisition failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Interactive Authentication
|
||||
|
||||
Browser-based interactive authentication flow for desktop applications.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for interactive token acquisition with browser
|
||||
*/
|
||||
type InteractiveRequest = {
|
||||
/** Function to open browser with authorization URL */
|
||||
openBrowser: (url: string) => Promise<void>;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes?: string[];
|
||||
/** HTML template for success page */
|
||||
successTemplate?: string;
|
||||
/** HTML template for error page */
|
||||
errorTemplate?: string;
|
||||
/** Window handle for desktop applications */
|
||||
windowHandle?: Buffer;
|
||||
/** Custom loopback client for handling redirects */
|
||||
loopbackClient?: ILoopbackClient;
|
||||
/** Additional query parameters for the authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** Prompt behavior for user interaction */
|
||||
prompt?: PromptValue;
|
||||
/** Login hint for pre-filling username */
|
||||
loginHint?: string;
|
||||
/** Domain hint for federated authentication */
|
||||
domainHint?: string;
|
||||
/** Account to use for authentication */
|
||||
account?: AccountInfo;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { open } from "open"; // For opening browser
|
||||
|
||||
const interactiveRequest = {
|
||||
scopes: ["user.read"],
|
||||
openBrowser: async (url: string) => {
|
||||
await open(url);
|
||||
},
|
||||
successTemplate: "<h1>Authentication successful! You can close this window.</h1>",
|
||||
errorTemplate: "<h1>Authentication failed. Please try again.</h1>"
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenInteractive(interactiveRequest);
|
||||
console.log("Access token:", response.accessToken);
|
||||
} catch (error) {
|
||||
console.error("Interactive authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Device Code Flow
|
||||
|
||||
OAuth 2.0 device code flow for devices with limited input capabilities.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for device code flow authentication
|
||||
*/
|
||||
type DeviceCodeRequest = {
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Callback function to display device code to user */
|
||||
deviceCodeCallback: (response: DeviceCodeResponse) => void;
|
||||
/** Flag to cancel the device code flow polling */
|
||||
cancel?: boolean;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Additional query parameters for the device authorization request */
|
||||
extraQueryParameters?: Record<string, string>;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Response containing device code information for user
|
||||
*/
|
||||
type DeviceCodeResponse = {
|
||||
/** Device code for internal polling */
|
||||
deviceCode: string;
|
||||
/** User-friendly code to display to user */
|
||||
userCode: string;
|
||||
/** URL where user should navigate to enter the user code */
|
||||
verificationUri: string;
|
||||
/** Complete verification URL including user code (optional) */
|
||||
verificationUriComplete?: string;
|
||||
/** Number of seconds the device code is valid */
|
||||
expiresIn: number;
|
||||
/** Minimum number of seconds to wait between polling requests */
|
||||
interval: number;
|
||||
/** Message to display to user */
|
||||
message: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
const deviceCodeRequest = {
|
||||
scopes: ["user.read"],
|
||||
deviceCodeCallback: (response) => {
|
||||
console.log("Please visit:", response.verificationUri);
|
||||
console.log("And enter code:", response.userCode);
|
||||
console.log("Or visit:", response.verificationUriComplete);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenByDeviceCode(deviceCodeRequest);
|
||||
if (response) {
|
||||
console.log("Access token:", response.accessToken);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Device code authentication failed:", error);
|
||||
}
|
||||
```
|
||||
|
||||
### Silent Token Acquisition
|
||||
|
||||
Silent token acquisition from cache or using refresh tokens.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request for silent token acquisition from cache
|
||||
*/
|
||||
type SilentFlowRequest = {
|
||||
/** Account to acquire token for */
|
||||
account: AccountInfo;
|
||||
/** Array of scopes the application is requesting access to */
|
||||
scopes: string[];
|
||||
/** Force refresh from authority instead of using cache */
|
||||
forceRefresh?: boolean;
|
||||
/** Authority URL to override default */
|
||||
authority?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
/** Claims request for additional token claims */
|
||||
claims?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Get accounts from cache
|
||||
const accounts = await pca.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
const silentRequest = {
|
||||
account: accounts[0],
|
||||
scopes: ["user.read"],
|
||||
forceRefresh: false // Use cache if available
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await pca.acquireTokenSilent(silentRequest);
|
||||
console.log("Access token from cache:", response.accessToken);
|
||||
} catch (error) {
|
||||
// Silent acquisition failed, fall back to interactive
|
||||
console.log("Silent acquisition failed, using interactive flow");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sign Out
|
||||
|
||||
Sign out functionality to clear user session and cache.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Request to sign out user
|
||||
*/
|
||||
type SignOutRequest = {
|
||||
/** Account to sign out */
|
||||
account: AccountInfo;
|
||||
/** Post logout redirect URI */
|
||||
postLogoutRedirectUri?: string;
|
||||
/** Correlation ID for tracking requests */
|
||||
correlationId?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
const accounts = await pca.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
const signOutRequest = {
|
||||
account: accounts[0],
|
||||
postLogoutRedirectUri: "http://localhost:3000/signedout"
|
||||
};
|
||||
|
||||
try {
|
||||
await pca.signOut(signOutRequest);
|
||||
console.log("User signed out successfully");
|
||||
} catch (error) {
|
||||
console.error("Sign out failed:", error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Additional Features
|
||||
|
||||
### Account Management
|
||||
|
||||
```typescript { .api }
|
||||
/** Get all cached accounts */
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
|
||||
/** Clear all cache entries */
|
||||
clearCache(): void;
|
||||
```
|
||||
|
||||
### Logger Configuration
|
||||
|
||||
```typescript { .api }
|
||||
/** Get current logger instance */
|
||||
getLogger(): Logger;
|
||||
|
||||
/** Set new logger instance */
|
||||
setLogger(logger: Logger): void;
|
||||
```
|
||||
|
||||
### Token Cache Access
|
||||
|
||||
```typescript { .api }
|
||||
/** Get token cache instance for advanced operations */
|
||||
getTokenCache(): TokenCache;
|
||||
```
|
||||
681
.tessl/tiles/tessl/npm-azure--msal-node/docs/token-cache.md
Normal file
681
.tessl/tiles/tessl/npm-azure--msal-node/docs/token-cache.md
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
# Token Cache Management
|
||||
|
||||
MSAL Node provides a comprehensive token caching system that stores authentication artifacts in memory with support for serialization, distributed caching, and custom cache plugins. The cache system improves performance by avoiding unnecessary network requests and enables silent token acquisition.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### TokenCache Class
|
||||
|
||||
Main token cache implementation providing in-memory storage with serialization support.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Token cache interface for basic cache operations
|
||||
*/
|
||||
interface ITokenCache {
|
||||
/** Get all cached accounts */
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
/** Get account by home account ID */
|
||||
getAccountByHomeId(homeAccountId: string): Promise<AccountInfo | null>;
|
||||
/** Get account by local account ID */
|
||||
getAccountByLocalId(localAccountId: string): Promise<AccountInfo | null>;
|
||||
/** Remove account and associated tokens from cache */
|
||||
removeAccount(account: AccountInfo): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main token cache class with serialization support
|
||||
*/
|
||||
class TokenCache implements ITokenCache, ISerializableTokenCache {
|
||||
/** Get all cached accounts */
|
||||
getAllAccounts(): Promise<AccountInfo[]>;
|
||||
|
||||
/** Get account by home account ID */
|
||||
getAccountByHomeId(homeAccountId: string): Promise<AccountInfo | null>;
|
||||
|
||||
/** Get account by local account ID */
|
||||
getAccountByLocalId(localAccountId: string): Promise<AccountInfo | null>;
|
||||
|
||||
/** Remove account and associated tokens from cache */
|
||||
removeAccount(account: AccountInfo): Promise<void>;
|
||||
|
||||
/** Serialize cache to JSON string */
|
||||
serialize(): Promise<string>;
|
||||
|
||||
/** Deserialize cache from JSON string */
|
||||
deserialize(cache: string): Promise<void>;
|
||||
|
||||
/** Get all cached access tokens */
|
||||
getAllAccessTokens(): Promise<AccessTokenEntity[]>;
|
||||
|
||||
/** Get all cached refresh tokens */
|
||||
getAllRefreshTokens(): Promise<RefreshTokenEntity[]>;
|
||||
|
||||
/** Get all cached ID tokens */
|
||||
getAllIdTokens(): Promise<IdTokenEntity[]>;
|
||||
|
||||
/** Clear all cache entries */
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import { PublicClientApplication } from "@azure/msal-node";
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id"
|
||||
}
|
||||
});
|
||||
|
||||
// Get token cache instance
|
||||
const cache = pca.getTokenCache();
|
||||
|
||||
// Get all accounts
|
||||
const accounts = await cache.getAllAccounts();
|
||||
console.log("Cached accounts:", accounts.length);
|
||||
|
||||
// Get specific account
|
||||
if (accounts.length > 0) {
|
||||
const account = await cache.getAccountByHomeId(accounts[0].homeAccountId);
|
||||
console.log("Account:", account?.username);
|
||||
}
|
||||
|
||||
// Remove account from cache
|
||||
if (accounts.length > 0) {
|
||||
await cache.removeAccount(accounts[0]);
|
||||
console.log("Account removed from cache");
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Serialization
|
||||
|
||||
Serialize and deserialize cache for persistent storage.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Interface for serializable token cache
|
||||
*/
|
||||
interface ISerializableTokenCache {
|
||||
/** Serialize cache to JSON string */
|
||||
serialize(): Promise<string>;
|
||||
/** Deserialize cache from JSON string */
|
||||
deserialize(cache: string): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache serialization types
|
||||
*/
|
||||
type JsonCache = {
|
||||
/** Account entities indexed by key */
|
||||
Account: Record<string, SerializedAccountEntity>;
|
||||
/** ID token entities indexed by key */
|
||||
IdToken: Record<string, SerializedIdTokenEntity>;
|
||||
/** Access token entities indexed by key */
|
||||
AccessToken: Record<string, SerializedAccessTokenEntity>;
|
||||
/** Refresh token entities indexed by key */
|
||||
RefreshToken: Record<string, SerializedRefreshTokenEntity>;
|
||||
/** App metadata entities indexed by key */
|
||||
AppMetadata: Record<string, SerializedAppMetadataEntity>;
|
||||
};
|
||||
|
||||
/**
|
||||
* In-memory cache representation
|
||||
*/
|
||||
type InMemoryCache = {
|
||||
/** Account entities */
|
||||
accounts: Record<string, AccountEntity>;
|
||||
/** ID token entities */
|
||||
idTokens: Record<string, IdTokenEntity>;
|
||||
/** Access token entities */
|
||||
accessTokens: Record<string, AccessTokenEntity>;
|
||||
/** Refresh token entities */
|
||||
refreshTokens: Record<string, RefreshTokenEntity>;
|
||||
/** App metadata entities */
|
||||
appMetadata: Record<string, AppMetadataEntity>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Key-value store for cache operations
|
||||
*/
|
||||
type CacheKVStore = Record<string, ValidCacheType>;
|
||||
|
||||
/**
|
||||
* Valid cache value types
|
||||
*/
|
||||
type ValidCacheType =
|
||||
| AccountEntity
|
||||
| IdTokenEntity
|
||||
| AccessTokenEntity
|
||||
| RefreshTokenEntity
|
||||
| AppMetadataEntity;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import fs from "fs/promises";
|
||||
|
||||
const cache = pca.getTokenCache();
|
||||
|
||||
// Serialize cache to file
|
||||
const serializedCache = await cache.serialize();
|
||||
await fs.writeFile("./token-cache.json", serializedCache);
|
||||
console.log("Cache saved to file");
|
||||
|
||||
// Load cache from file
|
||||
try {
|
||||
const cacheData = await fs.readFile("./token-cache.json", "utf8");
|
||||
await cache.deserialize(cacheData);
|
||||
console.log("Cache loaded from file");
|
||||
} catch (error) {
|
||||
console.log("No existing cache file found");
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
await cache.clear();
|
||||
console.log("Cache cleared");
|
||||
```
|
||||
|
||||
### Serialized Entity Types
|
||||
|
||||
Detailed structure of cached entities for serialization.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Serialized account entity
|
||||
*/
|
||||
type SerializedAccountEntity = {
|
||||
/** Home account ID (user identifier across tenants) */
|
||||
home_account_id: string;
|
||||
/** Environment (authority host) */
|
||||
environment: string;
|
||||
/** Realm (tenant ID) */
|
||||
realm: string;
|
||||
/** Local account ID (user identifier within tenant) */
|
||||
local_account_id: string;
|
||||
/** Username (UPN or email) */
|
||||
username: string;
|
||||
/** Account type */
|
||||
authority_type: string;
|
||||
/** Display name */
|
||||
name?: string;
|
||||
/** Last modification timestamp */
|
||||
last_modification_time?: string;
|
||||
/** Additional client info */
|
||||
client_info?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialized ID token entity
|
||||
*/
|
||||
type SerializedIdTokenEntity = {
|
||||
/** Home account ID */
|
||||
home_account_id: string;
|
||||
/** Environment */
|
||||
environment: string;
|
||||
/** Credential type */
|
||||
credential_type: string;
|
||||
/** Client ID */
|
||||
client_id: string;
|
||||
/** ID token JWT */
|
||||
secret: string;
|
||||
/** Realm */
|
||||
realm: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialized access token entity
|
||||
*/
|
||||
type SerializedAccessTokenEntity = {
|
||||
/** Home account ID */
|
||||
home_account_id: string;
|
||||
/** Environment */
|
||||
environment: string;
|
||||
/** Credential type */
|
||||
credential_type: string;
|
||||
/** Client ID */
|
||||
client_id: string;
|
||||
/** Access token */
|
||||
secret: string;
|
||||
/** Realm */
|
||||
realm: string;
|
||||
/** Target scopes */
|
||||
target: string;
|
||||
/** Expiration timestamp */
|
||||
expires_on: string;
|
||||
/** Extended expiration timestamp */
|
||||
extended_expires_on?: string;
|
||||
/** Cached at timestamp */
|
||||
cached_at: string;
|
||||
/** Token type (Bearer) */
|
||||
token_type?: string;
|
||||
/** Key ID for key-based tokens */
|
||||
key_id?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialized refresh token entity
|
||||
*/
|
||||
type SerializedRefreshTokenEntity = {
|
||||
/** Home account ID */
|
||||
home_account_id: string;
|
||||
/** Environment */
|
||||
environment: string;
|
||||
/** Credential type */
|
||||
credential_type: string;
|
||||
/** Client ID */
|
||||
client_id: string;
|
||||
/** Refresh token */
|
||||
secret: string;
|
||||
/** Family ID for family of client IDs */
|
||||
family_id?: string;
|
||||
/** Target scopes */
|
||||
target?: string;
|
||||
/** Realm */
|
||||
realm?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialized app metadata entity
|
||||
*/
|
||||
type SerializedAppMetadataEntity = {
|
||||
/** Client ID */
|
||||
client_id: string;
|
||||
/** Environment */
|
||||
environment: string;
|
||||
/** Family ID */
|
||||
family_id?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### Cache Plugin System
|
||||
|
||||
Pluggable cache system for custom storage backends.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Cache plugin interface for custom cache implementations
|
||||
*/
|
||||
interface ICachePlugin {
|
||||
/** Called before cache access */
|
||||
beforeCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
|
||||
/** Called after cache access */
|
||||
afterCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token cache context provided to cache plugins
|
||||
*/
|
||||
type TokenCacheContext = {
|
||||
/** Token cache instance */
|
||||
tokenCache: ISerializableTokenCache;
|
||||
/** Whether cache has changed */
|
||||
hasChanged: boolean;
|
||||
/** Client ID of the application */
|
||||
clientId: string;
|
||||
/** Account information if available */
|
||||
account?: AccountInfo;
|
||||
/** Requested scopes */
|
||||
scopes?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache options for configuring cache behavior
|
||||
*/
|
||||
type CacheOptions = {
|
||||
/** Cache plugin for custom storage */
|
||||
cachePlugin?: ICachePlugin;
|
||||
/**
|
||||
* @deprecated claims-based-caching functionality will be removed in the next version
|
||||
*/
|
||||
claimsBasedCachingEnabled?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import fs from "fs/promises";
|
||||
|
||||
// Custom file-based cache plugin
|
||||
class FileCachePlugin implements ICachePlugin {
|
||||
private cacheFilePath = "./msal-cache.json";
|
||||
|
||||
async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
try {
|
||||
const cacheData = await fs.readFile(this.cacheFilePath, "utf8");
|
||||
await cacheContext.tokenCache.deserialize(cacheData);
|
||||
} catch (error) {
|
||||
// Cache file doesn't exist yet
|
||||
console.log("No existing cache file found");
|
||||
}
|
||||
}
|
||||
|
||||
async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
if (cacheContext.hasChanged) {
|
||||
const serializedCache = await cacheContext.tokenCache.serialize();
|
||||
await fs.writeFile(this.cacheFilePath, serializedCache);
|
||||
console.log("Cache updated and saved to file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use custom cache plugin
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id"
|
||||
},
|
||||
cache: {
|
||||
cachePlugin: new FileCachePlugin()
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Distributed Cache Support
|
||||
|
||||
Distributed cache plugin for Redis and other external cache systems.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Interface for external cache clients (Redis, etc.)
|
||||
*/
|
||||
interface ICacheClient {
|
||||
/** Retrieve value from cache using key */
|
||||
get(key: string): Promise<string>;
|
||||
/** Save value to cache using key */
|
||||
set(key: string, value: string): Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for cache partitioning strategies
|
||||
*/
|
||||
interface IPartitionManager {
|
||||
/** Get partition key for current context */
|
||||
getKey(): Promise<string>;
|
||||
/** Extract partition key from account entity */
|
||||
extractKey(accountEntity: AccountEntity): Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distributed cache plugin for external cache systems like Redis, DynamoDB, etc.
|
||||
*/
|
||||
class DistributedCachePlugin implements ICachePlugin {
|
||||
constructor(client: ICacheClient, partitionManager: IPartitionManager);
|
||||
|
||||
/** Called before cache access to deserialize from external cache */
|
||||
beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void>;
|
||||
|
||||
/** Called after cache access to serialize to external cache */
|
||||
afterCacheAccess(cacheContext: TokenCacheContext): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
import Redis from "ioredis";
|
||||
|
||||
// Redis cache client implementation
|
||||
class RedisCacheClient implements ICacheClient {
|
||||
private redis: Redis;
|
||||
|
||||
constructor(redisUrl: string) {
|
||||
this.redis = new Redis(redisUrl);
|
||||
}
|
||||
|
||||
async get(key: string): Promise<string | null> {
|
||||
return await this.redis.get(key);
|
||||
}
|
||||
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
await this.redis.set(key, value, "EX", 3600); // 1 hour expiration
|
||||
}
|
||||
}
|
||||
|
||||
// Simple partition manager
|
||||
class SimplePartitionManager implements IPartitionManager {
|
||||
private clientId: string;
|
||||
|
||||
constructor(clientId: string) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
getKey(): string {
|
||||
return `msal-cache:${this.clientId}`;
|
||||
}
|
||||
|
||||
extractKey(accountEntity: AccountEntity): string {
|
||||
return `msal-cache:${this.clientId}:${accountEntity.homeAccountId}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Use distributed cache
|
||||
const redisClient = new RedisCacheClient("redis://localhost:6379");
|
||||
const partitionManager = new SimplePartitionManager("your-client-id");
|
||||
const distributedCachePlugin = new DistributedCachePlugin(redisClient, partitionManager);
|
||||
|
||||
const pca = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: "your-client-id"
|
||||
},
|
||||
cache: {
|
||||
cachePlugin: distributedCachePlugin
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Account Management
|
||||
|
||||
Advanced account management operations.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Account information structure
|
||||
*/
|
||||
type AccountInfo = {
|
||||
/** Home account ID (unique across tenants) */
|
||||
homeAccountId: string;
|
||||
/** Environment (authority host) */
|
||||
environment: string;
|
||||
/** Tenant ID */
|
||||
tenantId: string;
|
||||
/** Username (UPN or email) */
|
||||
username: string;
|
||||
/** Local account ID (unique within tenant) */
|
||||
localAccountId: string;
|
||||
/** Display name */
|
||||
name?: string;
|
||||
/** ID token claims */
|
||||
idTokenClaims?: IdTokenClaims;
|
||||
};
|
||||
|
||||
/**
|
||||
* ID token claims structure
|
||||
*/
|
||||
type IdTokenClaims = {
|
||||
/** Issuer */
|
||||
iss?: string;
|
||||
/** Subject (user ID) */
|
||||
sub?: string;
|
||||
/** Audience */
|
||||
aud?: string;
|
||||
/** Expiration time */
|
||||
exp?: number;
|
||||
/** Issued at time */
|
||||
iat?: number;
|
||||
/** Authentication time */
|
||||
auth_time?: number;
|
||||
/** Nonce */
|
||||
nonce?: string;
|
||||
/** Preferred username */
|
||||
preferred_username?: string;
|
||||
/** Name */
|
||||
name?: string;
|
||||
/** Email */
|
||||
email?: string;
|
||||
/** Object ID */
|
||||
oid?: string;
|
||||
/** Tenant ID */
|
||||
tid?: string;
|
||||
/** Version */
|
||||
ver?: string;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// Account management operations
|
||||
const accounts = await cache.getAllAccounts();
|
||||
|
||||
// Filter accounts by tenant
|
||||
const tenantAccounts = accounts.filter(account =>
|
||||
account.tenantId === "specific-tenant-id"
|
||||
);
|
||||
|
||||
// Find account by username
|
||||
const userAccount = accounts.find(account =>
|
||||
account.username === "user@domain.com"
|
||||
);
|
||||
|
||||
// Get detailed account information
|
||||
if (userAccount) {
|
||||
console.log("Account details:");
|
||||
console.log("- Home Account ID:", userAccount.homeAccountId);
|
||||
console.log("- Tenant ID:", userAccount.tenantId);
|
||||
console.log("- Username:", userAccount.username);
|
||||
console.log("- Display Name:", userAccount.name);
|
||||
|
||||
if (userAccount.idTokenClaims) {
|
||||
console.log("- Email:", userAccount.idTokenClaims.email);
|
||||
console.log("- Object ID:", userAccount.idTokenClaims.oid);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove specific account
|
||||
if (userAccount) {
|
||||
await cache.removeAccount(userAccount);
|
||||
console.log("Account removed from cache");
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Performance Optimization
|
||||
|
||||
Best practices for cache performance and management.
|
||||
|
||||
```typescript { .api }
|
||||
/**
|
||||
* Cache performance considerations
|
||||
*/
|
||||
type CachePerformanceOptions = {
|
||||
/** Enable cache compression */
|
||||
enableCompression?: boolean;
|
||||
/** Cache size limits */
|
||||
maxCacheSize?: number;
|
||||
/** Token expiration buffer */
|
||||
expirationBuffer?: number;
|
||||
/** Cleanup interval */
|
||||
cleanupInterval?: number;
|
||||
};
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```typescript
|
||||
// High-performance cache plugin with compression and cleanup
|
||||
class OptimizedCachePlugin implements ICachePlugin {
|
||||
private cacheFile = "./optimized-cache.json";
|
||||
private lastCleanup = Date.now();
|
||||
private cleanupInterval = 60000; // 1 minute
|
||||
|
||||
async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
// Periodic cleanup of expired tokens
|
||||
if (Date.now() - this.lastCleanup > this.cleanupInterval) {
|
||||
await this.cleanupExpiredTokens(cacheContext);
|
||||
this.lastCleanup = Date.now();
|
||||
}
|
||||
|
||||
// Load cache from file
|
||||
try {
|
||||
const cacheData = await fs.readFile(this.cacheFile, "utf8");
|
||||
await cacheContext.tokenCache.deserialize(cacheData);
|
||||
} catch (error) {
|
||||
// No cache file exists
|
||||
}
|
||||
}
|
||||
|
||||
async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
|
||||
if (cacheContext.hasChanged) {
|
||||
const serializedCache = await cacheContext.tokenCache.serialize();
|
||||
|
||||
// Compress cache data (optional)
|
||||
const compressedCache = this.compressCache(serializedCache);
|
||||
|
||||
await fs.writeFile(this.cacheFile, compressedCache);
|
||||
}
|
||||
}
|
||||
|
||||
private async cleanupExpiredTokens(cacheContext: TokenCacheContext): Promise<void> {
|
||||
// Implementation to remove expired access tokens
|
||||
const accessTokens = await cacheContext.tokenCache.getAllAccessTokens();
|
||||
const now = Date.now() / 1000;
|
||||
|
||||
for (const token of accessTokens) {
|
||||
if (token.expiresOn && parseInt(token.expiresOn) < now) {
|
||||
// Remove expired token
|
||||
console.log("Removing expired token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private compressCache(cache: string): string {
|
||||
// Optional: implement compression
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cache Storage Patterns
|
||||
|
||||
### In-Memory Only
|
||||
|
||||
```typescript
|
||||
// Default behavior - cache only exists in memory
|
||||
const pca = new PublicClientApplication({
|
||||
auth: { clientId: "your-client-id" }
|
||||
// No cache plugin = in-memory only
|
||||
});
|
||||
```
|
||||
|
||||
### File-Based Persistence
|
||||
|
||||
```typescript
|
||||
// File-based cache for desktop applications
|
||||
const pca = new PublicClientApplication({
|
||||
auth: { clientId: "your-client-id" },
|
||||
cache: { cachePlugin: new FileCachePlugin() }
|
||||
});
|
||||
```
|
||||
|
||||
### Distributed Cache
|
||||
|
||||
```typescript
|
||||
// Redis-based cache for scalable web applications
|
||||
const pca = new PublicClientApplication({
|
||||
auth: { clientId: "your-client-id" },
|
||||
cache: { cachePlugin: new DistributedCachePlugin(redisClient, partitionManager) }
|
||||
});
|
||||
```
|
||||
|
||||
### Encrypted Cache
|
||||
|
||||
```typescript
|
||||
// Encrypted file cache for sensitive environments
|
||||
const pca = new PublicClientApplication({
|
||||
auth: { clientId: "your-client-id" },
|
||||
cache: { cachePlugin: new EncryptedFileCachePlugin(encryptionKey) }
|
||||
});
|
||||
```
|
||||
7
.tessl/tiles/tessl/npm-azure--msal-node/tile.json
Normal file
7
.tessl/tiles/tessl/npm-azure--msal-node/tile.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "tessl/npm-azure--msal-node",
|
||||
"version": "3.7.0",
|
||||
"docs": "docs/index.md",
|
||||
"describes": "pkg:npm/@azure/msal-node@3.7.3",
|
||||
"summary": "Microsoft Authentication Library for Node - A comprehensive Node.js authentication library for Microsoft identity platform supporting multiple OAuth 2.0 flows"
|
||||
}
|
||||
346
.tessl/tiles/tessl/npm-chart-js/docs/chart-management.md
Normal file
346
.tessl/tiles/tessl/npm-chart-js/docs/chart-management.md
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
# Chart Management
|
||||
|
||||
Core Chart class functionality for creating, updating, and managing chart instances.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Chart Constructor
|
||||
|
||||
Creates a new chart instance on a canvas element.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Creates a new chart instance
|
||||
* @param item - Canvas element, context, string ID, or other chart item
|
||||
* @param config - Chart configuration object
|
||||
*/
|
||||
constructor(
|
||||
item: ChartItem,
|
||||
config: ChartConfiguration
|
||||
): Chart;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
import { Chart } from "chart.js";
|
||||
|
||||
const ctx = document.getElementById('myChart').getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['A', 'B', 'C'],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
data: [1, 2, 3]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Update Chart
|
||||
|
||||
Updates the chart with new data or configuration changes.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Updates the chart
|
||||
* @param mode - Animation mode for the update
|
||||
* @returns void
|
||||
*/
|
||||
update(mode?: UpdateMode): void;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Update chart data
|
||||
chart.data.datasets[0].data = [4, 5, 6];
|
||||
chart.update('active'); // Animates the update
|
||||
|
||||
// Update without animation
|
||||
chart.update('none');
|
||||
```
|
||||
|
||||
### Render Chart
|
||||
|
||||
Triggers a re-render of the chart.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Renders the chart
|
||||
* @returns void
|
||||
*/
|
||||
render(): void;
|
||||
```
|
||||
|
||||
### Draw Chart
|
||||
|
||||
Draws the chart without animation.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Draws the chart without animation
|
||||
* @returns void
|
||||
*/
|
||||
draw(): void;
|
||||
```
|
||||
|
||||
### Resize Chart
|
||||
|
||||
Resizes the chart to fit its container or specified dimensions.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Resizes the chart
|
||||
* @param width - Optional new width
|
||||
* @param height - Optional new height
|
||||
* @returns void
|
||||
*/
|
||||
resize(width?: number, height?: number): void;
|
||||
```
|
||||
|
||||
### Clear Chart
|
||||
|
||||
Clears the chart canvas.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Clears the chart canvas
|
||||
* @returns Chart instance for chaining
|
||||
*/
|
||||
clear(): this;
|
||||
```
|
||||
|
||||
### Reset Chart
|
||||
|
||||
Resets the chart to its initial state.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Resets the chart to initial state
|
||||
* @returns void
|
||||
*/
|
||||
reset(): void;
|
||||
```
|
||||
|
||||
### Stop Animations
|
||||
|
||||
Stops all current animations.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Stops all chart animations
|
||||
* @returns Chart instance for chaining
|
||||
*/
|
||||
stop(): this;
|
||||
```
|
||||
|
||||
### Destroy Chart
|
||||
|
||||
Destroys the chart instance and cleans up resources.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Destroys the chart instance
|
||||
* @returns void
|
||||
*/
|
||||
destroy(): void;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Clean up when done
|
||||
chart.destroy();
|
||||
```
|
||||
|
||||
### Export Image
|
||||
|
||||
Exports the chart as a base64 encoded image.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Exports chart as base64 image
|
||||
* @param type - Image MIME type (default: 'image/png')
|
||||
* @param quality - Image quality for lossy formats
|
||||
* @returns Base64 encoded image string
|
||||
*/
|
||||
toBase64Image(type?: string, quality?: unknown): string;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Export as PNG
|
||||
const pngImage = chart.toBase64Image();
|
||||
|
||||
// Export as JPEG with quality
|
||||
const jpegImage = chart.toBase64Image('image/jpeg', 0.8);
|
||||
```
|
||||
|
||||
### Element Visibility
|
||||
|
||||
Control visibility of chart elements.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Hides chart elements
|
||||
* @param datasetIndex - Index of dataset
|
||||
* @param dataIndex - Optional index of specific data point
|
||||
* @returns void
|
||||
*/
|
||||
hide(datasetIndex: number, dataIndex?: number): void;
|
||||
|
||||
/**
|
||||
* Shows chart elements
|
||||
* @param datasetIndex - Index of dataset
|
||||
* @param dataIndex - Optional index of specific data point
|
||||
* @returns void
|
||||
*/
|
||||
show(datasetIndex: number, dataIndex?: number): void;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Hide entire dataset
|
||||
chart.hide(0);
|
||||
|
||||
// Hide specific data point
|
||||
chart.hide(0, 2);
|
||||
|
||||
// Show dataset
|
||||
chart.show(0);
|
||||
```
|
||||
|
||||
### Active Elements
|
||||
|
||||
Get and set active (highlighted) chart elements.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Gets currently active elements
|
||||
* @returns Array of active elements
|
||||
*/
|
||||
getActiveElements(): ActiveElement[];
|
||||
|
||||
/**
|
||||
* Sets active elements
|
||||
* @param active - Array of data points to make active
|
||||
* @returns void
|
||||
*/
|
||||
setActiveElements(active: ActiveDataPoint[]): void;
|
||||
```
|
||||
|
||||
### Static Methods
|
||||
|
||||
Class-level methods for chart management.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Gets chart instance by key
|
||||
* @param key - Chart ID, canvas element, or context
|
||||
* @returns Chart instance or undefined
|
||||
*/
|
||||
static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart | undefined;
|
||||
|
||||
/**
|
||||
* Registers chart components
|
||||
* @param items - Components to register
|
||||
* @returns void
|
||||
*/
|
||||
static register(...items: ChartComponentLike[]): void;
|
||||
|
||||
/**
|
||||
* Unregisters chart components
|
||||
* @param items - Components to unregister
|
||||
* @returns void
|
||||
*/
|
||||
static unregister(...items: ChartComponentLike[]): void;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
import { Chart, LinearScale, CategoryScale } from 'chart.js';
|
||||
|
||||
// Register components
|
||||
Chart.register(LinearScale, CategoryScale);
|
||||
|
||||
// Get chart by canvas ID
|
||||
const chart = Chart.getChart('myCanvas');
|
||||
```
|
||||
|
||||
### Instance Properties
|
||||
|
||||
Key properties available on chart instances.
|
||||
|
||||
```javascript { .api }
|
||||
interface Chart {
|
||||
// Read-only properties
|
||||
readonly id: string;
|
||||
readonly canvas: HTMLCanvasElement;
|
||||
readonly ctx: CanvasRenderingContext2D;
|
||||
readonly platform: BasePlatform;
|
||||
readonly config: ChartConfiguration;
|
||||
readonly scales: { [key: string]: Scale };
|
||||
|
||||
// Mutable properties
|
||||
data: ChartData;
|
||||
options: ChartOptions;
|
||||
}
|
||||
```
|
||||
|
||||
### Static Properties
|
||||
|
||||
Class-level properties and registries.
|
||||
|
||||
```javascript { .api }
|
||||
interface ChartStatic {
|
||||
defaults: Defaults;
|
||||
overrides: Overrides;
|
||||
version: string;
|
||||
instances: { [key: string]: Chart };
|
||||
registry: Registry;
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
type UpdateMode = 'resize' | 'reset' | 'none' | 'hide' | 'show' | 'default' | 'active';
|
||||
|
||||
interface ActiveElement {
|
||||
datasetIndex: number;
|
||||
index: number;
|
||||
element: Element;
|
||||
}
|
||||
|
||||
interface ActiveDataPoint {
|
||||
datasetIndex: number;
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface ChartConfiguration<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown> {
|
||||
type: TType;
|
||||
data: ChartData<TType, TData, TLabel>;
|
||||
options?: ChartOptions<TType>;
|
||||
plugins?: Plugin[];
|
||||
}
|
||||
|
||||
interface ChartData<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown> {
|
||||
labels?: TLabel[];
|
||||
datasets: ChartDataset<TType, TData>[];
|
||||
}
|
||||
|
||||
type ChartItem =
|
||||
| string
|
||||
| CanvasRenderingContext2D
|
||||
| HTMLCanvasElement
|
||||
| { canvas: HTMLCanvasElement }
|
||||
| ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>;
|
||||
```
|
||||
461
.tessl/tiles/tessl/npm-chart-js/docs/chart-types.md
Normal file
461
.tessl/tiles/tessl/npm-chart-js/docs/chart-types.md
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
# Chart Types
|
||||
|
||||
Built-in chart type controllers for different visualization patterns. Each controller handles dataset-specific data processing, element creation, and interaction handling.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Bar Charts
|
||||
|
||||
Creates bar and column charts for categorical data comparison.
|
||||
|
||||
```javascript { .api }
|
||||
class BarController extends DatasetController {
|
||||
static id: 'bar';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface BarChartConfiguration {
|
||||
type: 'bar';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: BarDataset[];
|
||||
};
|
||||
options?: BarChartOptions;
|
||||
}
|
||||
|
||||
interface BarDataset {
|
||||
label?: string;
|
||||
data: number[];
|
||||
backgroundColor?: Color | Color[];
|
||||
borderColor?: Color | Color[];
|
||||
borderWidth?: number | number[];
|
||||
borderRadius?: number | BorderRadius | (number | BorderRadius)[];
|
||||
borderSkipped?: string | string[];
|
||||
base?: number | number[];
|
||||
grouped?: boolean;
|
||||
indexAxis?: 'x' | 'y';
|
||||
maxBarThickness?: number;
|
||||
minBarLength?: number;
|
||||
order?: number;
|
||||
stack?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
|
||||
datasets: [{
|
||||
label: 'Sales',
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Line Charts
|
||||
|
||||
Creates line charts for showing trends over continuous data.
|
||||
|
||||
```javascript { .api }
|
||||
class LineController extends DatasetController {
|
||||
static id: 'line';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface LineChartConfiguration {
|
||||
type: 'line';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: LineDataset[];
|
||||
};
|
||||
options?: LineChartOptions;
|
||||
}
|
||||
|
||||
interface LineDataset {
|
||||
label?: string;
|
||||
data: (number | Point)[];
|
||||
backgroundColor?: Color;
|
||||
borderColor?: Color;
|
||||
borderWidth?: number;
|
||||
borderDash?: number[];
|
||||
borderDashOffset?: number;
|
||||
borderCapStyle?: CanvasLineCap;
|
||||
borderJoinStyle?: CanvasLineJoin;
|
||||
cubicInterpolationMode?: 'default' | 'monotone';
|
||||
fill?: boolean | string | number | object;
|
||||
lineTension?: number;
|
||||
pointBackgroundColor?: Color | Color[];
|
||||
pointBorderColor?: Color | Color[];
|
||||
pointBorderWidth?: number | number[];
|
||||
pointRadius?: number | number[];
|
||||
pointStyle?: PointStyle | PointStyle[];
|
||||
showLine?: boolean;
|
||||
spanGaps?: boolean | number;
|
||||
stepped?: boolean | 'before' | 'after' | 'middle';
|
||||
tension?: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
datasets: [{
|
||||
label: 'Temperature',
|
||||
data: [20, 25, 22, 28, 24, 30],
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
tension: 0.1,
|
||||
fill: true
|
||||
}]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Radar Charts
|
||||
|
||||
Creates radar charts for multivariate data comparison.
|
||||
|
||||
```javascript { .api }
|
||||
class RadarController extends DatasetController {
|
||||
static id: 'radar';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface RadarChartConfiguration {
|
||||
type: 'radar';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: RadarDataset[];
|
||||
};
|
||||
options?: RadarChartOptions;
|
||||
}
|
||||
|
||||
interface RadarDataset {
|
||||
label?: string;
|
||||
data: number[];
|
||||
backgroundColor?: Color;
|
||||
borderColor?: Color;
|
||||
borderWidth?: number;
|
||||
borderDash?: number[];
|
||||
borderDashOffset?: number;
|
||||
borderCapStyle?: CanvasLineCap;
|
||||
borderJoinStyle?: CanvasLineJoin;
|
||||
fill?: boolean | string | number;
|
||||
lineTension?: number;
|
||||
pointBackgroundColor?: Color | Color[];
|
||||
pointBorderColor?: Color | Color[];
|
||||
pointBorderWidth?: number | number[];
|
||||
pointRadius?: number | number[];
|
||||
pointStyle?: PointStyle | PointStyle[];
|
||||
tension?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Doughnut Charts
|
||||
|
||||
Creates doughnut charts for proportional data visualization.
|
||||
|
||||
```javascript { .api }
|
||||
class DoughnutController extends DatasetController {
|
||||
static id: 'doughnut';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface DoughnutChartConfiguration {
|
||||
type: 'doughnut';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: DoughnutDataset[];
|
||||
};
|
||||
options?: DoughnutChartOptions;
|
||||
}
|
||||
|
||||
interface DoughnutDataset {
|
||||
label?: string;
|
||||
data: number[];
|
||||
backgroundColor?: Color | Color[];
|
||||
borderColor?: Color | Color[];
|
||||
borderWidth?: number | number[];
|
||||
borderAlign?: 'center' | 'inner';
|
||||
borderRadius?: number | number[];
|
||||
circumference?: number;
|
||||
clip?: number | object;
|
||||
hoverBackgroundColor?: Color | Color[];
|
||||
hoverBorderColor?: Color | Color[];
|
||||
hoverBorderWidth?: number | number[];
|
||||
hoverOffset?: number | number[];
|
||||
offset?: number | number[];
|
||||
rotation?: number;
|
||||
spacing?: number;
|
||||
weight?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Pie Charts
|
||||
|
||||
Creates pie charts, extends doughnut charts with 100% circumference.
|
||||
|
||||
```javascript { .api }
|
||||
class PieController extends DoughnutController {
|
||||
static id: 'pie';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface PieChartConfiguration {
|
||||
type: 'pie';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: PieDataset[];
|
||||
};
|
||||
options?: PieChartOptions;
|
||||
}
|
||||
|
||||
// PieDataset extends DoughnutDataset
|
||||
type PieDataset = DoughnutDataset;
|
||||
```
|
||||
|
||||
### Polar Area Charts
|
||||
|
||||
Creates polar area charts for proportional data with equal angles.
|
||||
|
||||
```javascript { .api }
|
||||
class PolarAreaController extends DatasetController {
|
||||
static id: 'polarArea';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface PolarAreaChartConfiguration {
|
||||
type: 'polarArea';
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: PolarAreaDataset[];
|
||||
};
|
||||
options?: PolarAreaChartOptions;
|
||||
}
|
||||
|
||||
interface PolarAreaDataset {
|
||||
label?: string;
|
||||
data: number[];
|
||||
backgroundColor?: Color | Color[];
|
||||
borderColor?: Color | Color[];
|
||||
borderWidth?: number | number[];
|
||||
borderAlign?: 'center' | 'inner';
|
||||
hoverBackgroundColor?: Color | Color[];
|
||||
hoverBorderColor?: Color | Color[];
|
||||
hoverBorderWidth?: number | number[];
|
||||
}
|
||||
```
|
||||
|
||||
### Bubble Charts
|
||||
|
||||
Creates bubble charts for three-dimensional data visualization.
|
||||
|
||||
```javascript { .api }
|
||||
class BubbleController extends DatasetController {
|
||||
static id: 'bubble';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface BubbleChartConfiguration {
|
||||
type: 'bubble';
|
||||
data: {
|
||||
datasets: BubbleDataset[];
|
||||
};
|
||||
options?: BubbleChartOptions;
|
||||
}
|
||||
|
||||
interface BubbleDataset {
|
||||
label?: string;
|
||||
data: BubbleDataPoint[];
|
||||
backgroundColor?: Color | Color[];
|
||||
borderColor?: Color | Color[];
|
||||
borderWidth?: number | number[];
|
||||
hoverBackgroundColor?: Color | Color[];
|
||||
hoverBorderColor?: Color | Color[];
|
||||
hoverBorderWidth?: number | number[];
|
||||
hoverRadius?: number | number[];
|
||||
pointRadius?: number | number[];
|
||||
pointStyle?: PointStyle | PointStyle[];
|
||||
rotation?: number | number[];
|
||||
}
|
||||
|
||||
interface BubbleDataPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
r: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Scatter Charts
|
||||
|
||||
Creates scatter plots, extends line charts without connecting lines.
|
||||
|
||||
```javascript { .api }
|
||||
class ScatterController extends LineController {
|
||||
static id: 'scatter';
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
}
|
||||
|
||||
// Chart configuration
|
||||
interface ScatterChartConfiguration {
|
||||
type: 'scatter';
|
||||
data: {
|
||||
datasets: ScatterDataset[];
|
||||
};
|
||||
options?: ScatterChartOptions;
|
||||
}
|
||||
|
||||
// ScatterDataset extends LineDataset
|
||||
interface ScatterDataset extends LineDataset {
|
||||
data: ScatterDataPoint[];
|
||||
showLine?: boolean; // Typically false for scatter
|
||||
}
|
||||
|
||||
interface ScatterDataPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Scatter Dataset',
|
||||
data: [{
|
||||
x: -10,
|
||||
y: 0
|
||||
}, {
|
||||
x: 0,
|
||||
y: 10
|
||||
}, {
|
||||
x: 10,
|
||||
y: 5
|
||||
}],
|
||||
backgroundColor: 'rgba(255, 99, 132, 1)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: {
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
type ChartType = 'line' | 'bar' | 'radar' | 'doughnut' | 'pie' | 'polarArea' | 'bubble' | 'scatter';
|
||||
|
||||
interface DatasetController {
|
||||
static id: string;
|
||||
static defaults: object;
|
||||
static overrides: object;
|
||||
|
||||
constructor(chart: Chart, datasetIndex: number);
|
||||
|
||||
initialize(): void;
|
||||
update(mode: UpdateMode): void;
|
||||
draw(): void;
|
||||
reset(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
type Color = string | CanvasGradient | CanvasPattern;
|
||||
|
||||
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle' | HTMLImageElement | HTMLCanvasElement;
|
||||
|
||||
interface BorderRadius {
|
||||
topLeft: number;
|
||||
topRight: number;
|
||||
bottomLeft: number;
|
||||
bottomRight: number;
|
||||
}
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
// Chart-specific options interfaces
|
||||
interface BarChartOptions extends ChartOptions<'bar'> {
|
||||
indexAxis?: 'x' | 'y';
|
||||
skipNull?: boolean;
|
||||
grouped?: boolean;
|
||||
maxBarThickness?: number;
|
||||
categoryPercentage?: number;
|
||||
barPercentage?: number;
|
||||
}
|
||||
|
||||
interface LineChartOptions extends ChartOptions<'line'> {
|
||||
showLine?: boolean;
|
||||
spanGaps?: boolean | number;
|
||||
}
|
||||
|
||||
interface RadarChartOptions extends ChartOptions<'radar'> {
|
||||
scale?: RadialLinearScaleOptions;
|
||||
}
|
||||
|
||||
interface DoughnutChartOptions extends ChartOptions<'doughnut'> {
|
||||
circumference?: number;
|
||||
rotation?: number;
|
||||
cutout?: number | string;
|
||||
radius?: number | string;
|
||||
}
|
||||
|
||||
interface PieChartOptions extends DoughnutChartOptions {
|
||||
// Inherits from DoughnutChartOptions
|
||||
}
|
||||
|
||||
interface PolarAreaChartOptions extends ChartOptions<'polarArea'> {
|
||||
scale?: RadialLinearScaleOptions;
|
||||
}
|
||||
|
||||
interface BubbleChartOptions extends ChartOptions<'bubble'> {
|
||||
// Extends base chart options
|
||||
}
|
||||
|
||||
interface ScatterChartOptions extends LineChartOptions {
|
||||
showLine?: false; // Typically false for scatter
|
||||
}
|
||||
```
|
||||
291
.tessl/tiles/tessl/npm-chart-js/docs/index.md
Normal file
291
.tessl/tiles/tessl/npm-chart-js/docs/index.md
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# Chart.js
|
||||
|
||||
Chart.js is a comprehensive JavaScript charting library that enables developers to create responsive, interactive HTML5 charts using the Canvas element. It provides a wide variety of chart types including line, bar, radar, doughnut, pie, polar area, bubble, and scatter charts with extensive customization options for colors, animations, tooltips, legends, and data interactions.
|
||||
|
||||
## Package Information
|
||||
|
||||
- **Package Name**: chart.js
|
||||
- **Package Type**: npm
|
||||
- **Language**: JavaScript/TypeScript
|
||||
- **Installation**: `npm install chart.js`
|
||||
|
||||
## Core Imports
|
||||
|
||||
```javascript
|
||||
import { Chart } from "chart.js";
|
||||
```
|
||||
|
||||
Import with auto-registration of all components:
|
||||
|
||||
```javascript
|
||||
import Chart from "chart.js/auto";
|
||||
```
|
||||
|
||||
For tree-shaking, import components selectively:
|
||||
|
||||
```javascript
|
||||
import {
|
||||
Chart,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from "chart.js";
|
||||
|
||||
Chart.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
|
||||
```
|
||||
|
||||
CommonJS:
|
||||
|
||||
```javascript
|
||||
const { Chart } = require("chart.js");
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```javascript
|
||||
import { Chart } from "chart.js/auto";
|
||||
|
||||
// Create a basic bar chart
|
||||
const ctx = document.getElementById('myChart').getContext('2d');
|
||||
const myChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
||||
datasets: [{
|
||||
label: '# of Votes',
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 205, 86, 0.2)',
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(153, 102, 255, 0.2)',
|
||||
'rgba(255, 159, 64, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 205, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)',
|
||||
'rgba(255, 159, 64, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
Chart.js is built around several key components:
|
||||
|
||||
- **Chart Class**: The main class for creating and managing chart instances
|
||||
- **Controllers**: Chart-type-specific logic for data processing (BarController, LineController, etc.)
|
||||
- **Elements**: Visual components for rendering (BarElement, LineElement, PointElement, ArcElement)
|
||||
- **Scales**: Data-to-pixel conversion and axis rendering (LinearScale, CategoryScale, TimeScale, etc.)
|
||||
- **Plugins**: Extensible functionality system (Tooltip, Legend, Title, etc.)
|
||||
- **Registry**: Component registration and management system
|
||||
- **Platform**: Environment abstraction (DOM, Basic, Custom platforms)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Chart Management
|
||||
|
||||
Core Chart class functionality for creating, updating, and managing chart instances.
|
||||
|
||||
```javascript { .api }
|
||||
class Chart {
|
||||
constructor(item: ChartItem, config: ChartConfiguration);
|
||||
|
||||
// Instance methods
|
||||
update(mode?: UpdateMode): void;
|
||||
render(): void;
|
||||
resize(width?: number, height?: number): void;
|
||||
destroy(): void;
|
||||
toBase64Image(type?: string, quality?: unknown): string;
|
||||
|
||||
// Static methods
|
||||
static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart;
|
||||
static register(...items: ChartComponentLike[]): void;
|
||||
static unregister(...items: ChartComponentLike[]): void;
|
||||
}
|
||||
```
|
||||
|
||||
[Chart Management](./chart-management.md)
|
||||
|
||||
### Chart Types
|
||||
|
||||
Built-in chart type controllers for different visualization patterns.
|
||||
|
||||
```javascript { .api }
|
||||
// Available chart types
|
||||
type ChartType = 'line' | 'bar' | 'radar' | 'doughnut' | 'pie' | 'polarArea' | 'bubble' | 'scatter';
|
||||
|
||||
// Controllers
|
||||
class BarController extends DatasetController { }
|
||||
class LineController extends DatasetController { }
|
||||
class RadarController extends DatasetController { }
|
||||
class DoughnutController extends DatasetController { }
|
||||
class PieController extends DoughnutController { }
|
||||
class PolarAreaController extends DatasetController { }
|
||||
class BubbleController extends DatasetController { }
|
||||
class ScatterController extends LineController { }
|
||||
```
|
||||
|
||||
[Chart Types](./chart-types.md)
|
||||
|
||||
### Scales
|
||||
|
||||
Data-to-pixel conversion and axis rendering for different data types.
|
||||
|
||||
```javascript { .api }
|
||||
// Available scale types
|
||||
type ScaleType = 'linear' | 'logarithmic' | 'category' | 'time' | 'timeseries' | 'radialLinear';
|
||||
|
||||
// Base scale interface
|
||||
interface Scale {
|
||||
getPixelForValue(value: unknown): number;
|
||||
getValueForPixel(pixel: number): unknown;
|
||||
getLabelForValue(value: unknown): string;
|
||||
getDecimalForPixel(pixel: number): number;
|
||||
}
|
||||
```
|
||||
|
||||
[Scales](./scales.md)
|
||||
|
||||
### Visual Elements
|
||||
|
||||
Core visual components for rendering chart elements.
|
||||
|
||||
```javascript { .api }
|
||||
// Element classes
|
||||
class BarElement extends Element { }
|
||||
class LineElement extends Element { }
|
||||
class PointElement extends Element { }
|
||||
class ArcElement extends Element { }
|
||||
|
||||
// Base element interface
|
||||
interface Element {
|
||||
draw(ctx: CanvasRenderingContext2D): void;
|
||||
inRange(mouseX: number, mouseY: number): boolean;
|
||||
inXRange(mouseX: number): boolean;
|
||||
inYRange(mouseY: number): boolean;
|
||||
getCenterPoint(): Point;
|
||||
}
|
||||
```
|
||||
|
||||
[Visual Elements](./visual-elements.md)
|
||||
|
||||
### Plugins
|
||||
|
||||
Built-in and extensible plugin system for additional functionality.
|
||||
|
||||
```javascript { .api }
|
||||
// Built-in plugins
|
||||
const Title: Plugin;
|
||||
const Legend: Plugin;
|
||||
const Tooltip: Plugin;
|
||||
const SubTitle: Plugin;
|
||||
const Filler: Plugin;
|
||||
const Decimation: Plugin;
|
||||
const Colors: Plugin;
|
||||
|
||||
// Plugin interface
|
||||
interface Plugin {
|
||||
id: string;
|
||||
beforeInit?(chart: Chart, args: EmptyObject, options: O): void;
|
||||
afterInit?(chart: Chart, args: EmptyObject, options: O): void;
|
||||
beforeUpdate?(chart: Chart, args: UpdateArgs, options: O): void;
|
||||
afterUpdate?(chart: Chart, args: UpdateArgs, options: O): void;
|
||||
// ... more lifecycle hooks
|
||||
}
|
||||
```
|
||||
|
||||
[Plugins](./plugins.md)
|
||||
|
||||
### Utilities
|
||||
|
||||
Helper functions for common operations, calculations, and canvas manipulation.
|
||||
|
||||
```javascript { .api }
|
||||
// Color utilities
|
||||
function color(value: string | CanvasGradient | CanvasPattern): Color;
|
||||
function getHoverColor(color: Color): Color;
|
||||
|
||||
// Core utilities
|
||||
function isArray(value: unknown): value is unknown[];
|
||||
function isObject(value: unknown): value is object;
|
||||
function valueOrDefault<T>(value: T | undefined, defaultValue: T): T;
|
||||
|
||||
// Canvas utilities
|
||||
function clearCanvas(ctx: CanvasRenderingContext2D): void;
|
||||
function drawPoint(ctx: CanvasRenderingContext2D, options: PointOptions, x: number, y: number): void;
|
||||
```
|
||||
|
||||
[Utilities](./utilities.md)
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
interface ChartConfiguration<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown> {
|
||||
type: TType;
|
||||
data: ChartData<TType, TData, TLabel>;
|
||||
options?: ChartOptions<TType>;
|
||||
plugins?: Plugin[];
|
||||
}
|
||||
|
||||
interface ChartData<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown> {
|
||||
labels?: TLabel[];
|
||||
datasets: ChartDataset<TType, TData>[];
|
||||
}
|
||||
|
||||
interface ChartOptions<TType extends ChartType = ChartType> {
|
||||
responsive?: boolean;
|
||||
maintainAspectRatio?: boolean;
|
||||
aspectRatio?: number;
|
||||
resizeDelay?: number;
|
||||
devicePixelRatio?: number;
|
||||
hover?: HoverOptions<TType>;
|
||||
events?: Event[];
|
||||
onClick?: (event: ChartEvent, elements: ActiveElement[], chart: Chart) => void;
|
||||
plugins?: PluginOptionsByType<TType>;
|
||||
scales?: ScaleOptionsByType<TType>;
|
||||
elements?: ElementOptionsByType<TType>;
|
||||
layout?: LayoutOptions;
|
||||
animation?: AnimationOptions<TType>;
|
||||
animations?: AnimationOptions<TType>;
|
||||
transitions?: TransitionsSpec<TType>;
|
||||
}
|
||||
|
||||
type UpdateMode = 'resize' | 'reset' | 'none' | 'hide' | 'show' | 'default' | 'active';
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Color {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
type ChartItem =
|
||||
| string
|
||||
| CanvasRenderingContext2D
|
||||
| HTMLCanvasElement
|
||||
| { canvas: HTMLCanvasElement }
|
||||
| ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>;
|
||||
```
|
||||
529
.tessl/tiles/tessl/npm-chart-js/docs/plugins.md
Normal file
529
.tessl/tiles/tessl/npm-chart-js/docs/plugins.md
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
# Plugins
|
||||
|
||||
Built-in and extensible plugin system for additional functionality. Plugins provide lifecycle hooks and configurable features that extend chart behavior.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Title Plugin
|
||||
|
||||
Displays chart titles and subtitles.
|
||||
|
||||
```javascript { .api }
|
||||
const Title: Plugin = {
|
||||
id: 'title',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface TitleOptions {
|
||||
display?: boolean;
|
||||
position?: 'top' | 'left' | 'bottom' | 'right';
|
||||
align?: 'start' | 'center' | 'end';
|
||||
color?: Color;
|
||||
font?: FontSpec;
|
||||
fullSize?: boolean;
|
||||
padding?: number | Padding;
|
||||
text?: string | string[];
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Chart Title',
|
||||
position: 'top',
|
||||
align: 'center',
|
||||
color: '#333',
|
||||
font: {
|
||||
size: 18,
|
||||
weight: 'bold'
|
||||
},
|
||||
padding: {
|
||||
top: 10,
|
||||
bottom: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Legend Plugin
|
||||
|
||||
Displays chart legend for datasets.
|
||||
|
||||
```javascript { .api }
|
||||
const Legend: Plugin = {
|
||||
id: 'legend',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface LegendOptions {
|
||||
display?: boolean;
|
||||
position?: 'top' | 'left' | 'bottom' | 'right' | 'chartArea';
|
||||
align?: 'start' | 'center' | 'end';
|
||||
maxHeight?: number;
|
||||
maxWidth?: number;
|
||||
fullSize?: boolean;
|
||||
onClick?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
|
||||
onHover?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
|
||||
onLeave?: (event: ChartEvent, legendItem: LegendItem, legend: LegendElement) => void;
|
||||
reverse?: boolean;
|
||||
labels?: LegendLabelOptions;
|
||||
rtl?: boolean;
|
||||
textDirection?: string;
|
||||
title?: LegendTitleOptions;
|
||||
}
|
||||
|
||||
interface LegendLabelOptions {
|
||||
boxWidth?: number;
|
||||
boxHeight?: number;
|
||||
color?: Color;
|
||||
font?: FontSpec;
|
||||
padding?: number;
|
||||
generateLabels?: (chart: Chart) => LegendItem[];
|
||||
filter?: (item: LegendItem, data: ChartData) => boolean;
|
||||
sort?: (a: LegendItem, b: LegendItem, data: ChartData) => number;
|
||||
pointStyle?: PointStyle | HTMLImageElement | HTMLCanvasElement;
|
||||
textAlign?: 'left' | 'right' | 'center';
|
||||
usePointStyle?: boolean;
|
||||
useBorderRadius?: boolean;
|
||||
borderRadius?: number;
|
||||
}
|
||||
|
||||
interface LegendItem {
|
||||
text: string;
|
||||
lineCap?: CanvasLineCap;
|
||||
lineDash?: number[];
|
||||
lineDashOffset?: number;
|
||||
lineJoin?: CanvasLineJoin;
|
||||
lineWidth?: number;
|
||||
strokeStyle?: Color;
|
||||
pointStyle?: PointStyle | HTMLImageElement | HTMLCanvasElement;
|
||||
rotation?: number;
|
||||
boxWidth?: number;
|
||||
boxHeight?: number;
|
||||
color?: Color;
|
||||
fillStyle?: Color;
|
||||
fontColor?: Color;
|
||||
hidden?: boolean;
|
||||
index?: number;
|
||||
datasetIndex?: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
align: 'center',
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
padding: 20,
|
||||
font: {
|
||||
size: 14
|
||||
},
|
||||
generateLabels: (chart) => {
|
||||
// Custom label generation
|
||||
return chart.data.datasets.map((dataset, i) => ({
|
||||
text: dataset.label,
|
||||
fillStyle: dataset.backgroundColor,
|
||||
strokeStyle: dataset.borderColor,
|
||||
hidden: !chart.isDatasetVisible(i),
|
||||
index: i
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Tooltip Plugin
|
||||
|
||||
Interactive tooltips on hover.
|
||||
|
||||
```javascript { .api }
|
||||
const Tooltip: Plugin = {
|
||||
id: 'tooltip',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface TooltipOptions {
|
||||
enabled?: boolean;
|
||||
external?: (context: TooltipContext) => void;
|
||||
mode?: InteractionMode;
|
||||
intersect?: boolean;
|
||||
position?: 'average' | 'nearest' | TooltipPositioner;
|
||||
callbacks?: TooltipCallbacks;
|
||||
itemSort?: (a: TooltipItem, b: TooltipItem, data: ChartData) => number;
|
||||
filter?: (tooltipItem: TooltipItem, index: number, tooltipItems: TooltipItem[], data: ChartData) => boolean;
|
||||
backgroundColor?: Color;
|
||||
titleColor?: Color;
|
||||
titleFont?: FontSpec;
|
||||
titleAlign?: 'left' | 'center' | 'right';
|
||||
titleSpacing?: number;
|
||||
titleMarginBottom?: number;
|
||||
bodyColor?: Color;
|
||||
bodyFont?: FontSpec;
|
||||
bodyAlign?: 'left' | 'center' | 'right';
|
||||
bodySpacing?: number;
|
||||
footerColor?: Color;
|
||||
footerFont?: FontSpec;
|
||||
footerAlign?: 'left' | 'center' | 'right';
|
||||
footerSpacing?: number;
|
||||
footerMarginTop?: number;
|
||||
padding?: number | Padding;
|
||||
caretPadding?: number;
|
||||
caretSize?: number;
|
||||
cornerRadius?: number;
|
||||
multiKeyBackground?: Color;
|
||||
displayColors?: boolean;
|
||||
boxWidth?: number;
|
||||
boxHeight?: number;
|
||||
boxPadding?: number;
|
||||
usePointStyle?: boolean;
|
||||
borderColor?: Color;
|
||||
borderWidth?: number;
|
||||
rtl?: boolean;
|
||||
textDirection?: string;
|
||||
xAlign?: 'left' | 'center' | 'right';
|
||||
yAlign?: 'top' | 'center' | 'bottom';
|
||||
}
|
||||
|
||||
interface TooltipCallbacks {
|
||||
beforeTitle?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
title?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
afterTitle?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
beforeBody?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
beforeLabel?: (tooltipItem: TooltipItem) => string | string[];
|
||||
label?: (tooltipItem: TooltipItem) => string | string[];
|
||||
labelColor?: (tooltipItem: TooltipItem) => TooltipLabelStyle;
|
||||
labelTextColor?: (tooltipItem: TooltipItem) => Color;
|
||||
labelPointStyle?: (tooltipItem: TooltipItem) => PointStyle | { pointStyle: PointStyle; rotation: number };
|
||||
afterLabel?: (tooltipItem: TooltipItem) => string | string[];
|
||||
afterBody?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
beforeFooter?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
footer?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
afterFooter?: (tooltipItems: TooltipItem[]) => string | string[];
|
||||
}
|
||||
|
||||
interface TooltipItem {
|
||||
chart: Chart;
|
||||
label: string;
|
||||
parsed: ParsedDataType;
|
||||
raw: unknown;
|
||||
formattedValue: string;
|
||||
dataset: ChartDataset;
|
||||
datasetIndex: number;
|
||||
dataIndex: number;
|
||||
element: Element;
|
||||
}
|
||||
```
|
||||
|
||||
### Subtitle Plugin
|
||||
|
||||
Displays chart subtitles.
|
||||
|
||||
```javascript { .api }
|
||||
const SubTitle: Plugin = {
|
||||
id: 'subtitle',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
// SubTitleOptions extends TitleOptions
|
||||
interface SubTitleOptions extends TitleOptions {
|
||||
// Inherits all title options
|
||||
}
|
||||
```
|
||||
|
||||
### Filler Plugin
|
||||
|
||||
Fills areas between datasets or to axes.
|
||||
|
||||
```javascript { .api }
|
||||
const Filler: Plugin = {
|
||||
id: 'filler',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface FillerOptions {
|
||||
propagate?: boolean;
|
||||
drawTime?: 'beforeDatasetDraw' | 'beforeDatasetsDraw';
|
||||
}
|
||||
|
||||
// Fill configurations (used in dataset options)
|
||||
type FillTarget = boolean | number | string | 'start' | 'end' | 'origin' | 'stack' | 'shape' | { target: FillTarget; above?: Color; below?: Color };
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
data: [10, 20, 30, 40],
|
||||
fill: 'start', // Fill to start of chart
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)'
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
data: [15, 25, 35, 45],
|
||||
fill: '-1', // Fill to previous dataset
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
plugins: {
|
||||
filler: {
|
||||
propagate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Decimation Plugin
|
||||
|
||||
Reduces data points for performance with large datasets.
|
||||
|
||||
```javascript { .api }
|
||||
const Decimation: Plugin = {
|
||||
id: 'decimation',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface DecimationOptions {
|
||||
enabled?: boolean;
|
||||
algorithm?: 'lttb' | 'min-max';
|
||||
samples?: number;
|
||||
threshold?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Colors Plugin
|
||||
|
||||
Automatic color assignment for datasets.
|
||||
|
||||
```javascript { .api }
|
||||
const Colors: Plugin = {
|
||||
id: 'colors',
|
||||
defaults: object,
|
||||
// Plugin implementation
|
||||
};
|
||||
|
||||
interface ColorsOptions {
|
||||
enabled?: boolean;
|
||||
forceOverride?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Interface
|
||||
|
||||
Base interface for creating custom plugins.
|
||||
|
||||
```javascript { .api }
|
||||
interface Plugin<TType extends ChartType = ChartType, O = object> {
|
||||
id: string;
|
||||
|
||||
// Lifecycle hooks
|
||||
beforeInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
afterInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): boolean | void;
|
||||
afterUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): void;
|
||||
beforeElementsUpdate?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeLayout?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
|
||||
afterLayout?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeDatasetsUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): boolean | void;
|
||||
afterDatasetsUpdate?(chart: Chart<TType>, args: UpdateArgs, options: O): void;
|
||||
beforeDatasetUpdate?(chart: Chart<TType>, args: DatasetUpdateArgs, options: O): boolean | void;
|
||||
afterDatasetUpdate?(chart: Chart<TType>, args: DatasetUpdateArgs, options: O): void;
|
||||
beforeRender?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
|
||||
afterRender?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeDraw?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
|
||||
afterDraw?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeDatasetsDraw?(chart: Chart<TType>, args: EmptyObject, options: O): boolean | void;
|
||||
afterDatasetsDraw?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
beforeDatasetDraw?(chart: Chart<TType>, args: DatasetDrawArgs, options: O): boolean | void;
|
||||
afterDatasetDraw?(chart: Chart<TType>, args: DatasetDrawArgs, options: O): void;
|
||||
beforeTooltipDraw?(chart: Chart<TType>, args: TooltipDrawArgs, options: O): boolean | void;
|
||||
afterTooltipDraw?(chart: Chart<TType>, args: TooltipDrawArgs, options: O): void;
|
||||
beforeEvent?(chart: Chart<TType>, args: EventArgs, options: O): boolean | void;
|
||||
afterEvent?(chart: Chart<TType>, args: EventArgs, options: O): void;
|
||||
resize?(chart: Chart<TType>, args: ResizeArgs, options: O): void;
|
||||
reset?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
stop?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
destroy?(chart: Chart<TType>, args: EmptyObject, options: O): void;
|
||||
|
||||
// Optional properties
|
||||
defaults?: object;
|
||||
defaultRoutes?: { [property: string]: string };
|
||||
descriptors?: { [property: string]: boolean };
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example (Custom Plugin):**
|
||||
|
||||
```javascript
|
||||
const customPlugin = {
|
||||
id: 'customPlugin',
|
||||
beforeDraw: (chart, args, options) => {
|
||||
const { ctx, chartArea } = chart;
|
||||
|
||||
// Custom drawing logic
|
||||
ctx.fillStyle = options.backgroundColor || 'lightgray';
|
||||
ctx.fillRect(chartArea.left, chartArea.top, chartArea.width, chartArea.height);
|
||||
}
|
||||
};
|
||||
|
||||
// Register and use
|
||||
Chart.register(customPlugin);
|
||||
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: { /* data */ },
|
||||
plugins: [customPlugin],
|
||||
options: {
|
||||
plugins: {
|
||||
customPlugin: {
|
||||
backgroundColor: 'lightblue'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Plugin Registration
|
||||
|
||||
Plugins must be registered before use.
|
||||
|
||||
```javascript { .api }
|
||||
import {
|
||||
Chart,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
SubTitle,
|
||||
Filler,
|
||||
Decimation,
|
||||
Colors
|
||||
} from 'chart.js';
|
||||
|
||||
// Register built-in plugins
|
||||
Chart.register(
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
SubTitle,
|
||||
Filler,
|
||||
Decimation,
|
||||
Colors
|
||||
);
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
type InteractionMode = 'point' | 'nearest' | 'index' | 'dataset' | 'x' | 'y';
|
||||
|
||||
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';
|
||||
|
||||
interface FontSpec {
|
||||
family?: string;
|
||||
size?: number;
|
||||
style?: 'normal' | 'italic' | 'oblique';
|
||||
weight?: string | number;
|
||||
lineHeight?: number | string;
|
||||
}
|
||||
|
||||
interface Padding {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
}
|
||||
|
||||
interface EmptyObject {}
|
||||
|
||||
interface UpdateArgs {
|
||||
mode: UpdateMode;
|
||||
}
|
||||
|
||||
interface DatasetUpdateArgs {
|
||||
index: number;
|
||||
mode: UpdateMode;
|
||||
}
|
||||
|
||||
interface EventArgs {
|
||||
event: ChartEvent;
|
||||
replay: boolean;
|
||||
changed?: boolean;
|
||||
cancelable: boolean;
|
||||
inChartArea: boolean;
|
||||
}
|
||||
|
||||
type Color = string | CanvasGradient | CanvasPattern;
|
||||
|
||||
interface ChartEvent {
|
||||
type: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
native?: Event;
|
||||
}
|
||||
|
||||
interface TooltipContext<TType extends ChartType = ChartType> {
|
||||
chart: Chart<TType>;
|
||||
tooltip: TooltipModel<TType>;
|
||||
tooltipItems: TooltipItem<TType>[];
|
||||
}
|
||||
|
||||
interface TooltipModel<TType extends ChartType = ChartType> {
|
||||
opacity: number;
|
||||
title: string[];
|
||||
body: TooltipBodyItem[];
|
||||
footer: string[];
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
caretX: number;
|
||||
caretY: number;
|
||||
}
|
||||
|
||||
interface TooltipBodyItem {
|
||||
before: string[];
|
||||
lines: string[];
|
||||
after: string[];
|
||||
}
|
||||
|
||||
type TooltipPositioner = (elements: Element[], eventPosition: Point) => Point;
|
||||
|
||||
type ParsedDataType = { [key: string]: unknown };
|
||||
|
||||
interface LegendElement {
|
||||
chart: Chart;
|
||||
options: LegendOptions;
|
||||
legendItems?: LegendItem[];
|
||||
}
|
||||
```
|
||||
469
.tessl/tiles/tessl/npm-chart-js/docs/scales.md
Normal file
469
.tessl/tiles/tessl/npm-chart-js/docs/scales.md
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
# Scales
|
||||
|
||||
Data-to-pixel conversion and axis rendering for different data types. Scales handle the transformation between data values and pixel coordinates on the chart canvas.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Linear Scale
|
||||
|
||||
Handles linear numeric data with equal intervals.
|
||||
|
||||
```javascript { .api }
|
||||
class LinearScale extends Scale {
|
||||
static id: 'linear';
|
||||
static defaults: object;
|
||||
|
||||
getPixelForValue(value: number): number;
|
||||
getValueForPixel(pixel: number): number;
|
||||
getLabelForValue(value: number): string;
|
||||
getDecimalForPixel(pixel: number): number;
|
||||
}
|
||||
|
||||
interface LinearScaleOptions {
|
||||
type: 'linear';
|
||||
position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
|
||||
axis?: 'x' | 'y';
|
||||
offset?: boolean;
|
||||
beginAtZero?: boolean;
|
||||
bounds?: 'data' | 'ticks';
|
||||
clip?: boolean;
|
||||
grace?: number | string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
suggestedMin?: number;
|
||||
suggestedMax?: number;
|
||||
ticks?: TickOptions;
|
||||
title?: TitleOptions;
|
||||
grid?: GridLineOptions;
|
||||
border?: BorderOptions;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear',
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
max: 100,
|
||||
ticks: {
|
||||
stepSize: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Category Scale
|
||||
|
||||
Handles categorical/ordinal data with discrete labels.
|
||||
|
||||
```javascript { .api }
|
||||
class CategoryScale extends Scale {
|
||||
static id: 'category';
|
||||
static defaults: object;
|
||||
|
||||
getPixelForValue(value: string | number): number;
|
||||
getValueForPixel(pixel: number): number;
|
||||
getLabelForValue(value: string | number): string;
|
||||
getLabels(): string[];
|
||||
}
|
||||
|
||||
interface CategoryScaleOptions {
|
||||
type: 'category';
|
||||
position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
|
||||
axis?: 'x' | 'y';
|
||||
offset?: boolean;
|
||||
labels?: string[];
|
||||
min?: string | number;
|
||||
max?: string | number;
|
||||
ticks?: TickOptions;
|
||||
title?: TitleOptions;
|
||||
grid?: GridLineOptions;
|
||||
border?: BorderOptions;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Red', 'Blue', 'Yellow', 'Green'],
|
||||
datasets: [{ /* dataset */ }]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: {
|
||||
type: 'category',
|
||||
labels: ['Custom Red', 'Custom Blue', 'Custom Yellow', 'Custom Green']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Logarithmic Scale
|
||||
|
||||
Handles logarithmic numeric data with exponential intervals.
|
||||
|
||||
```javascript { .api }
|
||||
class LogarithmicScale extends Scale {
|
||||
static id: 'logarithmic';
|
||||
static defaults: object;
|
||||
|
||||
getPixelForValue(value: number): number;
|
||||
getValueForPixel(pixel: number): number;
|
||||
getLabelForValue(value: number): string;
|
||||
}
|
||||
|
||||
interface LogarithmicScaleOptions {
|
||||
type: 'logarithmic';
|
||||
position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
|
||||
axis?: 'x' | 'y';
|
||||
min?: number;
|
||||
max?: number;
|
||||
suggestedMin?: number;
|
||||
suggestedMax?: number;
|
||||
ticks?: TickOptions;
|
||||
title?: TitleOptions;
|
||||
grid?: GridLineOptions;
|
||||
border?: BorderOptions;
|
||||
}
|
||||
```
|
||||
|
||||
### Time Scale
|
||||
|
||||
Handles time-based data with temporal intervals.
|
||||
|
||||
```javascript { .api }
|
||||
class TimeScale extends Scale {
|
||||
static id: 'time';
|
||||
static defaults: object;
|
||||
|
||||
getPixelForValue(value: string | number | Date): number;
|
||||
getValueForPixel(pixel: number): number;
|
||||
getLabelForValue(value: string | number | Date): string;
|
||||
}
|
||||
|
||||
interface TimeScaleOptions {
|
||||
type: 'time';
|
||||
position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
|
||||
axis?: 'x' | 'y';
|
||||
adapters?: DateAdapter;
|
||||
bounds?: 'data' | 'ticks';
|
||||
offset?: boolean;
|
||||
min?: string | number | Date;
|
||||
max?: string | number | Date;
|
||||
suggestedMin?: string | number | Date;
|
||||
suggestedMax?: string | number | Date;
|
||||
time?: TimeOptions;
|
||||
ticks?: TickOptions;
|
||||
title?: TitleOptions;
|
||||
grid?: GridLineOptions;
|
||||
border?: BorderOptions;
|
||||
}
|
||||
|
||||
interface TimeOptions {
|
||||
displayFormats?: {
|
||||
millisecond?: string;
|
||||
second?: string;
|
||||
minute?: string;
|
||||
hour?: string;
|
||||
day?: string;
|
||||
week?: string;
|
||||
month?: string;
|
||||
quarter?: string;
|
||||
year?: string;
|
||||
};
|
||||
isoWeekday?: boolean | number;
|
||||
parser?: string | ((value: unknown) => Date);
|
||||
round?: TimeUnit;
|
||||
tooltipFormat?: string;
|
||||
unit?: TimeUnit;
|
||||
stepSize?: number;
|
||||
minUnit?: TimeUnit;
|
||||
}
|
||||
|
||||
type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
|
||||
|
||||
interface DateAdapter {
|
||||
formats(): { [key: string]: string };
|
||||
parse(value: unknown, format?: string): Date | null;
|
||||
format(time: Date, format: string): string;
|
||||
add(time: Date, amount: number, unit: TimeUnit): Date;
|
||||
diff(max: Date, min: Date, unit: TimeUnit): number;
|
||||
startOf(time: Date, unit: TimeUnit, weekday?: number): Date;
|
||||
endOf(time: Date, unit: TimeUnit): Date;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Time Series',
|
||||
data: [
|
||||
{ x: '2023-01-01', y: 10 },
|
||||
{ x: '2023-02-01', y: 20 },
|
||||
{ x: '2023-03-01', y: 15 }
|
||||
]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'month',
|
||||
displayFormats: {
|
||||
month: 'MMM YYYY'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Time Series Scale
|
||||
|
||||
Optimized time scale for large time series datasets.
|
||||
|
||||
```javascript { .api }
|
||||
class TimeSeriesScale extends TimeScale {
|
||||
static id: 'timeseries';
|
||||
static defaults: object;
|
||||
}
|
||||
|
||||
interface TimeSeriesScaleOptions extends TimeScaleOptions {
|
||||
type: 'timeseries';
|
||||
bounds: 'data'; // Always 'data' for timeseries
|
||||
offset: false; // Always false for timeseries
|
||||
}
|
||||
```
|
||||
|
||||
### Radial Linear Scale
|
||||
|
||||
Handles radial numeric data for radar charts.
|
||||
|
||||
```javascript { .api }
|
||||
class RadialLinearScale extends Scale {
|
||||
static id: 'radialLinear';
|
||||
static defaults: object;
|
||||
|
||||
getPixelForValue(value: number, index?: number): number;
|
||||
getValueForPixel(pixel: number): number;
|
||||
getLabelForValue(value: number): string;
|
||||
getIndexAngle(index: number): number;
|
||||
getDistanceFromCenterForValue(value: number): number;
|
||||
getValueForDistanceFromCenter(distance: number): number;
|
||||
}
|
||||
|
||||
interface RadialLinearScaleOptions {
|
||||
type: 'radialLinear';
|
||||
animate?: boolean;
|
||||
angleLines?: AngleLineOptions;
|
||||
beginAtZero?: boolean;
|
||||
grid?: GridLineOptions;
|
||||
min?: number;
|
||||
max?: number;
|
||||
pointLabels?: PointLabelOptions;
|
||||
startAngle?: number;
|
||||
ticks?: TickOptions;
|
||||
title?: TitleOptions;
|
||||
}
|
||||
|
||||
interface AngleLineOptions {
|
||||
display?: boolean;
|
||||
color?: Color;
|
||||
lineWidth?: number;
|
||||
borderDash?: number[];
|
||||
borderDashOffset?: number;
|
||||
}
|
||||
|
||||
interface PointLabelOptions {
|
||||
display?: boolean;
|
||||
callback?: (label: string, index: number) => string;
|
||||
color?: Color;
|
||||
font?: FontSpec;
|
||||
padding?: number;
|
||||
centerPointLabels?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### Base Scale Interface
|
||||
|
||||
Common interface implemented by all scales.
|
||||
|
||||
```javascript { .api }
|
||||
interface Scale {
|
||||
readonly id: string;
|
||||
readonly type: string;
|
||||
readonly ctx: CanvasRenderingContext2D;
|
||||
readonly chart: Chart;
|
||||
|
||||
// Core conversion methods
|
||||
getPixelForValue(value: unknown, index?: number): number;
|
||||
getValueForPixel(pixel: number): unknown;
|
||||
getPixelForTick(index: number): number;
|
||||
getPixelForDecimal(decimal: number): number;
|
||||
getDecimalForPixel(pixel: number): number;
|
||||
getBaseValue(): number;
|
||||
getBasePixel(): number;
|
||||
|
||||
// Label methods
|
||||
getLabelForValue(value: unknown): string;
|
||||
|
||||
// Lifecycle methods
|
||||
beforeLayout(): void;
|
||||
afterLayout(): void;
|
||||
beforeUpdate(mode: UpdateMode): void;
|
||||
update(width: number, height: number, margins: ChartArea): void;
|
||||
afterUpdate(mode: UpdateMode): void;
|
||||
|
||||
// Drawing methods
|
||||
beforeDraw(): void;
|
||||
draw(chartArea: ChartArea): void;
|
||||
afterDraw(): void;
|
||||
|
||||
// Utility methods
|
||||
isHorizontal(): boolean;
|
||||
buildTicks(): Tick[];
|
||||
configure(): void;
|
||||
fit(): void;
|
||||
}
|
||||
```
|
||||
|
||||
## Scale Registration
|
||||
|
||||
Scales must be registered before use in charts.
|
||||
|
||||
```javascript { .api }
|
||||
import {
|
||||
Chart,
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
TimeScale,
|
||||
LogarithmicScale,
|
||||
RadialLinearScale,
|
||||
TimeSeriesScale
|
||||
} from 'chart.js';
|
||||
|
||||
// Register scales
|
||||
Chart.register(
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
TimeScale,
|
||||
LogarithmicScale,
|
||||
RadialLinearScale,
|
||||
TimeSeriesScale
|
||||
);
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
type ScaleType = 'linear' | 'logarithmic' | 'category' | 'time' | 'timeseries' | 'radialLinear';
|
||||
|
||||
interface TickOptions {
|
||||
display?: boolean;
|
||||
align?: 'start' | 'center' | 'end' | 'inner';
|
||||
callback?: (tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[];
|
||||
color?: Color | Color[];
|
||||
count?: number;
|
||||
crossAlign?: 'near' | 'center' | 'far';
|
||||
font?: FontSpec | FontSpec[];
|
||||
includeBounds?: boolean;
|
||||
labelOffset?: number;
|
||||
maxRotation?: number;
|
||||
maxTicksLimit?: number;
|
||||
minRotation?: number;
|
||||
mirror?: boolean;
|
||||
padding?: number;
|
||||
precision?: number;
|
||||
sampleSize?: number;
|
||||
showLabelBackdrop?: boolean;
|
||||
source?: 'auto' | 'data' | 'labels';
|
||||
stepSize?: number;
|
||||
textStrokeColor?: Color | Color[];
|
||||
textStrokeWidth?: number | number[];
|
||||
z?: number;
|
||||
}
|
||||
|
||||
interface TitleOptions {
|
||||
display?: boolean;
|
||||
align?: 'start' | 'center' | 'end';
|
||||
color?: Color;
|
||||
font?: FontSpec;
|
||||
padding?: Padding;
|
||||
text?: string | string[];
|
||||
}
|
||||
|
||||
interface GridLineOptions {
|
||||
display?: boolean;
|
||||
circular?: boolean;
|
||||
color?: Color | Color[];
|
||||
lineWidth?: number | number[];
|
||||
drawBorder?: boolean;
|
||||
drawOnChartArea?: boolean;
|
||||
drawTicks?: boolean;
|
||||
tickBorderDash?: number[];
|
||||
tickBorderDashOffset?: number;
|
||||
tickColor?: Color | Color[];
|
||||
tickLength?: number;
|
||||
tickWidth?: number;
|
||||
offset?: boolean;
|
||||
z?: number;
|
||||
}
|
||||
|
||||
interface BorderOptions {
|
||||
display?: boolean;
|
||||
color?: Color;
|
||||
width?: number;
|
||||
dash?: number[];
|
||||
dashOffset?: number;
|
||||
z?: number;
|
||||
}
|
||||
|
||||
interface Tick {
|
||||
value: number;
|
||||
label?: string;
|
||||
major?: boolean;
|
||||
}
|
||||
|
||||
interface ChartArea {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface FontSpec {
|
||||
family?: string;
|
||||
size?: number;
|
||||
style?: 'normal' | 'italic' | 'oblique';
|
||||
weight?: string | number;
|
||||
lineHeight?: number | string;
|
||||
}
|
||||
|
||||
interface Padding {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
}
|
||||
```
|
||||
495
.tessl/tiles/tessl/npm-chart-js/docs/utilities.md
Normal file
495
.tessl/tiles/tessl/npm-chart-js/docs/utilities.md
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
# Utilities
|
||||
|
||||
Helper functions for common operations, calculations, and canvas manipulation. The helpers module provides utility functions organized by category for various chart-related tasks.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Color Utilities
|
||||
|
||||
Functions for color parsing, manipulation, and conversion.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Parse and create color objects
|
||||
* @param value - Color string, gradient, or pattern
|
||||
* @returns Color object with RGBA properties
|
||||
*/
|
||||
function color(value: string | CanvasGradient | CanvasPattern): Color;
|
||||
|
||||
/**
|
||||
* Generate hover color variant
|
||||
* @param color - Base color
|
||||
* @returns Darker/lighter variant for hover state
|
||||
*/
|
||||
function getHoverColor(color: Color): Color;
|
||||
|
||||
/**
|
||||
* Check if value is a pattern or gradient
|
||||
* @param value - Value to check
|
||||
* @returns True if pattern or gradient
|
||||
*/
|
||||
function isPatternOrGradient(value: unknown): value is CanvasGradient | CanvasPattern;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
import { color, getHoverColor } from 'chart.js/helpers';
|
||||
|
||||
// Parse color
|
||||
const blue = color('rgba(54, 162, 235, 0.8)');
|
||||
console.log(blue.r, blue.g, blue.b, blue.a); // 54, 162, 235, 0.8
|
||||
|
||||
// Generate hover color
|
||||
const hoverBlue = getHoverColor(blue);
|
||||
```
|
||||
|
||||
### Core Utilities
|
||||
|
||||
Basic utility functions for type checking and value handling.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Check if value is an array
|
||||
* @param value - Value to check
|
||||
* @returns True if array
|
||||
*/
|
||||
function isArray(value: unknown): value is unknown[];
|
||||
|
||||
/**
|
||||
* Check if value is an object
|
||||
* @param value - Value to check
|
||||
* @returns True if object (not null, array, or primitive)
|
||||
*/
|
||||
function isObject(value: unknown): value is object;
|
||||
|
||||
/**
|
||||
* Check if value is null or undefined
|
||||
* @param value - Value to check
|
||||
* @returns True if null or undefined
|
||||
*/
|
||||
function isNullOrUndef(value: unknown): value is null | undefined;
|
||||
|
||||
/**
|
||||
* No-operation function
|
||||
* @returns void
|
||||
*/
|
||||
function noop(): void;
|
||||
|
||||
/**
|
||||
* Generate unique identifier
|
||||
* @returns Unique string ID
|
||||
*/
|
||||
function uid(): string;
|
||||
|
||||
/**
|
||||
* Return value or default if undefined
|
||||
* @param value - Value to check
|
||||
* @param defaultValue - Default to return
|
||||
* @returns Value or default
|
||||
*/
|
||||
function valueOrDefault<T>(value: T | undefined, defaultValue: T): T;
|
||||
|
||||
/**
|
||||
* Clamp value to range
|
||||
* @param value - Value to clamp
|
||||
* @param min - Minimum value
|
||||
* @param max - Maximum value
|
||||
* @returns Clamped value
|
||||
*/
|
||||
function clamp(value: number, min: number, max: number): number;
|
||||
```
|
||||
|
||||
### Canvas Utilities
|
||||
|
||||
Functions for canvas drawing and manipulation.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Clear entire canvas
|
||||
* @param ctx - Canvas rendering context
|
||||
*/
|
||||
function clearCanvas(ctx: CanvasRenderingContext2D): void;
|
||||
|
||||
/**
|
||||
* Clip canvas to specified area
|
||||
* @param ctx - Canvas rendering context
|
||||
* @param area - Area to clip to
|
||||
*/
|
||||
function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void;
|
||||
|
||||
/**
|
||||
* Restore canvas from clipping
|
||||
* @param ctx - Canvas rendering context
|
||||
* @param area - Previously clipped area
|
||||
*/
|
||||
function unclipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void;
|
||||
|
||||
/**
|
||||
* Draw point shape at coordinates
|
||||
* @param ctx - Canvas rendering context
|
||||
* @param options - Point style options
|
||||
* @param x - X coordinate
|
||||
* @param y - Y coordinate
|
||||
*/
|
||||
function drawPoint(ctx: CanvasRenderingContext2D, options: PointOptions, x: number, y: number): void;
|
||||
|
||||
/**
|
||||
* Render text with alignment and rotation
|
||||
* @param ctx - Canvas rendering context
|
||||
* @param text - Text to render
|
||||
* @param x - X coordinate
|
||||
* @param y - Y coordinate
|
||||
* @param options - Text rendering options
|
||||
*/
|
||||
function renderText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, options?: TextOptions): void;
|
||||
|
||||
/**
|
||||
* Measure text dimensions
|
||||
* @param ctx - Canvas rendering context
|
||||
* @param text - Text to measure
|
||||
* @param font - Font specification
|
||||
* @returns Text metrics
|
||||
*/
|
||||
function measureText(ctx: CanvasRenderingContext2D, text: string, font?: FontSpec): TextMetrics;
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
import { clearCanvas, drawPoint, renderText } from 'chart.js/helpers';
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Clear canvas
|
||||
clearCanvas(ctx);
|
||||
|
||||
// Draw custom point
|
||||
drawPoint(ctx, {
|
||||
pointStyle: 'star',
|
||||
radius: 10,
|
||||
backgroundColor: 'gold'
|
||||
}, 100, 100);
|
||||
|
||||
// Render text
|
||||
renderText(ctx, 'Custom Label', 100, 120, {
|
||||
color: 'black',
|
||||
font: { size: 14, weight: 'bold' },
|
||||
textAlign: 'center'
|
||||
});
|
||||
```
|
||||
|
||||
### Collection Utilities
|
||||
|
||||
Functions for array and object manipulation.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Iterate over collection
|
||||
* @param loopable - Array or object to iterate
|
||||
* @param fn - Function to call for each item
|
||||
* @param thisArg - Context for function calls
|
||||
*/
|
||||
function each<T>(loopable: T[] | { [key: string]: T }, fn: (item: T, index: number | string) => void, thisArg?: unknown): void;
|
||||
|
||||
/**
|
||||
* Find index in array matching condition
|
||||
* @param array - Array to search
|
||||
* @param callback - Condition function
|
||||
* @returns Index or -1 if not found
|
||||
*/
|
||||
function findIndex<T>(array: T[], callback: (item: T, index: number) => boolean): number;
|
||||
|
||||
/**
|
||||
* Filter collection by condition
|
||||
* @param collection - Collection to filter
|
||||
* @param filterCallback - Filter function
|
||||
* @returns Filtered array
|
||||
*/
|
||||
function where<T>(collection: T[], filterCallback: (item: T) => boolean): T[];
|
||||
|
||||
/**
|
||||
* Get unique values from array
|
||||
* @param array - Input array
|
||||
* @returns Array with unique values
|
||||
*/
|
||||
function uniq<T>(array: T[]): T[];
|
||||
|
||||
/**
|
||||
* Create array with specified length and fill value
|
||||
* @param length - Array length
|
||||
* @param value - Fill value
|
||||
* @returns Filled array
|
||||
*/
|
||||
function array<T>(length: number, value: T): T[];
|
||||
```
|
||||
|
||||
### Configuration Utilities
|
||||
|
||||
Functions for resolving and merging configuration options.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Resolve scriptable options
|
||||
* @param inputs - Option inputs to resolve
|
||||
* @param context - Resolution context
|
||||
* @param index - Data index
|
||||
* @param info - Additional info object
|
||||
* @returns Resolved value
|
||||
*/
|
||||
function resolve<T>(inputs: T[], context?: object, index?: number, info?: object): T;
|
||||
|
||||
/**
|
||||
* Merge objects conditionally
|
||||
* @param target - Target object
|
||||
* @param source - Source object
|
||||
* @returns Merged object
|
||||
*/
|
||||
function mergeIf(target: object, source: object): object;
|
||||
|
||||
/**
|
||||
* Deep merge objects
|
||||
* @param target - Target object
|
||||
* @param source - Source object(s)
|
||||
* @returns Merged object
|
||||
*/
|
||||
function merge(target: object, ...source: object[]): object;
|
||||
|
||||
/**
|
||||
* Deep clone object or array
|
||||
* @param source - Object to clone
|
||||
* @returns Cloned object
|
||||
*/
|
||||
function clone<T>(source: T): T;
|
||||
```
|
||||
|
||||
### DOM Utilities
|
||||
|
||||
Functions for DOM element manipulation and event handling.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Get canvas element from various inputs
|
||||
* @param item - Canvas, context, or selector
|
||||
* @returns Canvas element
|
||||
*/
|
||||
function getCanvas(item: string | HTMLCanvasElement | CanvasRenderingContext2D): HTMLCanvasElement;
|
||||
|
||||
/**
|
||||
* Get relative position of event within chart
|
||||
* @param event - DOM event
|
||||
* @param chart - Chart instance
|
||||
* @returns Relative coordinates
|
||||
*/
|
||||
function getRelativePosition(event: Event, chart: Chart): Point;
|
||||
|
||||
/**
|
||||
* Calculate maximum size for canvas
|
||||
* @param canvas - Canvas element
|
||||
* @param width - Desired width
|
||||
* @param height - Desired height
|
||||
* @param aspectRatio - Aspect ratio to maintain
|
||||
* @returns Maximum size object
|
||||
*/
|
||||
function getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): Size;
|
||||
|
||||
/**
|
||||
* Get device pixel ratio
|
||||
* @returns Device pixel ratio
|
||||
*/
|
||||
function getDevicePixelRatio(): number;
|
||||
|
||||
/**
|
||||
* Add event listener with options
|
||||
* @param element - DOM element
|
||||
* @param type - Event type
|
||||
* @param listener - Event listener
|
||||
* @param options - Event options
|
||||
*/
|
||||
function addEventListener(element: Element, type: string, listener: EventListener, options?: AddEventListenerOptions): void;
|
||||
|
||||
/**
|
||||
* Remove event listener
|
||||
* @param element - DOM element
|
||||
* @param type - Event type
|
||||
* @param listener - Event listener
|
||||
*/
|
||||
function removeEventListener(element: Element, type: string, listener: EventListener): void;
|
||||
```
|
||||
|
||||
### Math Utilities
|
||||
|
||||
Mathematical helper functions for calculations.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Check if two numbers are approximately equal
|
||||
* @param x - First number
|
||||
* @param y - Second number
|
||||
* @param epsilon - Tolerance
|
||||
* @returns True if approximately equal
|
||||
*/
|
||||
function almostEquals(x: number, y: number, epsilon: number): boolean;
|
||||
|
||||
/**
|
||||
* Check if number is almost a whole number
|
||||
* @param x - Number to check
|
||||
* @param epsilon - Tolerance
|
||||
* @returns True if almost whole
|
||||
*/
|
||||
function almostWhole(x: number, epsilon: number): boolean;
|
||||
|
||||
/**
|
||||
* Convert degrees to radians
|
||||
* @param degrees - Angle in degrees
|
||||
* @returns Angle in radians
|
||||
*/
|
||||
function toRadians(degrees: number): number;
|
||||
|
||||
/**
|
||||
* Convert radians to degrees
|
||||
* @param radians - Angle in radians
|
||||
* @returns Angle in degrees
|
||||
*/
|
||||
function toDegrees(radians: number): number;
|
||||
|
||||
/**
|
||||
* Calculate distance between two points
|
||||
* @param pt1 - First point
|
||||
* @param pt2 - Second point
|
||||
* @returns Distance
|
||||
*/
|
||||
function distanceBetweenPoints(pt1: Point, pt2: Point): number;
|
||||
|
||||
/**
|
||||
* Linear interpolation between values
|
||||
* @param a - Start value
|
||||
* @param b - End value
|
||||
* @param t - Interpolation factor (0-1)
|
||||
* @returns Interpolated value
|
||||
*/
|
||||
function lerp(a: number, b: number, t: number): number;
|
||||
|
||||
/**
|
||||
* Get angle of line between two points
|
||||
* @param pt1 - First point
|
||||
* @param pt2 - Second point
|
||||
* @returns Angle in radians
|
||||
*/
|
||||
function getAngleFromPoint(pt1: Point, pt2: Point): number;
|
||||
```
|
||||
|
||||
### RTL (Right-to-Left) Utilities
|
||||
|
||||
Functions for right-to-left text support.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Get RTL helper for canvas
|
||||
* @param rtl - RTL mode
|
||||
* @param canvas - Canvas element
|
||||
* @returns RTL helper object
|
||||
*/
|
||||
function getRtlHelper(rtl: boolean, canvas: HTMLCanvasElement): RtlHelper;
|
||||
|
||||
interface RtlHelper {
|
||||
x(x: number): number;
|
||||
setWidth(width: number): void;
|
||||
textAlign(align: string): string;
|
||||
xPlus(x: number, value: number): number;
|
||||
leftForLtr(x: number, itemWidth: number): number;
|
||||
}
|
||||
```
|
||||
|
||||
### Interpolation Utilities
|
||||
|
||||
Functions for value interpolation and animation.
|
||||
|
||||
```javascript { .api }
|
||||
/**
|
||||
* Interpolate between objects
|
||||
* @param from - Start object
|
||||
* @param to - End object
|
||||
* @param factor - Interpolation factor (0-1)
|
||||
* @returns Interpolated object
|
||||
*/
|
||||
function interpolate<T>(from: T, to: T, factor: number): T;
|
||||
|
||||
/**
|
||||
* Get interpolator function for property
|
||||
* @param property - Property name
|
||||
* @returns Interpolator function
|
||||
*/
|
||||
function getInterpolator(property: string): (from: unknown, to: unknown, factor: number) => unknown;
|
||||
```
|
||||
|
||||
## Helper Module Access
|
||||
|
||||
All helpers are available through the helpers namespace:
|
||||
|
||||
```javascript { .api }
|
||||
import { helpers } from 'chart.js';
|
||||
|
||||
// Access utilities via helpers object
|
||||
const { color, isArray, clearCanvas } = helpers;
|
||||
|
||||
// Or import specific helpers
|
||||
import { color, isArray, clearCanvas } from 'chart.js/helpers';
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface ChartArea {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface Color {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
interface FontSpec {
|
||||
family?: string;
|
||||
size?: number;
|
||||
style?: 'normal' | 'italic' | 'oblique';
|
||||
weight?: string | number;
|
||||
lineHeight?: number | string;
|
||||
}
|
||||
|
||||
interface PointOptions {
|
||||
pointStyle: PointStyle | HTMLImageElement | HTMLCanvasElement;
|
||||
radius: number;
|
||||
backgroundColor?: Color;
|
||||
borderColor?: Color;
|
||||
borderWidth?: number;
|
||||
rotation?: number;
|
||||
}
|
||||
|
||||
interface TextOptions {
|
||||
color?: Color;
|
||||
font?: FontSpec;
|
||||
textAlign?: CanvasTextAlign;
|
||||
textBaseline?: CanvasTextBaseline;
|
||||
rotation?: number;
|
||||
}
|
||||
|
||||
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';
|
||||
```
|
||||
418
.tessl/tiles/tessl/npm-chart-js/docs/visual-elements.md
Normal file
418
.tessl/tiles/tessl/npm-chart-js/docs/visual-elements.md
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
# Visual Elements
|
||||
|
||||
Core visual components for rendering chart elements. Elements handle the drawing and interaction logic for different visual shapes used in charts.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Bar Element
|
||||
|
||||
Renders rectangular bars for bar charts.
|
||||
|
||||
```javascript { .api }
|
||||
class BarElement extends Element {
|
||||
static id: 'bar';
|
||||
static defaults: object;
|
||||
static defaultRoutes: object;
|
||||
|
||||
// Properties
|
||||
x: number;
|
||||
y: number;
|
||||
base: number;
|
||||
horizontal: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
// Methods
|
||||
draw(ctx: CanvasRenderingContext2D): void;
|
||||
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
|
||||
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
getCenterPoint(useFinalPosition?: boolean): Point;
|
||||
getRange(axis: 'x' | 'y'): number;
|
||||
}
|
||||
|
||||
interface BarElementOptions {
|
||||
backgroundColor: Color;
|
||||
borderColor: Color;
|
||||
borderSkipped: 'start' | 'end' | 'middle' | 'bottom' | 'left' | 'top' | 'right' | boolean;
|
||||
borderWidth: number;
|
||||
borderRadius: number | BorderRadius;
|
||||
inflateAmount: number | 'auto';
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Bar element styling
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
elements: {
|
||||
bar: {
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 2,
|
||||
borderRadius: 4,
|
||||
borderSkipped: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Line Element
|
||||
|
||||
Renders line paths connecting data points.
|
||||
|
||||
```javascript { .api }
|
||||
class LineElement extends Element {
|
||||
static id: 'line';
|
||||
static defaults: object;
|
||||
static defaultRoutes: object;
|
||||
|
||||
// Properties
|
||||
points: PointElement[];
|
||||
segments: LineSegment[];
|
||||
first: boolean;
|
||||
last: boolean;
|
||||
smooth: boolean;
|
||||
|
||||
// Methods
|
||||
draw(ctx: CanvasRenderingContext2D): void;
|
||||
interpolate(point: PointElement, property: string): unknown;
|
||||
pathSegment(ctx: CanvasRenderingContext2D, segment: LineSegment, params: SegmentParams): boolean | void;
|
||||
path(ctx: CanvasRenderingContext2D): boolean | void;
|
||||
}
|
||||
|
||||
interface LineElementOptions {
|
||||
backgroundColor: Color;
|
||||
borderCapStyle: CanvasLineCap;
|
||||
borderColor: Color;
|
||||
borderDash: number[];
|
||||
borderDashOffset: number;
|
||||
borderJoinStyle: CanvasLineJoin;
|
||||
borderWidth: number;
|
||||
fill: boolean | string | number | object;
|
||||
stepped: boolean | 'before' | 'after' | 'middle';
|
||||
tension: number;
|
||||
}
|
||||
|
||||
interface LineSegment {
|
||||
start: number;
|
||||
end: number;
|
||||
loop: boolean;
|
||||
}
|
||||
|
||||
interface SegmentParams {
|
||||
move: boolean;
|
||||
reverse: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Line element styling
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
elements: {
|
||||
line: {
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 3,
|
||||
borderDash: [5, 5],
|
||||
borderCapStyle: 'round',
|
||||
borderJoinStyle: 'round',
|
||||
fill: false,
|
||||
tension: 0.4
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Point Element
|
||||
|
||||
Renders individual data points and markers.
|
||||
|
||||
```javascript { .api }
|
||||
class PointElement extends Element {
|
||||
static id: 'point';
|
||||
static defaults: object;
|
||||
static defaultRoutes: object;
|
||||
|
||||
// Properties
|
||||
x: number;
|
||||
y: number;
|
||||
size: number;
|
||||
|
||||
// Methods
|
||||
draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void;
|
||||
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
|
||||
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
getCenterPoint(useFinalPosition?: boolean): Point;
|
||||
size(options?: PointElementOptions): number;
|
||||
resolveElementProperties(chart: Chart, options: PointElementOptions): void;
|
||||
}
|
||||
|
||||
interface PointElementOptions {
|
||||
backgroundColor: Color;
|
||||
borderColor: Color;
|
||||
borderWidth: number;
|
||||
hitRadius: number;
|
||||
hoverBackgroundColor: Color;
|
||||
hoverBorderColor: Color;
|
||||
hoverBorderWidth: number;
|
||||
hoverRadius: number;
|
||||
pointStyle: PointStyle | HTMLImageElement | HTMLCanvasElement;
|
||||
radius: number;
|
||||
rotation: number;
|
||||
}
|
||||
|
||||
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Point element styling
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
elements: {
|
||||
point: {
|
||||
backgroundColor: 'rgba(255, 206, 86, 1)',
|
||||
borderColor: 'rgba(255, 206, 86, 1)',
|
||||
borderWidth: 2,
|
||||
radius: 6,
|
||||
pointStyle: 'circle',
|
||||
hoverRadius: 8,
|
||||
hoverBorderWidth: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Arc Element
|
||||
|
||||
Renders arc/sector shapes for pie and doughnut charts.
|
||||
|
||||
```javascript { .api }
|
||||
class ArcElement extends Element {
|
||||
static id: 'arc';
|
||||
static defaults: object;
|
||||
static defaultRoutes: object;
|
||||
|
||||
// Properties
|
||||
x: number;
|
||||
y: number;
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
innerRadius: number;
|
||||
outerRadius: number;
|
||||
circumference: number;
|
||||
|
||||
// Methods
|
||||
draw(ctx: CanvasRenderingContext2D): void;
|
||||
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
|
||||
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
getCenterPoint(useFinalPosition?: boolean): Point;
|
||||
getAngle(): number;
|
||||
tooltipPosition(): Point;
|
||||
}
|
||||
|
||||
interface ArcElementOptions {
|
||||
angle: number;
|
||||
backgroundColor: Color;
|
||||
borderAlign: 'center' | 'inner';
|
||||
borderColor: Color;
|
||||
borderJoinStyle: CanvasLineJoin;
|
||||
borderRadius: number | ArcBorderRadius;
|
||||
borderWidth: number;
|
||||
offset: number;
|
||||
spacing: number;
|
||||
circular: boolean;
|
||||
}
|
||||
|
||||
interface ArcBorderRadius {
|
||||
outerStart?: number;
|
||||
outerEnd?: number;
|
||||
innerStart?: number;
|
||||
innerEnd?: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Arc element styling
|
||||
const config = {
|
||||
type: 'doughnut',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.2)',
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
borderWidth: 2,
|
||||
borderAlign: 'center',
|
||||
spacing: 2,
|
||||
borderRadius: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Base Element Interface
|
||||
|
||||
Common interface implemented by all elements.
|
||||
|
||||
```javascript { .api }
|
||||
interface Element {
|
||||
// Properties
|
||||
readonly chart: Chart;
|
||||
active: boolean;
|
||||
options: ElementOptions;
|
||||
|
||||
// Core methods
|
||||
draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void;
|
||||
|
||||
// Hit detection methods
|
||||
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
|
||||
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
|
||||
|
||||
// Position methods
|
||||
getCenterPoint(useFinalPosition?: boolean): Point;
|
||||
getRange?(axis: 'x' | 'y'): number;
|
||||
|
||||
// Lifecycle methods
|
||||
tooltipPosition?(useFinalPosition?: boolean): Point;
|
||||
hasValue?(): boolean;
|
||||
getProps?(props: string[], final?: boolean): { [key: string]: unknown };
|
||||
}
|
||||
```
|
||||
|
||||
### Element Factory
|
||||
|
||||
Helper for creating element instances.
|
||||
|
||||
```javascript { .api }
|
||||
interface ElementFactory {
|
||||
create<T extends Element>(type: string, scope: object, values: object): T;
|
||||
update<T extends Element>(element: T, properties: object, active: boolean): void;
|
||||
}
|
||||
```
|
||||
|
||||
## Element Registration
|
||||
|
||||
Elements must be registered before use in charts.
|
||||
|
||||
```javascript { .api }
|
||||
import {
|
||||
Chart,
|
||||
BarElement,
|
||||
LineElement,
|
||||
PointElement,
|
||||
ArcElement
|
||||
} from 'chart.js';
|
||||
|
||||
// Register elements
|
||||
Chart.register(
|
||||
BarElement,
|
||||
LineElement,
|
||||
PointElement,
|
||||
ArcElement
|
||||
);
|
||||
```
|
||||
|
||||
## Animation Properties
|
||||
|
||||
Elements support property animation during chart updates.
|
||||
|
||||
```javascript { .api }
|
||||
interface AnimatableElement {
|
||||
// Properties that can be animated
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
radius?: number;
|
||||
innerRadius?: number;
|
||||
outerRadius?: number;
|
||||
startAngle?: number;
|
||||
endAngle?: number;
|
||||
backgroundColor?: Color;
|
||||
borderColor?: Color;
|
||||
borderWidth?: number;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// Custom animation configuration
|
||||
const config = {
|
||||
type: 'bar',
|
||||
data: { /* data */ },
|
||||
options: {
|
||||
animation: {
|
||||
duration: 2000,
|
||||
easing: 'easeInOutQuart'
|
||||
},
|
||||
animations: {
|
||||
x: {
|
||||
type: 'number',
|
||||
easing: 'linear',
|
||||
duration: 1000,
|
||||
from: 0
|
||||
},
|
||||
y: {
|
||||
type: 'number',
|
||||
easing: 'easeInOutCubic',
|
||||
duration: 1500,
|
||||
delay: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```javascript { .api }
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface BorderRadius {
|
||||
topLeft: number;
|
||||
topRight: number;
|
||||
bottomLeft: number;
|
||||
bottomRight: number;
|
||||
}
|
||||
|
||||
interface ChartArea {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
type Color = string | CanvasGradient | CanvasPattern;
|
||||
|
||||
interface ElementOptions {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
```
|
||||
8
.tessl/tiles/tessl/npm-chart-js/tile.json
Normal file
8
.tessl/tiles/tessl/npm-chart-js/tile.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "tessl/npm-chart-js",
|
||||
"version": "4.3.0",
|
||||
"docs": "docs/index.md",
|
||||
"describes": "pkg:npm/chart.js@4.3.3",
|
||||
"summary": "Simple HTML5 charts using the canvas element.",
|
||||
"private": false
|
||||
}
|
||||
237
.tessl/tiles/tessl/npm-clsx/docs/index.md
Normal file
237
.tessl/tiles/tessl/npm-clsx/docs/index.md
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
# clsx
|
||||
|
||||
clsx is a tiny (239B) utility for constructing className strings conditionally. It serves as a faster and smaller drop-in replacement for the popular classnames module, supporting various input types including strings, objects, arrays, and booleans while automatically filtering out falsy values.
|
||||
|
||||
## Package Information
|
||||
|
||||
- **Package Name**: clsx
|
||||
- **Package Type**: npm
|
||||
- **Language**: JavaScript with TypeScript definitions
|
||||
- **Installation**: `npm install clsx`
|
||||
|
||||
## Core Imports
|
||||
|
||||
ES Module (default):
|
||||
```javascript
|
||||
import clsx from "clsx";
|
||||
```
|
||||
|
||||
ES Module (named):
|
||||
```javascript
|
||||
import { clsx } from "clsx";
|
||||
```
|
||||
|
||||
CommonJS:
|
||||
```javascript
|
||||
const clsx = require("clsx");
|
||||
```
|
||||
|
||||
Lite version (string-only):
|
||||
```javascript
|
||||
import clsx from "clsx/lite";
|
||||
// or
|
||||
import { clsx } from "clsx/lite";
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```javascript
|
||||
import clsx from 'clsx';
|
||||
|
||||
// Strings (variadic)
|
||||
clsx('foo', true && 'bar', 'baz');
|
||||
//=> 'foo bar baz'
|
||||
|
||||
// Objects
|
||||
clsx({ foo: true, bar: false, baz: isTrue() });
|
||||
//=> 'foo baz'
|
||||
|
||||
// Arrays
|
||||
clsx(['foo', 0, false, 'bar']);
|
||||
//=> 'foo bar'
|
||||
|
||||
// Mixed arguments (kitchen sink)
|
||||
clsx('foo', [1 && 'bar', { baz: false, bat: null }, ['hello', ['world']]], 'cya');
|
||||
//=> 'foo bar hello world cya'
|
||||
```
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Main clsx Function
|
||||
|
||||
The primary function that accepts any number of arguments of various types and returns a consolidated className string.
|
||||
|
||||
```typescript { .api }
|
||||
function clsx(...inputs: ClassValue[]): string;
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
- `inputs` - Any number of ClassValue arguments (strings, numbers, booleans, objects, arrays, null, undefined)
|
||||
|
||||
**Returns:**
|
||||
- `string` - Consolidated className string with space-separated class names
|
||||
|
||||
**Behavior:**
|
||||
- Falsy values (false, 0, '', null, undefined, NaN) are ignored
|
||||
- Standalone boolean values are discarded
|
||||
- String and number values are included directly
|
||||
- Object keys with truthy values are included as class names
|
||||
- Arrays are processed recursively, flattening nested structures
|
||||
- BigInt values are converted to strings and included
|
||||
|
||||
### Lite Version
|
||||
|
||||
A lightweight variant that only processes string arguments, ignoring all other input types.
|
||||
|
||||
```typescript { .api }
|
||||
function clsx(...inputs: string[]): string;
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
- `inputs` - Any number of string arguments (non-string arguments are ignored)
|
||||
|
||||
**Returns:**
|
||||
- `string` - Consolidated className string from valid string inputs only
|
||||
|
||||
**Usage:**
|
||||
```javascript
|
||||
import clsx from 'clsx/lite';
|
||||
|
||||
// Only strings are processed
|
||||
clsx('hello', true && 'foo', false && 'bar');
|
||||
//=> "hello foo"
|
||||
|
||||
// Non-string inputs are ignored
|
||||
clsx({ foo: true }, ['bar'], 42);
|
||||
//=> ""
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```typescript { .api }
|
||||
type ClassValue = ClassArray | ClassDictionary | string | number | bigint | null | boolean | undefined;
|
||||
|
||||
type ClassDictionary = Record<string, any>;
|
||||
|
||||
type ClassArray = ClassValue[];
|
||||
```
|
||||
|
||||
**Type Descriptions:**
|
||||
- `ClassValue` - Union type representing all valid input types for clsx
|
||||
- `ClassDictionary` - Object type where keys are class names and values determine inclusion
|
||||
- `ClassArray` - Recursive array type allowing nested ClassValue items
|
||||
|
||||
## Input Type Behavior
|
||||
|
||||
### Strings
|
||||
- Included directly in the output
|
||||
- Empty strings are ignored
|
||||
- Whitespace is preserved as provided
|
||||
|
||||
### Numbers
|
||||
- Converted to strings and included
|
||||
- Zero (0) is treated as falsy and ignored
|
||||
- Infinity is converted to "Infinity"
|
||||
- NaN is treated as falsy and ignored
|
||||
|
||||
### Booleans
|
||||
- Used for conditional logic but not included in output
|
||||
- Commonly used with logical operators: `condition && 'class-name'`
|
||||
|
||||
### Objects
|
||||
- Keys with truthy values are included as class names
|
||||
- Keys with falsy values are ignored
|
||||
- Nested objects within arrays are processed normally
|
||||
|
||||
### Arrays
|
||||
- Processed recursively, supporting unlimited nesting
|
||||
- Each element is evaluated according to its type
|
||||
- Empty arrays are ignored
|
||||
|
||||
### Null/Undefined
|
||||
- Always ignored and filtered out
|
||||
- Safe to pass without conditional checks
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### Conditional Classes
|
||||
```javascript
|
||||
clsx('base-class', {
|
||||
'active': isActive,
|
||||
'disabled': !isEnabled,
|
||||
'error': hasError
|
||||
});
|
||||
```
|
||||
|
||||
### Mixed Input Types
|
||||
```javascript
|
||||
clsx(
|
||||
'always-present',
|
||||
condition && 'conditional-class',
|
||||
{
|
||||
'object-based': someBoolean,
|
||||
'another-class': anotherCondition
|
||||
},
|
||||
['array', 'classes', nested && 'nested-class']
|
||||
);
|
||||
```
|
||||
|
||||
### React Component Example
|
||||
```javascript
|
||||
function Button({ variant, size, disabled, className, children }) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
'btn',
|
||||
`btn-${variant}`,
|
||||
`btn-${size}`,
|
||||
{
|
||||
'btn-disabled': disabled
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Tailwind CSS Integration
|
||||
```javascript
|
||||
// Optimal for Tailwind with clsx/lite
|
||||
import clsx from 'clsx/lite';
|
||||
|
||||
const classes = clsx(
|
||||
'text-base',
|
||||
props.active && 'text-primary',
|
||||
props.className
|
||||
);
|
||||
```
|
||||
|
||||
## Distribution Formats
|
||||
|
||||
### Main Package
|
||||
- **CommonJS**: `dist/clsx.js`
|
||||
- **ES Module**: `dist/clsx.mjs`
|
||||
- **UMD**: `dist/clsx.min.js`
|
||||
- **Size**: 239 bytes (gzipped)
|
||||
|
||||
### Lite Package
|
||||
- **CommonJS**: `dist/lite.js`
|
||||
- **ES Module**: `dist/lite.mjs`
|
||||
- **Size**: 140 bytes (gzipped)
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Modern Browsers**: All browsers supporting Array.isArray (IE9+)
|
||||
- **Node.js**: All versions 6+
|
||||
- **Legacy Support**: clsx@1.0.x for IE8 and below
|
||||
|
||||
## Error Handling
|
||||
|
||||
clsx is designed to be fault-tolerant:
|
||||
- Accepts any input without throwing errors
|
||||
- Gracefully handles undefined, null, and unexpected types
|
||||
- No validation errors - invalid inputs are simply ignored
|
||||
- Safe to use with dynamic or untrusted input data
|
||||
7
.tessl/tiles/tessl/npm-clsx/tile.json
Normal file
7
.tessl/tiles/tessl/npm-clsx/tile.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "tessl/npm-clsx",
|
||||
"version": "2.1.0",
|
||||
"docs": "docs/index.md",
|
||||
"describes": "pkg:npm/clsx@2.1.1",
|
||||
"summary": "A tiny (239B) utility for constructing className strings conditionally."
|
||||
}
|
||||
377
.tessl/tiles/tessl/npm-date-fns/docs/arithmetic.md
Normal file
377
.tessl/tiles/tessl/npm-date-fns/docs/arithmetic.md
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
# Date Arithmetic
|
||||
|
||||
Date arithmetic functions provide core date manipulation capabilities for adding, subtracting, and calculating differences between dates. All functions are pure and return new date instances without modifying the input dates.
|
||||
|
||||
## Add Functions
|
||||
|
||||
### add
|
||||
|
||||
Add a duration to a date.
|
||||
|
||||
```typescript { .api }
|
||||
function add<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
duration: Duration
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - The date to add the duration to
|
||||
- `duration` - The duration object specifying amounts to add
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { add } from "date-fns";
|
||||
|
||||
const result = add(new Date(2014, 8, 1), {
|
||||
years: 2,
|
||||
months: 9,
|
||||
weeks: 1,
|
||||
days: 7,
|
||||
hours: 5,
|
||||
minutes: 9,
|
||||
seconds: 30,
|
||||
});
|
||||
//=> Sun Jun 15 2017 05:09:30
|
||||
```
|
||||
|
||||
### Individual Add Functions
|
||||
|
||||
Add specific time units to a date.
|
||||
|
||||
```typescript { .api }
|
||||
function addYears<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addMonths<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addQuarters<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addWeeks<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addDays<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addBusinessDays<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addHours<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addMinutes<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addSeconds<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addMilliseconds<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function addISOWeekYears<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - The date to add to
|
||||
- `amount` - The amount to add (can be negative for subtraction)
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { addDays, addBusinessDays, addMonths } from "date-fns";
|
||||
|
||||
// Add 10 days
|
||||
addDays(new Date(2014, 8, 1), 10);
|
||||
//=> Thu Sep 11 2014
|
||||
|
||||
// Add 10 business days (skips weekends)
|
||||
addBusinessDays(new Date(2014, 8, 1), 10);
|
||||
//=> Mon Sep 15 2014
|
||||
|
||||
// Add 2 months
|
||||
addMonths(new Date(2014, 8, 1), 2);
|
||||
//=> Wed Nov 01 2014
|
||||
```
|
||||
|
||||
## Subtract Functions
|
||||
|
||||
### sub
|
||||
|
||||
Subtract a duration from a date.
|
||||
|
||||
```typescript { .api }
|
||||
function sub<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
duration: Duration
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - The date to subtract the duration from
|
||||
- `duration` - The duration object specifying amounts to subtract
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { sub } from "date-fns";
|
||||
|
||||
const result = sub(new Date(2014, 8, 1), {
|
||||
years: 2,
|
||||
months: 9,
|
||||
weeks: 1,
|
||||
days: 7,
|
||||
hours: 5,
|
||||
minutes: 9,
|
||||
seconds: 30,
|
||||
});
|
||||
//=> Tue Sep 04 2012
|
||||
```
|
||||
|
||||
### Individual Subtract Functions
|
||||
|
||||
Subtract specific time units from a date.
|
||||
|
||||
```typescript { .api }
|
||||
function subYears<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subMonths<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subQuarters<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subWeeks<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subDays<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subBusinessDays<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subHours<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subMinutes<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subSeconds<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subMilliseconds<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
function subISOWeekYears<DateType extends Date>(date: DateArg<DateType>, amount: number): DateType;
|
||||
```
|
||||
|
||||
## Difference Functions
|
||||
|
||||
Calculate the difference between two dates in various units.
|
||||
|
||||
```typescript { .api }
|
||||
function differenceInYears(laterDate: DateArg<Date>, earlierDate: DateArg<Date>, options?: DifferenceInYearsOptions): number;
|
||||
function differenceInMonths(laterDate: DateArg<Date>, earlierDate: DateArg<Date>, options?: DifferenceInMonthsOptions): number;
|
||||
function differenceInQuarters(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInWeeks(laterDate: DateArg<Date>, earlierDate: DateArg<Date>, options?: DifferenceInWeeksOptions): number;
|
||||
function differenceInDays(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInBusinessDays(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInHours(laterDate: DateArg<Date>, earlierDate: DateArg<Date>, options?: DifferenceInHoursOptions): number;
|
||||
function differenceInMinutes(dateLeft: DateArg<Date>, dateRight: DateArg<Date>, options?: DifferenceInMinutesOptions): number;
|
||||
function differenceInSeconds(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInMilliseconds(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `laterDate` - The later date (or `dateLeft` for differenceInMinutes)
|
||||
- `earlierDate` - The earlier date (or `dateRight` for differenceInMinutes)
|
||||
- `options` - Configuration options for supported functions
|
||||
|
||||
**Returns:** The number of units between the dates (positive if laterDate > earlierDate)
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { differenceInDays, differenceInHours, differenceInMonths } from "date-fns";
|
||||
|
||||
// Days difference
|
||||
differenceInDays(new Date(2014, 6, 2), new Date(2014, 0, 1));
|
||||
//=> 182
|
||||
|
||||
// Hours difference
|
||||
differenceInHours(new Date(2014, 6, 2, 06, 0), new Date(2014, 6, 2, 19, 0));
|
||||
//=> -13
|
||||
|
||||
// Months difference
|
||||
differenceInMonths(new Date(2014, 6, 2), new Date(2012, 0, 1));
|
||||
//=> 30
|
||||
```
|
||||
|
||||
## Calendar Difference Functions
|
||||
|
||||
Calculate differences based on calendar periods rather than exact time spans.
|
||||
|
||||
```typescript { .api }
|
||||
function differenceInCalendarYears(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInCalendarMonths(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInCalendarQuarters(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInCalendarWeeks(laterDate: DateArg<Date>, earlierDate: DateArg<Date>, options?: DifferenceInCalendarWeeksOptions): number;
|
||||
function differenceInCalendarDays(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInCalendarISOWeeks(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
function differenceInCalendarISOWeekYears(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { differenceInCalendarMonths, differenceInMonths } from "date-fns";
|
||||
|
||||
// Calendar months (counts month boundaries)
|
||||
differenceInCalendarMonths(new Date(2014, 6, 2), new Date(2014, 0, 31));
|
||||
//=> 6
|
||||
|
||||
// Full months (30+ day periods)
|
||||
differenceInMonths(new Date(2014, 6, 2), new Date(2014, 0, 31));
|
||||
//=> 5
|
||||
```
|
||||
|
||||
## ISO Week Functions
|
||||
|
||||
ISO week year arithmetic for international week-based calculations.
|
||||
|
||||
```typescript { .api }
|
||||
function differenceInISOWeekYears(laterDate: DateArg<Date>, earlierDate: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { differenceInISOWeekYears } from "date-fns";
|
||||
|
||||
differenceInISOWeekYears(new Date(2014, 6, 2), new Date(2012, 0, 1));
|
||||
//=> 2
|
||||
```
|
||||
|
||||
## Unit Conversion Functions
|
||||
|
||||
Convert between different time units for calculations and display.
|
||||
|
||||
```typescript { .api }
|
||||
// Time unit conversions
|
||||
function millisecondsToSeconds(milliseconds: number): number;
|
||||
function millisecondsToMinutes(milliseconds: number): number;
|
||||
function millisecondsToHours(milliseconds: number): number;
|
||||
function secondsToMinutes(seconds: number): number;
|
||||
function secondsToHours(seconds: number): number;
|
||||
function secondsToMilliseconds(seconds: number): number;
|
||||
function minutesToSeconds(minutes: number): number;
|
||||
function minutesToHours(minutes: number): number;
|
||||
function minutesToMilliseconds(minutes: number): number;
|
||||
function hoursToMinutes(hours: number): number;
|
||||
function hoursToSeconds(hours: number): number;
|
||||
function hoursToMilliseconds(hours: number): number;
|
||||
|
||||
// Date unit conversions
|
||||
function daysToWeeks(days: number): number;
|
||||
function weeksToDays(weeks: number): number;
|
||||
function monthsToQuarters(months: number): number;
|
||||
function monthsToYears(months: number): number;
|
||||
function quartersToMonths(quarters: number): number;
|
||||
function quartersToYears(quarters: number): number;
|
||||
function yearsToDays(years: number): number;
|
||||
function yearsToMonths(years: number): number;
|
||||
function yearsToQuarters(years: number): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { hoursToMinutes, daysToWeeks, monthsToQuarters } from "date-fns";
|
||||
|
||||
hoursToMinutes(2); //=> 120
|
||||
daysToWeeks(14); //=> 2
|
||||
monthsToQuarters(9); //=> 3
|
||||
```
|
||||
|
||||
## Utility Functions
|
||||
|
||||
### clamp
|
||||
|
||||
Clamp a date to fit within an interval.
|
||||
|
||||
```typescript { .api }
|
||||
function clamp<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
interval: Interval
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { clamp } from "date-fns";
|
||||
|
||||
clamp(new Date(2014, 0, 1), {
|
||||
start: new Date(2014, 0, 5),
|
||||
end: new Date(2014, 0, 10)
|
||||
});
|
||||
//=> Sun Jan 05 2014 (clamped to start)
|
||||
```
|
||||
|
||||
### intervalToDuration
|
||||
|
||||
Convert an interval to a duration object.
|
||||
|
||||
```typescript { .api }
|
||||
function intervalToDuration(interval: Interval): Duration;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { intervalToDuration } from "date-fns";
|
||||
|
||||
intervalToDuration({
|
||||
start: new Date(2014, 0, 1, 0, 0, 0),
|
||||
end: new Date(2014, 0, 1, 0, 0, 15)
|
||||
});
|
||||
//=> { seconds: 15 }
|
||||
```
|
||||
|
||||
## Rounding Functions
|
||||
|
||||
Round dates to the nearest specified time unit.
|
||||
|
||||
```typescript { .api }
|
||||
function roundToNearestHours<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: RoundToNearestHoursOptions
|
||||
): DateType;
|
||||
|
||||
function roundToNearestMinutes<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: RoundToNearestMinutesOptions
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - The date to round
|
||||
- `options` - Rounding configuration
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { roundToNearestHours, roundToNearestMinutes } from "date-fns";
|
||||
|
||||
// Round to nearest hour
|
||||
roundToNearestHours(new Date(2014, 6, 10, 12, 30));
|
||||
//=> Thu Jul 10 2014 13:00:00
|
||||
|
||||
// Round to nearest 15 minutes
|
||||
roundToNearestMinutes(new Date(2014, 6, 10, 12, 7), { nearestTo: 15 });
|
||||
//=> Thu Jul 10 2014 12:00:00
|
||||
```
|
||||
|
||||
## Duration Conversion
|
||||
|
||||
### milliseconds
|
||||
|
||||
Convert a duration to milliseconds.
|
||||
|
||||
```typescript { .api }
|
||||
function milliseconds(duration: Duration): number;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { milliseconds } from "date-fns";
|
||||
|
||||
milliseconds({ hours: 2, minutes: 30 });
|
||||
//=> 9000000
|
||||
```
|
||||
|
||||
## Options Interfaces
|
||||
|
||||
```typescript { .api }
|
||||
interface DifferenceInYearsOptions extends ContextOptions<Date> {}
|
||||
|
||||
interface DifferenceInMonthsOptions extends ContextOptions<Date> {}
|
||||
|
||||
interface DifferenceInHoursOptions extends RoundingOptions, ContextOptions<Date> {}
|
||||
|
||||
interface DifferenceInMinutesOptions extends RoundingOptions {}
|
||||
|
||||
interface DifferenceInWeeksOptions extends RoundingOptions, ContextOptions<Date> {}
|
||||
|
||||
interface DifferenceInCalendarWeeksOptions extends LocalizedOptions<"options">, WeekOptions, ContextOptions<Date> {}
|
||||
|
||||
interface RoundToNearestHoursOptions<DateType extends Date = Date>
|
||||
extends NearestToUnitOptions<NearestHours>, RoundingOptions, ContextOptions<DateType> {}
|
||||
|
||||
interface RoundToNearestMinutesOptions<DateType extends Date = Date>
|
||||
extends NearestToUnitOptions<NearestMinutes>, RoundingOptions, ContextOptions<DateType> {}
|
||||
|
||||
interface RoundingOptions {
|
||||
roundingMethod?: "ceil" | "floor" | "round" | "trunc";
|
||||
}
|
||||
|
||||
interface NearestToUnitOptions<Unit extends number> {
|
||||
nearestTo?: Unit;
|
||||
}
|
||||
|
||||
type NearestHours = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
type NearestMinutes = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30;
|
||||
```
|
||||
527
.tessl/tiles/tessl/npm-date-fns/docs/components.md
Normal file
527
.tessl/tiles/tessl/npm-date-fns/docs/components.md
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
# Date Components
|
||||
|
||||
Date component functions provide utilities for getting and setting individual date parts (year, month, day, hour, etc.) with proper handling of time zones and edge cases. All functions are pure and return new date instances.
|
||||
|
||||
## Get Functions
|
||||
|
||||
### Year Components
|
||||
|
||||
```typescript { .api }
|
||||
function getYear(date: DateArg<Date>): number;
|
||||
function getISOWeekYear(date: DateArg<Date>): number;
|
||||
function getWeekYear(date: DateArg<Date>, options?: WeekYearOptions): number;
|
||||
function getDecade(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getYear, getISOWeekYear, getDecade } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
getYear(date); //=> 2014
|
||||
getISOWeekYear(date); //=> 2014
|
||||
getDecade(date); //=> 2014
|
||||
|
||||
// Edge case: ISO week year can differ from calendar year
|
||||
getYear(new Date(2005, 0, 1)); //=> 2005
|
||||
getISOWeekYear(new Date(2005, 0, 1)); //=> 2004 (belongs to 2004's ISO week year)
|
||||
```
|
||||
|
||||
### Month and Quarter Components
|
||||
|
||||
```typescript { .api }
|
||||
function getMonth(date: DateArg<Date>): number;
|
||||
function getQuarter(date: DateArg<Date>): number;
|
||||
function getDaysInMonth(date: DateArg<Date>): number;
|
||||
function getDaysInYear(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getMonth, getQuarter, getDaysInMonth, getDaysInYear } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // February 11, 2014
|
||||
|
||||
getMonth(date); //=> 1 (February, 0-indexed)
|
||||
getQuarter(date); //=> 1 (Q1: Jan-Mar)
|
||||
getDaysInMonth(date); //=> 28 (February 2014)
|
||||
getDaysInYear(date); //=> 365 (2014 is not a leap year)
|
||||
|
||||
// Leap year
|
||||
getDaysInYear(new Date(2012, 0, 1)); //=> 366
|
||||
```
|
||||
|
||||
### Day Components
|
||||
|
||||
```typescript { .api }
|
||||
function getDate(date: DateArg<Date>): number;
|
||||
function getDay(date: DateArg<Date>): number;
|
||||
function getISODay(date: DateArg<Date>): number;
|
||||
function getDayOfYear(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getDate, getDay, getISODay, getDayOfYear } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday, February 11, 2014
|
||||
|
||||
getDate(date); //=> 11 (day of month)
|
||||
getDay(date); //=> 2 (Tuesday, 0=Sunday)
|
||||
getISODay(date); //=> 2 (Tuesday, 1=Monday in ISO)
|
||||
getDayOfYear(date); //=> 42 (42nd day of 2014)
|
||||
```
|
||||
|
||||
### Week Components
|
||||
|
||||
```typescript { .api }
|
||||
function getWeek(date: DateArg<Date>, options?: WeekOptions): number;
|
||||
function getISOWeek(date: DateArg<Date>): number;
|
||||
function getWeekOfMonth(date: DateArg<Date>, options?: WeekOptions): number;
|
||||
function getWeeksInMonth(date: DateArg<Date>, options?: WeekOptions): number;
|
||||
function getISOWeeksInYear(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getWeek, getISOWeek, getWeekOfMonth } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
getWeek(date); //=> 7 (7th week of 2014)
|
||||
getISOWeek(date); //=> 7 (ISO week 7)
|
||||
getWeekOfMonth(date); //=> 2 (2nd week of February)
|
||||
```
|
||||
|
||||
### Time Components
|
||||
|
||||
```typescript { .api }
|
||||
function getHours(date: DateArg<Date>): number;
|
||||
function getMinutes(date: DateArg<Date>): number;
|
||||
function getSeconds(date: DateArg<Date>): number;
|
||||
function getMilliseconds(date: DateArg<Date>): number;
|
||||
function getTime(date: DateArg<Date>): number;
|
||||
function getUnixTime(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getHours, getMinutes, getSeconds, getTime, getUnixTime } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11, 14, 30, 45, 500);
|
||||
|
||||
getHours(date); //=> 14
|
||||
getMinutes(date); //=> 30
|
||||
getSeconds(date); //=> 45
|
||||
getMilliseconds(date); //=> 500
|
||||
getTime(date); //=> 1392123045500 (milliseconds since epoch)
|
||||
getUnixTime(date); //=> 1392123045 (seconds since epoch)
|
||||
```
|
||||
|
||||
## Set Functions
|
||||
|
||||
### Year Setting
|
||||
|
||||
```typescript { .api }
|
||||
function setYear<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
year: number
|
||||
): DateType;
|
||||
function setISOWeekYear<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
isoWeekYear: number
|
||||
): DateType;
|
||||
function setWeekYear<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
weekYear: number,
|
||||
options?: WeekYearOptions
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { setYear, setISOWeekYear } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
setYear(date, 2020);
|
||||
//=> Tue Feb 11 2020
|
||||
|
||||
// Handle leap year edge case
|
||||
setYear(new Date(2016, 1, 29), 2017); // Feb 29, 2016
|
||||
//=> Tue Feb 28 2017 (2017 is not a leap year)
|
||||
```
|
||||
|
||||
### Month and Quarter Setting
|
||||
|
||||
```typescript { .api }
|
||||
function setMonth<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
month: number
|
||||
): DateType;
|
||||
function setQuarter<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
quarter: number
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { setMonth, setQuarter } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
setMonth(date, 5); // Set to June (0-indexed)
|
||||
//=> Wed Jun 11 2014
|
||||
|
||||
setQuarter(date, 3); // Set to Q3 (July)
|
||||
//=> Fri Jul 11 2014
|
||||
|
||||
// Handle month overflow
|
||||
setMonth(new Date(2014, 0, 31), 1); // Jan 31 to February
|
||||
//=> Fri Feb 28 2014 (February doesn't have 31 days)
|
||||
```
|
||||
|
||||
### Day Setting
|
||||
|
||||
```typescript { .api }
|
||||
function setDate<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
dayOfMonth: number
|
||||
): DateType;
|
||||
function setDay<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
day: number,
|
||||
options?: WeekOptions
|
||||
): DateType;
|
||||
function setISODay<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
day: number
|
||||
): DateType;
|
||||
function setDayOfYear<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
dayOfYear: number
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { setDate, setDay, setISODay, setDayOfYear } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday
|
||||
|
||||
setDate(date, 20);
|
||||
//=> Thu Feb 20 2014
|
||||
|
||||
setDay(date, 0); // Set to Sunday
|
||||
//=> Sun Feb 09 2014
|
||||
|
||||
setISODay(date, 1); // Set to Monday (ISO: 1=Monday)
|
||||
//=> Mon Feb 10 2014
|
||||
|
||||
setDayOfYear(date, 100); // 100th day of year
|
||||
//=> Thu Apr 10 2014
|
||||
```
|
||||
|
||||
### Week Setting
|
||||
|
||||
```typescript { .api }
|
||||
function setWeek<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
week: number,
|
||||
options?: WeekOptions
|
||||
): DateType;
|
||||
function setISOWeek<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
isoWeek: number
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { setWeek, setISOWeek } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
setWeek(date, 1); // First week of year
|
||||
//=> Mon Jan 06 2014
|
||||
|
||||
setISOWeek(date, 1); // ISO week 1
|
||||
//=> Mon Jan 06 2014
|
||||
```
|
||||
|
||||
### Time Setting
|
||||
|
||||
```typescript { .api }
|
||||
function setHours<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
hours: number
|
||||
): DateType;
|
||||
function setMinutes<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
minutes: number
|
||||
): DateType;
|
||||
function setSeconds<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
seconds: number
|
||||
): DateType;
|
||||
function setMilliseconds<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
milliseconds: number
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { setHours, setMinutes, setSeconds, setMilliseconds } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11, 10, 30, 40, 500);
|
||||
|
||||
setHours(date, 14);
|
||||
//=> Tue Feb 11 2014 14:30:40.500
|
||||
|
||||
setMinutes(date, 45);
|
||||
//=> Tue Feb 11 2014 10:45:40.500
|
||||
|
||||
setSeconds(date, 0);
|
||||
//=> Tue Feb 11 2014 10:30:00.500
|
||||
|
||||
setMilliseconds(date, 0);
|
||||
//=> Tue Feb 11 2014 10:30:40.000
|
||||
```
|
||||
|
||||
## Bulk Setting
|
||||
|
||||
### set
|
||||
|
||||
Set multiple date components at once.
|
||||
|
||||
```typescript { .api }
|
||||
function set<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
values: {
|
||||
year?: number;
|
||||
month?: number;
|
||||
date?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
milliseconds?: number;
|
||||
}
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { set } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11, 10, 30, 40, 500);
|
||||
|
||||
// Set multiple components
|
||||
set(date, {
|
||||
year: 2020,
|
||||
month: 5, // June (0-indexed)
|
||||
date: 15,
|
||||
hours: 14,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
milliseconds: 0
|
||||
});
|
||||
//=> Mon Jun 15 2020 14:00:00.000
|
||||
|
||||
// Partial setting
|
||||
set(date, {
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
milliseconds: 0
|
||||
});
|
||||
//=> Tue Feb 11 2014 00:00:00.000 (only time components changed)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Default Options
|
||||
|
||||
```typescript { .api }
|
||||
function getDefaultOptions(): DefaultOptions;
|
||||
function setDefaultOptions(newOptions: DefaultOptions): void;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { getDefaultOptions, setDefaultOptions } from "date-fns";
|
||||
import { enGB } from "date-fns/locale";
|
||||
|
||||
// Get current defaults
|
||||
const currentOptions = getDefaultOptions();
|
||||
|
||||
// Set new defaults
|
||||
setDefaultOptions({
|
||||
locale: enGB,
|
||||
weekStartsOn: 1, // Monday
|
||||
firstWeekContainsDate: 4
|
||||
});
|
||||
|
||||
// Now all functions will use these defaults unless overridden
|
||||
```
|
||||
|
||||
## Advanced Component Access
|
||||
|
||||
### Overlapping Intervals
|
||||
|
||||
```typescript { .api }
|
||||
function getOverlappingDaysInIntervals(
|
||||
intervalLeft: Interval,
|
||||
intervalRight: Interval
|
||||
): number;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { getOverlappingDaysInIntervals } from "date-fns";
|
||||
|
||||
const leftInterval = {
|
||||
start: new Date(2014, 0, 10),
|
||||
end: new Date(2014, 0, 20)
|
||||
};
|
||||
|
||||
const rightInterval = {
|
||||
start: new Date(2014, 0, 17),
|
||||
end: new Date(2014, 0, 21)
|
||||
};
|
||||
|
||||
getOverlappingDaysInIntervals(leftInterval, rightInterval);
|
||||
//=> 4 (days 17, 18, 19, 20)
|
||||
```
|
||||
|
||||
## Component Manipulation Patterns
|
||||
|
||||
### Date Normalization
|
||||
|
||||
```typescript
|
||||
import { set } from "date-fns";
|
||||
|
||||
// Normalize to start of day
|
||||
function normalizeToStartOfDay(date: Date): Date {
|
||||
return set(date, {
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
milliseconds: 0
|
||||
});
|
||||
}
|
||||
|
||||
// Normalize to end of day
|
||||
function normalizeToEndOfDay(date: Date): Date {
|
||||
return set(date, {
|
||||
hours: 23,
|
||||
minutes: 59,
|
||||
seconds: 59,
|
||||
milliseconds: 999
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Safe Component Setting
|
||||
|
||||
```typescript
|
||||
import { setMonth, getDaysInMonth, setDate } from "date-fns";
|
||||
|
||||
// Safely set month, handling day overflow
|
||||
function safeSetMonth(date: Date, month: number): Date {
|
||||
const tempDate = setMonth(date, month);
|
||||
const daysInNewMonth = getDaysInMonth(tempDate);
|
||||
const currentDay = getDate(date);
|
||||
|
||||
if (currentDay > daysInNewMonth) {
|
||||
return setDate(tempDate, daysInNewMonth);
|
||||
}
|
||||
|
||||
return tempDate;
|
||||
}
|
||||
```
|
||||
|
||||
### Component Validation
|
||||
|
||||
```typescript
|
||||
import { isValid, set, getYear, getMonth, getDate } from "date-fns";
|
||||
|
||||
function isValidDateComponents(year: number, month: number, day: number): boolean {
|
||||
try {
|
||||
const testDate = set(new Date(), { year, month, date: day });
|
||||
return isValid(testDate) &&
|
||||
getYear(testDate) === year &&
|
||||
getMonth(testDate) === month &&
|
||||
getDate(testDate) === day;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### WeekOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface WeekOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
```
|
||||
|
||||
### WeekYearOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface WeekYearOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
}
|
||||
```
|
||||
|
||||
### DefaultOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface DefaultOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
}
|
||||
```
|
||||
|
||||
## Edge Cases and Considerations
|
||||
|
||||
### Month Overflow
|
||||
|
||||
When setting months, date-fns automatically handles overflow:
|
||||
|
||||
```typescript
|
||||
import { setMonth } from "date-fns";
|
||||
|
||||
// January 31 → February 28 (not March 3)
|
||||
setMonth(new Date(2014, 0, 31), 1); //=> Feb 28, 2014
|
||||
```
|
||||
|
||||
### Leap Year Handling
|
||||
|
||||
```typescript
|
||||
import { setYear } from "date-fns";
|
||||
|
||||
// February 29 in leap year → February 28 in non-leap year
|
||||
setYear(new Date(2016, 1, 29), 2017); //=> Feb 28, 2017
|
||||
```
|
||||
|
||||
### Week Year vs Calendar Year
|
||||
|
||||
ISO week years can differ from calendar years at year boundaries:
|
||||
|
||||
```typescript
|
||||
import { getYear, getISOWeekYear } from "date-fns";
|
||||
|
||||
const date = new Date(2005, 0, 1); // January 1, 2005 (Saturday)
|
||||
getYear(date); //=> 2005
|
||||
getISOWeekYear(date); //=> 2004 (belongs to last week of ISO year 2004)
|
||||
```
|
||||
525
.tessl/tiles/tessl/npm-date-fns/docs/constants.md
Normal file
525
.tessl/tiles/tessl/npm-date-fns/docs/constants.md
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
# Constants and Utilities
|
||||
|
||||
Constants and utility functions provide mathematical constants for time calculations, utility functions for date construction, and helper functions for common operations. These form the foundation of date-fns functionality.
|
||||
|
||||
## Time Constants
|
||||
|
||||
### Basic Time Units
|
||||
|
||||
```typescript { .api }
|
||||
const millisecondsInSecond: number = 1000;
|
||||
const millisecondsInMinute: number = 60000;
|
||||
const millisecondsInHour: number = 3600000;
|
||||
const millisecondsInDay: number = 86400000;
|
||||
const millisecondsInWeek: number = 604800000;
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { millisecondsInDay, millisecondsInHour } from "date-fns/constants";
|
||||
|
||||
// Calculate milliseconds for custom durations
|
||||
const threeDaysInMs = 3 * millisecondsInDay;
|
||||
const sixHoursInMs = 6 * millisecondsInHour;
|
||||
|
||||
// Convert millisecond differences to days
|
||||
const msDifference = date2.getTime() - date1.getTime();
|
||||
const daysDifference = msDifference / millisecondsInDay;
|
||||
```
|
||||
|
||||
### Duration Constants
|
||||
|
||||
```typescript { .api }
|
||||
const secondsInMinute: number = 60;
|
||||
const secondsInHour: number = 3600;
|
||||
const secondsInDay: number = 86400;
|
||||
const secondsInWeek: number = 604800;
|
||||
const secondsInMonth: number = 2629800; // Average month (secondsInYear / 12)
|
||||
const secondsInQuarter: number = 7889400; // Average quarter (secondsInMonth * 3)
|
||||
const secondsInYear: number = 31557600; // Average year (secondsInDay * daysInYear)
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { secondsInDay, secondsInHour } from "date-fns/constants";
|
||||
|
||||
// Calculate time intervals
|
||||
const workingHoursPerDay = 8;
|
||||
const workingSecondsPerDay = workingHoursPerDay * secondsInHour;
|
||||
|
||||
// Convert Unix timestamps
|
||||
const unixTimestamp = Date.now() / 1000;
|
||||
const daysFromEpoch = unixTimestamp / secondsInDay;
|
||||
```
|
||||
|
||||
### Calendar Constants
|
||||
|
||||
```typescript { .api }
|
||||
const minutesInHour: number = 60;
|
||||
const minutesInDay: number = 1440;
|
||||
const minutesInMonth: number = 43200; // Average month
|
||||
const minutesInYear: number = 525600; // Average year
|
||||
|
||||
const daysInWeek: number = 7;
|
||||
const daysInYear: number = 365.2425; // Average with leap years
|
||||
|
||||
const monthsInQuarter: number = 3;
|
||||
const monthsInYear: number = 12;
|
||||
const quartersInYear: number = 4;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { daysInWeek, monthsInYear, quartersInYear } from "date-fns/constants";
|
||||
|
||||
// Calculate periods
|
||||
const weeksInYear = daysInYear / daysInWeek; // ~52.18
|
||||
const monthsInTwoYears = 2 * monthsInYear; // 24
|
||||
const quartersInFiveYears = 5 * quartersInYear; // 20
|
||||
```
|
||||
|
||||
### Boundary Constants
|
||||
|
||||
```typescript { .api }
|
||||
const maxTime: number = 8640000000000000;
|
||||
const minTime: number = -8640000000000000;
|
||||
```
|
||||
|
||||
These represent the maximum and minimum valid JavaScript Date values.
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { maxTime, minTime } from "date-fns/constants";
|
||||
|
||||
// Create boundary dates
|
||||
const maxDate = new Date(maxTime);
|
||||
//=> Sat Sep 13 275760 02:00:00 (maximum valid date)
|
||||
|
||||
const minDate = new Date(minTime);
|
||||
//=> Tue Apr 20 -271821 02:00:00 (minimum valid date)
|
||||
|
||||
// Validate date ranges
|
||||
function isValidDateRange(date: Date): boolean {
|
||||
const time = date.getTime();
|
||||
return time >= minTime && time <= maxTime;
|
||||
}
|
||||
```
|
||||
|
||||
### Construction Symbol
|
||||
|
||||
```typescript { .api }
|
||||
const constructFromSymbol: unique symbol = Symbol.for("constructDateFrom");
|
||||
```
|
||||
|
||||
Internal symbol used for date constructor injection in generic date types and extensions like UTCDate.
|
||||
|
||||
## Core Utility Functions
|
||||
|
||||
### Date Construction
|
||||
|
||||
```typescript { .api }
|
||||
function toDate<DateType extends Date>(
|
||||
argument: DateArg<DateType>,
|
||||
context?: ContextFn<DateType>
|
||||
): DateType;
|
||||
|
||||
function constructFrom<DateType extends Date>(
|
||||
date: DateArg<DateType> | ContextFn<DateType>,
|
||||
value: DateArg<Date>
|
||||
): DateType;
|
||||
|
||||
function constructNow<DateType extends Date>(
|
||||
date: DateArg<DateType> | ContextFn<DateType>
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { toDate, constructFrom, constructNow } from "date-fns";
|
||||
|
||||
// Convert various inputs to Date
|
||||
toDate(new Date(2014, 1, 11)); //=> Date object
|
||||
toDate('2014-02-11'); //=> Date object
|
||||
toDate(1392076800000); //=> Date object
|
||||
|
||||
// Construct date with same type as reference
|
||||
const utcDate = new UTCDate(2014, 1, 11);
|
||||
const newUTCDate = constructFrom(utcDate, '2015-02-11');
|
||||
//=> UTCDate instance
|
||||
|
||||
// Construct "now" with same type as reference
|
||||
const nowUTC = constructNow(utcDate);
|
||||
//=> UTCDate instance of current time
|
||||
```
|
||||
|
||||
The `toDate` function is the core conversion utility that all date-fns functions use internally. It converts any DateArg input to a proper Date instance, enabling automatic handling of timestamps, date strings, and existing Date objects.
|
||||
|
||||
The `constructFrom` function enables generic date construction, preserving the type of the reference date. This is crucial for working with date extensions like UTCDate or TZDate.
|
||||
|
||||
The `constructNow` function creates a new date representing the current time, but using the same constructor type as the reference date.
|
||||
|
||||
### Date Validation
|
||||
|
||||
```typescript { .api }
|
||||
function isDate(value: any): value is Date;
|
||||
function isValid(date: any): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isDate, isValid } from "date-fns";
|
||||
|
||||
// Type checking
|
||||
isDate(new Date()); //=> true
|
||||
isDate('2014-02-11'); //=> false
|
||||
isDate(null); //=> false
|
||||
|
||||
// Validity checking
|
||||
isValid(new Date(2014, 1, 11)); //=> true
|
||||
isValid(new Date('invalid')); //=> false
|
||||
isValid(new Date(2014, 13, 1)); //=> false (invalid month)
|
||||
```
|
||||
|
||||
### Unix Time Utilities
|
||||
|
||||
```typescript { .api }
|
||||
function fromUnixTime(unixTime: number): Date;
|
||||
function getUnixTime(date: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { fromUnixTime, getUnixTime } from "date-fns";
|
||||
|
||||
// Convert from Unix timestamp (seconds since epoch)
|
||||
fromUnixTime(1392123045);
|
||||
//=> Tue Feb 11 2014 11:30:45
|
||||
|
||||
// Convert to Unix timestamp
|
||||
getUnixTime(new Date(2014, 1, 11, 11, 30, 45));
|
||||
//=> 1392123045
|
||||
```
|
||||
|
||||
## Interval Utilities
|
||||
|
||||
### Interval Construction
|
||||
|
||||
```typescript { .api }
|
||||
function interval(start: DateArg<Date>, end: DateArg<Date>): Interval;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { interval } from "date-fns";
|
||||
|
||||
const dateInterval = interval(
|
||||
new Date(2014, 0, 1),
|
||||
new Date(2014, 0, 7)
|
||||
);
|
||||
//=> { start: Date(2014-01-01), end: Date(2014-01-07) }
|
||||
```
|
||||
|
||||
### Duration Conversion
|
||||
|
||||
```typescript { .api }
|
||||
function intervalToDuration(interval: Interval): Duration;
|
||||
function milliseconds(duration: Duration): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { intervalToDuration, milliseconds } from "date-fns";
|
||||
|
||||
// Convert interval to duration object
|
||||
const duration = intervalToDuration({
|
||||
start: new Date(2014, 0, 1, 0, 0, 0),
|
||||
end: new Date(2014, 0, 1, 1, 30, 15)
|
||||
});
|
||||
//=> { hours: 1, minutes: 30, seconds: 15 }
|
||||
|
||||
// Get total milliseconds from duration
|
||||
const totalMs = milliseconds({
|
||||
hours: 2,
|
||||
minutes: 30,
|
||||
seconds: 45
|
||||
});
|
||||
//=> 9045000 (2.5 hours + 45 seconds in milliseconds)
|
||||
```
|
||||
|
||||
### Date Clamping
|
||||
|
||||
```typescript { .api }
|
||||
function clamp<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
interval: Interval
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { clamp } from "date-fns";
|
||||
|
||||
// Clamp date to fit within bounds
|
||||
clamp(new Date(2014, 0, 1), {
|
||||
start: new Date(2014, 0, 5),
|
||||
end: new Date(2014, 0, 10)
|
||||
});
|
||||
//=> Date(2014-01-05) (clamped to start bound)
|
||||
|
||||
clamp(new Date(2014, 0, 15), {
|
||||
start: new Date(2014, 0, 5),
|
||||
end: new Date(2014, 0, 10)
|
||||
});
|
||||
//=> Date(2014-01-10) (clamped to end bound)
|
||||
```
|
||||
|
||||
## Type Transformation
|
||||
|
||||
### Date Transposition
|
||||
|
||||
```typescript { .api }
|
||||
function transpose<InputDate extends Date, ResultDate extends Date>(
|
||||
date: InputDate,
|
||||
constructor: ResultDate | GenericDateConstructor<ResultDate> | ContextFn<ResultDate>
|
||||
): ResultDate;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { transpose } from "date-fns";
|
||||
|
||||
// Convert between different date types
|
||||
const regularDate = new Date(2022, 6, 10); // July 10, 2022 00:00 in local time
|
||||
const utcDate = transpose(regularDate, UTCDate);
|
||||
//=> UTCDate instance: 'Sun Jul 10 2022 00:00:00 GMT+0000 (UTC)'
|
||||
|
||||
// Transpose to custom date extension
|
||||
const tzDate = transpose(regularDate, TZDate);
|
||||
//=> TZDate instance maintaining the date values in the target timezone
|
||||
```
|
||||
|
||||
The `transpose` function is essential for working with date extensions. Unlike simple conversion, it preserves the date values (year, month, day, hour, etc.) while changing the underlying date type. This is particularly useful when moving between different timezone representations or date implementations.
|
||||
|
||||
## Mathematical Utilities
|
||||
|
||||
### Time Calculations
|
||||
|
||||
```typescript
|
||||
import { millisecondsInDay, secondsInHour, minutesInDay } from "date-fns/constants";
|
||||
|
||||
// Calculate elapsed time
|
||||
function getElapsedDays(startDate: Date, endDate: Date): number {
|
||||
const diffMs = endDate.getTime() - startDate.getTime();
|
||||
return diffMs / millisecondsInDay;
|
||||
}
|
||||
|
||||
// Convert between units
|
||||
function hoursToMinutes(hours: number): number {
|
||||
return hours * minutesInHour;
|
||||
}
|
||||
|
||||
function daysToSeconds(days: number): number {
|
||||
return days * secondsInDay;
|
||||
}
|
||||
```
|
||||
|
||||
### Age Calculations
|
||||
|
||||
```typescript
|
||||
import { daysInYear } from "date-fns/constants";
|
||||
|
||||
function calculateAge(birthDate: Date, referenceDate: Date = new Date()): number {
|
||||
const diffMs = referenceDate.getTime() - birthDate.getTime();
|
||||
const diffDays = diffMs / millisecondsInDay;
|
||||
return Math.floor(diffDays / daysInYear);
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Constant-Based Calculations
|
||||
|
||||
```typescript
|
||||
import { millisecondsInDay, secondsInHour } from "date-fns/constants";
|
||||
|
||||
// Faster than repeated calculations
|
||||
function addDaysOptimized(date: Date, days: number): Date {
|
||||
return new Date(date.getTime() + days * millisecondsInDay);
|
||||
}
|
||||
|
||||
// Pre-calculated constants for common operations
|
||||
const MILLISECONDS_IN_HOUR = millisecondsInHour;
|
||||
const MILLISECONDS_IN_WEEK = millisecondsInWeek;
|
||||
|
||||
function addHoursOptimized(date: Date, hours: number): Date {
|
||||
return new Date(date.getTime() + hours * MILLISECONDS_IN_HOUR);
|
||||
}
|
||||
```
|
||||
|
||||
### Boundary Checking
|
||||
|
||||
```typescript
|
||||
import { maxTime, minTime } from "date-fns/constants";
|
||||
|
||||
function safeCreateDate(timestamp: number): Date | null {
|
||||
if (timestamp < minTime || timestamp > maxTime) {
|
||||
return null; // Invalid timestamp
|
||||
}
|
||||
return new Date(timestamp);
|
||||
}
|
||||
|
||||
function isDateInValidRange(date: Date): boolean {
|
||||
const time = date.getTime();
|
||||
return !isNaN(time) && time >= minTime && time <= maxTime;
|
||||
}
|
||||
```
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### Core Types
|
||||
|
||||
```typescript { .api }
|
||||
type DateArg<DateType extends Date> = DateType | number | string;
|
||||
|
||||
interface Duration {
|
||||
years?: number;
|
||||
months?: number;
|
||||
weeks?: number;
|
||||
days?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
}
|
||||
|
||||
interface Interval {
|
||||
start: DateArg<Date>;
|
||||
end: DateArg<Date>;
|
||||
}
|
||||
|
||||
interface GenericDateConstructor<DateType extends Date = Date> {
|
||||
new (): DateType;
|
||||
new (value: DateArg<Date> & {}): DateType;
|
||||
new (
|
||||
year: number,
|
||||
month: number,
|
||||
date?: number,
|
||||
hours?: number,
|
||||
minutes?: number,
|
||||
seconds?: number,
|
||||
ms?: number,
|
||||
): DateType;
|
||||
}
|
||||
```
|
||||
|
||||
### Utility Types
|
||||
|
||||
```typescript { .api }
|
||||
interface ConstructableDate extends Date {
|
||||
[constructFromSymbol]: <DateType extends Date = Date>(
|
||||
value: DateArg<Date> & {}
|
||||
) => DateType;
|
||||
}
|
||||
|
||||
type DurationUnit = keyof Duration;
|
||||
|
||||
type IntervalUnit = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second';
|
||||
|
||||
type LocaleUnit = 'second' | 'minute' | 'hour' | 'day' | 'date' | 'month' | 'year';
|
||||
|
||||
type Day = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
type Month = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;
|
||||
type Quarter = 1 | 2 | 3 | 4;
|
||||
type Era = 0 | 1; // 0 = AD, 1 = BC
|
||||
|
||||
type FirstWeekContainsDate = 1 | 4;
|
||||
|
||||
type ISOStringFormat = "extended" | "basic";
|
||||
type ISOStringRepresentation = "complete" | "date" | "time";
|
||||
|
||||
type RoundingMethod = "ceil" | "floor" | "round" | "trunc";
|
||||
|
||||
type NearestHours = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
type NearestMinutes = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30;
|
||||
|
||||
type ContextFn<DateType extends Date> = (value: DateArg<Date> & {}) => DateType;
|
||||
```
|
||||
|
||||
## Common Utility Patterns
|
||||
|
||||
### Safe Date Operations
|
||||
|
||||
```typescript
|
||||
import { isValid, toDate, maxTime, minTime } from "date-fns";
|
||||
|
||||
function safeDateOperation<T>(
|
||||
date: any,
|
||||
operation: (validDate: Date) => T,
|
||||
fallback: T
|
||||
): T {
|
||||
try {
|
||||
const converted = toDate(date);
|
||||
if (!isValid(converted)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const timestamp = converted.getTime();
|
||||
if (timestamp < minTime || timestamp > maxTime) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return operation(converted);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Date Range Validation
|
||||
|
||||
```typescript
|
||||
import { isValid, constructFrom } from "date-fns";
|
||||
|
||||
function createValidDateRange(start: any, end: any): Interval | null {
|
||||
try {
|
||||
const startDate = constructFrom(new Date(), start);
|
||||
const endDate = constructFrom(new Date(), end);
|
||||
|
||||
if (!isValid(startDate) || !isValid(endDate)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (startDate > endDate) {
|
||||
return { start: endDate, end: startDate }; // Swap if needed
|
||||
}
|
||||
|
||||
return { start: startDate, end: endDate };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Timing
|
||||
|
||||
```typescript
|
||||
import { millisecondsInSecond } from "date-fns/constants";
|
||||
|
||||
function measurePerformance<T>(operation: () => T): { result: T; durationMs: number } {
|
||||
const start = performance.now();
|
||||
const result = operation();
|
||||
const end = performance.now();
|
||||
|
||||
return {
|
||||
result,
|
||||
durationMs: end - start
|
||||
};
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
if (ms < millisecondsInSecond) {
|
||||
return `${ms.toFixed(2)}ms`;
|
||||
} else {
|
||||
return `${(ms / millisecondsInSecond).toFixed(2)}s`;
|
||||
}
|
||||
}
|
||||
```
|
||||
486
.tessl/tiles/tessl/npm-date-fns/docs/formatting.md
Normal file
486
.tessl/tiles/tessl/npm-date-fns/docs/formatting.md
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
# Date Formatting
|
||||
|
||||
Date formatting functions provide comprehensive date-to-string conversion capabilities with extensive customization options, internationalization support, and multiple output formats. All formatting functions are pure and work with various date input types.
|
||||
|
||||
## Core Formatting
|
||||
|
||||
### format
|
||||
|
||||
Format a date according to a given pattern string.
|
||||
|
||||
```typescript { .api }
|
||||
function format(
|
||||
date: DateArg<Date>,
|
||||
formatStr: string,
|
||||
options?: FormatOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - The date to format
|
||||
- `formatStr` - The format pattern string
|
||||
- `options` - Optional formatting configuration
|
||||
|
||||
**Format Tokens:**
|
||||
- `yyyy` - 4-digit year
|
||||
- `MM` - 2-digit month (01-12)
|
||||
- `dd` - 2-digit day (01-31)
|
||||
- `HH` - 24-hour format hour (00-23)
|
||||
- `mm` - 2-digit minute (00-59)
|
||||
- `ss` - 2-digit second (00-59)
|
||||
- `PP` - Localized date (Jan 1, 2023)
|
||||
- `pp` - Localized time (12:00 AM)
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
|
||||
// Basic formatting
|
||||
format(new Date(2014, 1, 11), "yyyy-MM-dd");
|
||||
//=> '2014-02-11'
|
||||
|
||||
// Full date with time
|
||||
format(new Date(2014, 1, 11, 14, 30, 45), "yyyy-MM-dd HH:mm:ss");
|
||||
//=> '2014-02-11 14:30:45'
|
||||
|
||||
// Localized format
|
||||
format(new Date(2014, 1, 11), "PP");
|
||||
//=> 'Feb 11, 2014'
|
||||
|
||||
// Custom pattern
|
||||
format(new Date(2014, 1, 11), "EEEE, MMMM do, yyyy");
|
||||
//=> 'Tuesday, February 11th, 2014'
|
||||
```
|
||||
|
||||
### lightFormat
|
||||
|
||||
Lightweight format without locale support for better performance.
|
||||
|
||||
```typescript { .api }
|
||||
function lightFormat(date: DateArg<Date>, formatStr: string): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { lightFormat } from "date-fns";
|
||||
|
||||
lightFormat(new Date(2014, 1, 11), "yyyy-MM-dd");
|
||||
//=> '2014-02-11'
|
||||
```
|
||||
|
||||
## Distance Formatting
|
||||
|
||||
### formatDistance
|
||||
|
||||
Format the distance between two dates in words.
|
||||
|
||||
```typescript { .api }
|
||||
function formatDistance(
|
||||
dateLeft: DateArg<Date>,
|
||||
dateRight: DateArg<Date>,
|
||||
options?: FormatDistanceOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dateLeft` - The first date
|
||||
- `dateRight` - The second date
|
||||
- `options` - Distance formatting options
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { formatDistance } from "date-fns";
|
||||
|
||||
// Basic distance
|
||||
formatDistance(
|
||||
new Date(2014, 6, 2),
|
||||
new Date(2015, 0, 1)
|
||||
);
|
||||
//=> '6 months'
|
||||
|
||||
// With suffix
|
||||
formatDistance(
|
||||
new Date(2014, 6, 2),
|
||||
new Date(2015, 0, 1),
|
||||
{ addSuffix: true }
|
||||
);
|
||||
//=> 'in 6 months'
|
||||
|
||||
// Include seconds
|
||||
formatDistance(
|
||||
new Date(2014, 6, 2, 0, 0, 15),
|
||||
new Date(2014, 6, 2, 0, 0, 0),
|
||||
{ includeSeconds: true }
|
||||
);
|
||||
//=> 'less than 20 seconds'
|
||||
```
|
||||
|
||||
### formatDistanceStrict
|
||||
|
||||
Format distance with strict units (no rounding to nearest unit).
|
||||
|
||||
```typescript { .api }
|
||||
function formatDistanceStrict(
|
||||
dateLeft: DateArg<Date>,
|
||||
dateRight: DateArg<Date>,
|
||||
options?: FormatDistanceStrictOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatDistanceStrict } from "date-fns";
|
||||
|
||||
formatDistanceStrict(
|
||||
new Date(2014, 6, 2, 0, 5),
|
||||
new Date(2014, 6, 2, 0, 0)
|
||||
);
|
||||
//=> '5 minutes'
|
||||
```
|
||||
|
||||
### formatDistanceToNow
|
||||
|
||||
Format the distance between a date and now.
|
||||
|
||||
```typescript { .api }
|
||||
function formatDistanceToNow(
|
||||
date: DateArg<Date>,
|
||||
options?: FormatDistanceOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
|
||||
formatDistanceToNow(new Date(2014, 6, 2), { addSuffix: true });
|
||||
//=> '8 years ago' (assuming current date is 2022)
|
||||
```
|
||||
|
||||
### formatDistanceToNowStrict
|
||||
|
||||
Strict distance formatting to current time.
|
||||
|
||||
```typescript { .api }
|
||||
function formatDistanceToNowStrict(
|
||||
date: DateArg<Date>,
|
||||
options?: FormatDistanceStrictOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
## Relative Formatting
|
||||
|
||||
### formatRelative
|
||||
|
||||
Format a date relative to a base date (e.g., "yesterday", "last Friday").
|
||||
|
||||
```typescript { .api }
|
||||
function formatRelative(
|
||||
date: DateArg<Date>,
|
||||
baseDate: DateArg<Date>,
|
||||
options?: FormatRelativeOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { formatRelative } from "date-fns";
|
||||
|
||||
const baseDate = new Date(2000, 0, 1, 0, 0, 0);
|
||||
|
||||
// Yesterday
|
||||
formatRelative(new Date(1999, 11, 31), baseDate);
|
||||
//=> 'yesterday at 12:00 AM'
|
||||
|
||||
// Last week
|
||||
formatRelative(new Date(1999, 11, 27), baseDate);
|
||||
//=> 'last Monday at 12:00 AM'
|
||||
|
||||
// Next week
|
||||
formatRelative(new Date(2000, 0, 7), baseDate);
|
||||
//=> 'next Friday at 12:00 AM'
|
||||
```
|
||||
|
||||
## ISO and Standard Formats
|
||||
|
||||
### formatISO
|
||||
|
||||
Format a date as ISO 8601 string.
|
||||
|
||||
```typescript { .api }
|
||||
function formatISO(date: DateArg<Date>, options?: FormatISOOptions): string;
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `representation` - 'complete' | 'date' | 'time'
|
||||
- `format` - 'extended' | 'basic'
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { formatISO } from "date-fns";
|
||||
|
||||
// Complete ISO format
|
||||
formatISO(new Date(2019, 8, 18, 19, 0, 52));
|
||||
//=> '2019-09-18T19:00:52+02:00'
|
||||
|
||||
// Date only
|
||||
formatISO(new Date(2019, 8, 18), { representation: 'date' });
|
||||
//=> '2019-09-18'
|
||||
|
||||
// Basic format
|
||||
formatISO(new Date(2019, 8, 18), { format: 'basic' });
|
||||
//=> '20190918T190052+0200'
|
||||
```
|
||||
|
||||
### formatISO9075
|
||||
|
||||
Format a date as ISO 9075 string (SQL compatible).
|
||||
|
||||
```typescript { .api }
|
||||
function formatISO9075(date: DateArg<Date>, options?: FormatISOOptions): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatISO9075 } from "date-fns";
|
||||
|
||||
formatISO9075(new Date(2019, 8, 18, 19, 0, 52));
|
||||
//=> '2019-09-18 19:00:52'
|
||||
```
|
||||
|
||||
### formatRFC3339
|
||||
|
||||
Format a date as RFC 3339 string.
|
||||
|
||||
```typescript { .api }
|
||||
function formatRFC3339(date: DateArg<Date>, options?: FormatRFC3339Options): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatRFC3339 } from "date-fns";
|
||||
|
||||
formatRFC3339(new Date(2019, 8, 18, 19, 0, 52));
|
||||
//=> '2019-09-18T19:00:52+02:00'
|
||||
```
|
||||
|
||||
### formatRFC7231
|
||||
|
||||
Format a date as RFC 7231 string (HTTP date).
|
||||
|
||||
```typescript { .api }
|
||||
function formatRFC7231(date: DateArg<Date>): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatRFC7231 } from "date-fns";
|
||||
|
||||
formatRFC7231(new Date(2014, 11, 6, 15, 45));
|
||||
//=> 'Sat, 06 Dec 2014 15:45:00 GMT'
|
||||
```
|
||||
|
||||
## Duration Formatting
|
||||
|
||||
### formatDuration
|
||||
|
||||
Format a duration object into a human-readable string.
|
||||
|
||||
```typescript { .api }
|
||||
function formatDuration(
|
||||
duration: Duration,
|
||||
options?: FormatDurationOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { formatDuration } from "date-fns";
|
||||
|
||||
// Basic duration
|
||||
formatDuration({
|
||||
years: 2,
|
||||
months: 9,
|
||||
weeks: 1,
|
||||
days: 7,
|
||||
hours: 5,
|
||||
minutes: 9,
|
||||
seconds: 30
|
||||
});
|
||||
//=> '2 years 9 months 1 week 7 days 5 hours 9 minutes 30 seconds'
|
||||
|
||||
// With custom format
|
||||
formatDuration({ hours: 1, minutes: 30 }, { format: ['hours', 'minutes'] });
|
||||
//=> '1 hour 30 minutes'
|
||||
```
|
||||
|
||||
### formatISODuration
|
||||
|
||||
Format a duration as ISO 8601 duration string.
|
||||
|
||||
```typescript { .api }
|
||||
function formatISODuration(duration: Duration): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { formatISODuration } from "date-fns";
|
||||
|
||||
formatISODuration({
|
||||
years: 2,
|
||||
months: 9,
|
||||
weeks: 1,
|
||||
days: 7,
|
||||
hours: 5,
|
||||
minutes: 9,
|
||||
seconds: 30
|
||||
});
|
||||
//=> 'P2Y9M1W7DT5H9M30S'
|
||||
```
|
||||
|
||||
## Internationalization Formatting
|
||||
|
||||
### intlFormat
|
||||
|
||||
Format a date using the Intl.DateTimeFormat API.
|
||||
|
||||
```typescript { .api }
|
||||
function intlFormat(
|
||||
date: DateArg<Date>,
|
||||
formatOptions?: Intl.DateTimeFormatOptions,
|
||||
localeOptions?: string | string[]
|
||||
): string;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { intlFormat } from "date-fns";
|
||||
|
||||
// Default format
|
||||
intlFormat(new Date(2019, 0, 1));
|
||||
//=> '1/1/2019'
|
||||
|
||||
// Custom format
|
||||
intlFormat(new Date(2019, 0, 1), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
//=> 'January 1, 2019'
|
||||
|
||||
// Different locale
|
||||
intlFormat(new Date(2019, 0, 1), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
}, 'de-DE');
|
||||
//=> '1. Januar 2019'
|
||||
```
|
||||
|
||||
### intlFormatDistance
|
||||
|
||||
Format distance using the Intl.RelativeTimeFormat API.
|
||||
|
||||
```typescript { .api }
|
||||
function intlFormatDistance(
|
||||
dateLeft: DateArg<Date>,
|
||||
dateRight: DateArg<Date>,
|
||||
options?: IntlFormatDistanceOptions
|
||||
): string;
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { intlFormatDistance } from "date-fns";
|
||||
|
||||
intlFormatDistance(
|
||||
new Date(1986, 3, 4, 11, 30, 0),
|
||||
new Date(1986, 3, 4, 10, 30, 0),
|
||||
{ locale: 'en-US' }
|
||||
);
|
||||
//=> 'in 1 hour'
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### FormatOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatDistanceOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatDistanceOptions {
|
||||
includeSeconds?: boolean;
|
||||
addSuffix?: boolean;
|
||||
locale?: Locale;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatDistanceStrictOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatDistanceStrictOptions {
|
||||
addSuffix?: boolean;
|
||||
unit?: 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year';
|
||||
roundingMethod?: 'floor' | 'ceil' | 'round';
|
||||
locale?: Locale;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatRelativeOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatRelativeOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatISOOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatISOOptions {
|
||||
format?: 'extended' | 'basic';
|
||||
representation?: 'complete' | 'date' | 'time';
|
||||
}
|
||||
```
|
||||
|
||||
### FormatRFC3339Options
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatRFC3339Options {
|
||||
fractionDigits?: 0 | 1 | 2 | 3;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatDurationOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatDurationOptions {
|
||||
format?: DurationUnit[];
|
||||
zero?: boolean;
|
||||
delimiter?: string;
|
||||
locale?: Locale;
|
||||
}
|
||||
```
|
||||
|
||||
### IntlFormatDistanceOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface IntlFormatDistanceOptions {
|
||||
locale?: string | string[];
|
||||
unit?: Intl.RelativeTimeFormatUnit;
|
||||
localeMatcher?: 'lookup' | 'best fit';
|
||||
numeric?: 'always' | 'auto';
|
||||
style?: 'long' | 'short' | 'narrow';
|
||||
}
|
||||
```
|
||||
477
.tessl/tiles/tessl/npm-date-fns/docs/fp.md
Normal file
477
.tessl/tiles/tessl/npm-date-fns/docs/fp.md
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
# Functional Programming
|
||||
|
||||
The date-fns functional programming (FP) module provides curried versions of all date functions, enabling functional composition and pipeline-style operations. All FP functions automatically reorder parameters for optimal currying and include full TypeScript support.
|
||||
|
||||
## FP Module Overview
|
||||
|
||||
### Import Structure
|
||||
|
||||
```typescript
|
||||
// Import individual curried functions
|
||||
import { add, format, isAfter } from "date-fns/fp";
|
||||
|
||||
// Import with namespace
|
||||
import * as fp from "date-fns/fp";
|
||||
```
|
||||
|
||||
### Currying Pattern
|
||||
|
||||
All FP functions follow a consistent currying pattern where the data (typically the date) comes last, enabling partial application and composition:
|
||||
|
||||
```typescript
|
||||
// Regular date-fns: data first
|
||||
format(date, 'yyyy-MM-dd')
|
||||
|
||||
// FP version: data last
|
||||
format('yyyy-MM-dd')(date)
|
||||
```
|
||||
|
||||
## Core FP Functions
|
||||
|
||||
### Arithmetic Functions
|
||||
|
||||
```typescript { .api }
|
||||
const add: CurriedFn2<Duration, DateArg<Date>, Date>;
|
||||
const addDays: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const addHours: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const addMinutes: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const addMonths: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const addYears: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const sub: CurriedFn2<Duration, DateArg<Date>, Date>;
|
||||
const subDays: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const subHours: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const subMinutes: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const subMonths: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
const subYears: CurriedFn2<number, DateArg<Date>, Date>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { add, addDays, subMonths } from "date-fns/fp";
|
||||
|
||||
// Create reusable functions
|
||||
const addOneWeek = add({ weeks: 1 });
|
||||
const addFiveDays = addDays(5);
|
||||
const subtractTwoMonths = subMonths(2);
|
||||
|
||||
// Apply to dates
|
||||
const dates = [
|
||||
new Date(2014, 0, 1),
|
||||
new Date(2014, 0, 15),
|
||||
new Date(2014, 0, 30)
|
||||
];
|
||||
|
||||
const datesPlus5 = dates.map(addFiveDays);
|
||||
const weekFromNow = addOneWeek(new Date());
|
||||
```
|
||||
|
||||
### Formatting Functions
|
||||
|
||||
```typescript { .api }
|
||||
const format: CurriedFn2<string, DateArg<Date>, string>;
|
||||
const formatDistance: CurriedFn2<DateArg<Date>, DateArg<Date>, string>;
|
||||
const formatDistanceToNow: CurriedFn1<DateArg<Date>, string>;
|
||||
const formatISO: CurriedFn1<DateArg<Date>, string>;
|
||||
const formatRelative: CurriedFn2<DateArg<Date>, DateArg<Date>, string>;
|
||||
const lightFormat: CurriedFn2<string, DateArg<Date>, string>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { format, formatDistance, formatISO } from "date-fns/fp";
|
||||
|
||||
// Create format functions
|
||||
const formatYMD = format('yyyy-MM-dd');
|
||||
const formatLong = format('EEEE, MMMM do, yyyy');
|
||||
const toISO = formatISO;
|
||||
|
||||
// Use in pipelines
|
||||
const dates = [new Date(2014, 0, 1), new Date(2014, 5, 15)];
|
||||
const formatted = dates.map(formatYMD);
|
||||
//=> ['2014-01-01', '2014-06-15']
|
||||
|
||||
// Distance from specific date
|
||||
const distanceFromNewYear = formatDistance(new Date(2014, 0, 1));
|
||||
distanceFromNewYear(new Date(2014, 5, 15));
|
||||
//=> '5 months'
|
||||
```
|
||||
|
||||
### Comparison Functions
|
||||
|
||||
```typescript { .api }
|
||||
const compareAsc: CurriedFn2<DateArg<Date>, DateArg<Date>, number>;
|
||||
const compareDesc: CurriedFn2<DateArg<Date>, DateArg<Date>, number>;
|
||||
const isAfter: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isBefore: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isEqual: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const max: CurriedFn1<DateArg<Date>[], Date>;
|
||||
const min: CurriedFn1<DateArg<Date>[], Date>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isAfter, isBefore, compareAsc } from "date-fns/fp";
|
||||
|
||||
// Create comparison functions
|
||||
const isAfter2020 = isAfter(new Date(2020, 0, 1));
|
||||
const isBefore2025 = isBefore(new Date(2025, 0, 1));
|
||||
|
||||
// Filter dates
|
||||
const dates = [
|
||||
new Date(2019, 0, 1),
|
||||
new Date(2021, 0, 1),
|
||||
new Date(2026, 0, 1)
|
||||
];
|
||||
|
||||
const recentDates = dates.filter(isAfter2020).filter(isBefore2025);
|
||||
//=> [new Date(2021, 0, 1)]
|
||||
|
||||
// Sort dates
|
||||
const sortedDates = dates.sort(compareAsc);
|
||||
```
|
||||
|
||||
### Validation Functions
|
||||
|
||||
```typescript { .api }
|
||||
const isValid: CurriedFn1<any, boolean>;
|
||||
const isDate: CurriedFn1<any, boolean>;
|
||||
const isFuture: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isPast: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isToday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isWeekend: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isMonday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isTuesday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isWednesday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isThursday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isFriday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isSaturday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
const isSunday: CurriedFn1<DateArg<Date>, boolean>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isValid, isWeekend, isFuture } from "date-fns/fp";
|
||||
|
||||
// Filter valid dates
|
||||
const mixedInput = [
|
||||
new Date(2014, 0, 1),
|
||||
'invalid-date',
|
||||
new Date(2014, 0, 2),
|
||||
null
|
||||
];
|
||||
|
||||
const validDates = mixedInput.filter(isValid);
|
||||
//=> [new Date(2014, 0, 1), new Date(2014, 0, 2)]
|
||||
|
||||
// Chain validations
|
||||
const dates = [/* array of dates */];
|
||||
const futureWeekends = dates.filter(isFuture).filter(isWeekend);
|
||||
```
|
||||
|
||||
### Same Period Functions
|
||||
|
||||
```typescript { .api }
|
||||
const isSameDay: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameWeek: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameMonth: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameYear: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameHour: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameMinute: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
const isSameSecond: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isSameDay, isSameMonth } from "date-fns/fp";
|
||||
|
||||
// Find dates matching a reference
|
||||
const referenceDate = new Date(2014, 0, 15);
|
||||
const dates = [/* array of dates */];
|
||||
|
||||
const sameDayDates = dates.filter(isSameDay(referenceDate));
|
||||
const sameMonthDates = dates.filter(isSameMonth(referenceDate));
|
||||
```
|
||||
|
||||
## Functional Composition
|
||||
|
||||
### Function Composition with Ramda
|
||||
|
||||
```typescript
|
||||
import { pipe, compose } from "ramda";
|
||||
import { format, addDays, startOfDay } from "date-fns/fp";
|
||||
|
||||
// Create a pipeline
|
||||
const formatTomorrowStart = pipe(
|
||||
addDays(1),
|
||||
startOfDay,
|
||||
format('yyyy-MM-dd HH:mm:ss')
|
||||
);
|
||||
|
||||
formatTomorrowStart(new Date(2014, 0, 1, 14, 30));
|
||||
//=> '2014-01-02 00:00:00'
|
||||
|
||||
// Compose operations (right to left)
|
||||
const formatNextWeekEnd = compose(
|
||||
format('PP'),
|
||||
endOfDay,
|
||||
addDays(7)
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Composition Helpers
|
||||
|
||||
```typescript
|
||||
import { addDays, format, startOfWeek, endOfWeek } from "date-fns/fp";
|
||||
|
||||
// Create utility functions
|
||||
const pipe = <T>(...fns: Array<(arg: T) => T>) => (value: T) =>
|
||||
fns.reduce((acc, fn) => fn(acc), value);
|
||||
|
||||
const formatWeekRange = (date: Date) => {
|
||||
const start = startOfWeek(date);
|
||||
const end = endOfWeek(date);
|
||||
const formatDate = format('MMM dd');
|
||||
return `${formatDate(start)} - ${formatDate(end)}`;
|
||||
};
|
||||
|
||||
// Pipeline for date processing
|
||||
const processDate = pipe(
|
||||
startOfDay,
|
||||
addDays(1),
|
||||
format('yyyy-MM-dd')
|
||||
);
|
||||
```
|
||||
|
||||
## Advanced FP Patterns
|
||||
|
||||
### Partial Application
|
||||
|
||||
```typescript
|
||||
import { formatDistance, isWithinInterval } from "date-fns/fp";
|
||||
|
||||
// Create partially applied functions
|
||||
const distanceFromToday = formatDistance(new Date());
|
||||
const isInCurrentYear = isWithinInterval({
|
||||
start: startOfYear(new Date()),
|
||||
end: endOfYear(new Date())
|
||||
});
|
||||
|
||||
// Use with arrays
|
||||
const dates = [/* dates */];
|
||||
const distances = dates.map(distanceFromToday);
|
||||
const currentYearDates = dates.filter(isInCurrentYear);
|
||||
```
|
||||
|
||||
### Function Factories
|
||||
|
||||
```typescript
|
||||
import { add, format, isBefore } from "date-fns/fp";
|
||||
|
||||
// Factory for creating date ranges
|
||||
const createDateRange = (duration: Duration) => (start: Date): Date[] => {
|
||||
const end = add(duration)(start);
|
||||
return eachDayOfInterval({ start, end });
|
||||
};
|
||||
|
||||
// Factory for deadline checkers
|
||||
const createDeadlineChecker = (deadline: Date) => (date: Date): {
|
||||
passed: boolean;
|
||||
remaining: string;
|
||||
} => ({
|
||||
passed: isAfter(deadline)(date),
|
||||
remaining: formatDistance(deadline)(date)
|
||||
});
|
||||
|
||||
// Usage
|
||||
const getWeekRange = createDateRange({ weeks: 1 });
|
||||
const checkProjectDeadline = createDeadlineChecker(new Date(2024, 11, 31));
|
||||
|
||||
const weekDates = getWeekRange(new Date());
|
||||
const status = checkProjectDeadline(new Date());
|
||||
```
|
||||
|
||||
### Data Transformation Pipelines
|
||||
|
||||
```typescript
|
||||
import { flow } from "lodash/fp";
|
||||
import { parse, format, addBusinessDays, isValid } from "date-fns/fp";
|
||||
|
||||
// Process CSV date data
|
||||
const processDateString = flow(
|
||||
parse('yyyy-MM-dd', new Date()), // Parse with reference date
|
||||
date => isValid(date) ? date : null, // Validate
|
||||
date => date ? addBusinessDays(5)(date) : null, // Add business days if valid
|
||||
date => date ? format('MM/dd/yyyy')(date) : 'Invalid Date' // Format output
|
||||
);
|
||||
|
||||
const csvDates = ['2014-01-01', '2014-02-15', 'invalid-date'];
|
||||
const processed = csvDates.map(processDateString);
|
||||
//=> ['01/08/2014', '02/24/2014', 'Invalid Date']
|
||||
```
|
||||
|
||||
## Option Handling in FP
|
||||
|
||||
### Functions with Options
|
||||
|
||||
```typescript { .api }
|
||||
const formatWithOptions: CurriedFn3<FormatOptions, string, DateArg<Date>, string>;
|
||||
const parseWithOptions: CurriedFn4<ParseOptions, DateArg<Date>, string, string, Date>;
|
||||
const startOfWeekWithOptions: CurriedFn2<WeekStartOptions, DateArg<Date>, Date>;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { formatWithOptions, startOfWeekWithOptions } from "date-fns/fp";
|
||||
import { de } from "date-fns/locale";
|
||||
|
||||
// Create localized formatters
|
||||
const formatGerman = formatWithOptions({ locale: de });
|
||||
const formatGermanDate = formatGerman('EEEE, do MMMM yyyy');
|
||||
|
||||
formatGermanDate(new Date(2014, 0, 1));
|
||||
//=> 'Mittwoch, 1. Januar 2014'
|
||||
|
||||
// Week starting on Monday
|
||||
const startOfWeekMonday = startOfWeekWithOptions({ weekStartsOn: 1 });
|
||||
const dates = [/* dates */];
|
||||
const weekStarts = dates.map(startOfWeekMonday);
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memoization
|
||||
|
||||
```typescript
|
||||
import memoize from "lodash/memoize";
|
||||
import { format, formatDistance } from "date-fns/fp";
|
||||
|
||||
// Memoize expensive operations
|
||||
const memoizedFormat = memoize(format);
|
||||
const formatISO = memoizedFormat('yyyy-MM-dd');
|
||||
|
||||
// Memoize with custom key
|
||||
const memoizedDistance = memoize(
|
||||
formatDistance,
|
||||
(referenceDate) => referenceDate.getTime()
|
||||
);
|
||||
|
||||
const distanceFromEpoch = memoizedDistance(new Date(0));
|
||||
```
|
||||
|
||||
### Lazy Evaluation
|
||||
|
||||
```typescript
|
||||
import { addDays, format, isFuture } from "date-fns/fp";
|
||||
|
||||
// Create lazy sequences
|
||||
function* dateSequence(start: Date, step: number = 1) {
|
||||
let current = start;
|
||||
while (true) {
|
||||
yield current;
|
||||
current = addDays(step)(current);
|
||||
}
|
||||
}
|
||||
|
||||
// Use with functional operations
|
||||
const futureDates = Array.from(dateSequence(new Date()))
|
||||
.slice(0, 100) // Take first 100
|
||||
.filter(isFuture)
|
||||
.map(format('yyyy-MM-dd'))
|
||||
.slice(0, 10); // Take first 10 future dates
|
||||
```
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### Curried Function Types
|
||||
|
||||
```typescript { .api }
|
||||
interface CurriedFn1<A, R> {
|
||||
(a: A): R;
|
||||
}
|
||||
|
||||
interface CurriedFn2<A, B, R> {
|
||||
(a: A): CurriedFn1<B, R>;
|
||||
(a: A, b: B): R;
|
||||
}
|
||||
|
||||
interface CurriedFn3<A, B, C, R> {
|
||||
(a: A): CurriedFn2<B, C, R>;
|
||||
(a: A, b: B): CurriedFn1<C, R>;
|
||||
(a: A, b: B, c: C): R;
|
||||
}
|
||||
|
||||
interface CurriedFn4<A, B, C, D, R> {
|
||||
(a: A): CurriedFn3<B, C, D, R>;
|
||||
(a: A, b: B): CurriedFn2<C, D, R>;
|
||||
(a: A, b: B, c: C): CurriedFn1<D, R>;
|
||||
(a: A, b: B, c: C, d: D): R;
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### React Hooks
|
||||
|
||||
```typescript
|
||||
import { useMemo } from "react";
|
||||
import { format, addDays, isToday } from "date-fns/fp";
|
||||
|
||||
function useDateFormatter(pattern: string) {
|
||||
return useMemo(() => format(pattern), [pattern]);
|
||||
}
|
||||
|
||||
function useRelativeDates(dates: Date[]) {
|
||||
return useMemo(() => ({
|
||||
today: dates.filter(isToday),
|
||||
tomorrow: dates.filter(date => isSameDay(addDays(1)(new Date()))(date)),
|
||||
formatted: dates.map(format('MMM dd, yyyy'))
|
||||
}), [dates]);
|
||||
}
|
||||
```
|
||||
|
||||
### Redux Selectors
|
||||
|
||||
```typescript
|
||||
import { createSelector } from "reselect";
|
||||
import { isAfter, format, isSameMonth } from "date-fns/fp";
|
||||
|
||||
const getEvents = (state: State) => state.events;
|
||||
const getCurrentMonth = () => new Date();
|
||||
|
||||
const getUpcomingEvents = createSelector(
|
||||
getEvents,
|
||||
events => events
|
||||
.filter(event => isAfter(new Date())(event.date))
|
||||
.sort(compareAsc)
|
||||
);
|
||||
|
||||
const getCurrentMonthEvents = createSelector(
|
||||
getEvents,
|
||||
getCurrentMonth,
|
||||
(events, currentMonth) => events.filter(
|
||||
event => isSameMonth(currentMonth)(event.date)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Validation Schemas
|
||||
|
||||
```typescript
|
||||
import { z } from "zod";
|
||||
import { isValid, isAfter, isBefore } from "date-fns/fp";
|
||||
|
||||
const dateSchema = z.string()
|
||||
.transform(str => new Date(str))
|
||||
.refine(isValid, "Invalid date")
|
||||
.refine(isAfter(new Date(1900, 0, 1)), "Date must be after 1900")
|
||||
.refine(isBefore(new Date(2100, 0, 1)), "Date must be before 2100");
|
||||
|
||||
const eventSchema = z.object({
|
||||
title: z.string(),
|
||||
startDate: dateSchema,
|
||||
endDate: dateSchema
|
||||
}).refine(
|
||||
data => isBefore(data.endDate)(data.startDate),
|
||||
"End date must be after start date"
|
||||
);
|
||||
```
|
||||
552
.tessl/tiles/tessl/npm-date-fns/docs/i18n.md
Normal file
552
.tessl/tiles/tessl/npm-date-fns/docs/i18n.md
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
# Internationalization
|
||||
|
||||
The date-fns internationalization system provides comprehensive support for 97+ languages and regions with customizable date formatting, relative time display, and cultural date conventions. All locale functionality is modular and tree-shakeable.
|
||||
|
||||
## Locale System Overview
|
||||
|
||||
### Locale Interface
|
||||
|
||||
```typescript { .api }
|
||||
interface Locale {
|
||||
code: string;
|
||||
formatDistance?: FormatDistanceFn;
|
||||
formatLong?: FormatLongOptions;
|
||||
formatRelative?: FormatRelativeFn;
|
||||
localize?: LocalizeOptions;
|
||||
match?: MatchOptions;
|
||||
options?: LocaleOptions;
|
||||
}
|
||||
```
|
||||
|
||||
The locale object contains all language-specific formatting rules and translations needed for date operations in different languages and regions.
|
||||
|
||||
## Supported Locales
|
||||
|
||||
### Major Language Families
|
||||
|
||||
**English Variants**
|
||||
- `enUS` - English (United States) - Default
|
||||
- `enGB` - English (United Kingdom)
|
||||
- `enCA` - English (Canada)
|
||||
- `enAU` - English (Australia)
|
||||
- `enNZ` - English (New Zealand)
|
||||
- `enIN` - English (India)
|
||||
- `enIE` - English (Ireland)
|
||||
- `enZA` - English (South Africa)
|
||||
|
||||
**European Languages**
|
||||
- `de` - German (Germany)
|
||||
- `deAT` - German (Austria)
|
||||
- `fr` - French (France)
|
||||
- `frCA` - French (Canada)
|
||||
- `frCH` - French (Switzerland)
|
||||
- `es` - Spanish (Spain)
|
||||
- `it` - Italian (Italy)
|
||||
- `itCH` - Italian (Switzerland)
|
||||
- `pt` - Portuguese (Portugal)
|
||||
- `ptBR` - Portuguese (Brazil)
|
||||
- `nl` - Dutch (Netherlands)
|
||||
- `nlBE` - Dutch (Belgium)
|
||||
|
||||
**Nordic Languages**
|
||||
- `da` - Danish
|
||||
- `sv` - Swedish
|
||||
- `nb` - Norwegian Bokmål
|
||||
- `nn` - Norwegian Nynorsk
|
||||
- `fi` - Finnish
|
||||
- `is` - Icelandic
|
||||
|
||||
**Slavic Languages**
|
||||
- `ru` - Russian
|
||||
- `pl` - Polish
|
||||
- `cs` - Czech
|
||||
- `sk` - Slovak
|
||||
- `sl` - Slovenian
|
||||
- `hr` - Croatian
|
||||
- `sr` - Serbian (Cyrillic)
|
||||
- `srLatn` - Serbian (Latin)
|
||||
- `bg` - Bulgarian
|
||||
- `mk` - Macedonian
|
||||
- `be` - Belarusian
|
||||
- `beTarask` - Belarusian (Taraskievica)
|
||||
- `uk` - Ukrainian
|
||||
|
||||
**Asian Languages**
|
||||
- `zhCN` - Chinese (Simplified, China)
|
||||
- `zhHK` - Chinese (Traditional, Hong Kong)
|
||||
- `zhTW` - Chinese (Traditional, Taiwan)
|
||||
- `ja` - Japanese
|
||||
- `jaHira` - Japanese (Hiragana)
|
||||
- `ko` - Korean
|
||||
- `th` - Thai
|
||||
- `vi` - Vietnamese
|
||||
- `hi` - Hindi
|
||||
- `bn` - Bengali
|
||||
- `gu` - Gujarati
|
||||
- `kn` - Kannada
|
||||
- `ta` - Tamil
|
||||
- `te` - Telugu
|
||||
|
||||
**Middle Eastern & African**
|
||||
- `ar` - Arabic
|
||||
- `arDZ` - Arabic (Algeria)
|
||||
- `arEG` - Arabic (Egypt)
|
||||
- `arMA` - Arabic (Morocco)
|
||||
- `arSA` - Arabic (Saudi Arabia)
|
||||
- `arTN` - Arabic (Tunisia)
|
||||
- `he` - Hebrew
|
||||
- `faIR` - Persian (Iran)
|
||||
- `tr` - Turkish
|
||||
- `af` - Afrikaans
|
||||
|
||||
### Complete Locale List
|
||||
|
||||
```typescript
|
||||
// Import specific locales
|
||||
import { enUS, de, fr, ja, zhCN, ar } from "date-fns/locale";
|
||||
|
||||
// Available locales (97 total)
|
||||
const locales = [
|
||||
'af', 'ar', 'arDZ', 'arEG', 'arMA', 'arSA', 'arTN',
|
||||
'az', 'be', 'beTarask', 'bg', 'bn', 'bs', 'ca', 'ckb',
|
||||
'cs', 'cy', 'da', 'de', 'deAT', 'el', 'enAU', 'enCA',
|
||||
'enGB', 'enIE', 'enIN', 'enNZ', 'enUS', 'enZA', 'eo',
|
||||
'es', 'et', 'eu', 'faIR', 'fi', 'fr', 'frCA', 'frCH',
|
||||
'fy', 'gd', 'gl', 'gu', 'he', 'hi', 'hr', 'ht', 'hu',
|
||||
'hy', 'id', 'is', 'it', 'itCH', 'ja', 'jaHira', 'ka',
|
||||
'kk', 'km', 'kn', 'ko', 'lb', 'lt', 'lv', 'mk', 'mn',
|
||||
'ms', 'mt', 'nb', 'nl', 'nlBE', 'nn', 'oc', 'pl', 'pt',
|
||||
'ptBR', 'ro', 'ru', 'se', 'sk', 'sl', 'sq', 'sr', 'srLatn',
|
||||
'sv', 'ta', 'te', 'th', 'tr', 'ug', 'uk', 'uz', 'uzCyrl',
|
||||
'vi', 'zhCN', 'zhHK', 'zhTW'
|
||||
];
|
||||
```
|
||||
|
||||
## Using Locales
|
||||
|
||||
### Basic Locale Usage
|
||||
|
||||
```typescript
|
||||
import { format, formatDistance, formatRelative } from "date-fns";
|
||||
import { de, fr, ja, zhCN } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
// German
|
||||
format(date, "EEEE, do MMMM yyyy", { locale: de });
|
||||
//=> 'Dienstag, 11. Februar 2014'
|
||||
|
||||
// French
|
||||
format(date, "EEEE, do MMMM yyyy", { locale: fr });
|
||||
//=> 'mardi, 11e février 2014'
|
||||
|
||||
// Japanese
|
||||
format(date, "EEEE, do MMMM yyyy", { locale: ja });
|
||||
//=> '火曜日, 11日 2月 2014'
|
||||
|
||||
// Chinese (Simplified)
|
||||
format(date, "EEEE, do MMMM yyyy", { locale: zhCN });
|
||||
//=> '星期二, 11日 二月 2014'
|
||||
```
|
||||
|
||||
### Distance Formatting with Locales
|
||||
|
||||
```typescript
|
||||
import { formatDistance, formatDistanceToNow } from "date-fns";
|
||||
import { de, es, ru, ar } from "date-fns/locale";
|
||||
|
||||
const date1 = new Date(2014, 6, 2);
|
||||
const date2 = new Date(2015, 0, 1);
|
||||
|
||||
// German
|
||||
formatDistance(date2, date1, { locale: de });
|
||||
//=> 'etwa 6 Monate'
|
||||
|
||||
// Spanish
|
||||
formatDistance(date2, date1, { locale: es });
|
||||
//=> 'alrededor de 6 meses'
|
||||
|
||||
// Russian
|
||||
formatDistance(date2, date1, { locale: ru });
|
||||
//=> 'около 6 месяцев'
|
||||
|
||||
// Arabic
|
||||
formatDistance(date2, date1, { locale: ar });
|
||||
//=> 'حوالي 6 أشهر'
|
||||
```
|
||||
|
||||
### Relative Formatting with Locales
|
||||
|
||||
```typescript
|
||||
import { formatRelative } from "date-fns";
|
||||
import { de, fr, ja } from "date-fns/locale";
|
||||
|
||||
const baseDate = new Date(2000, 0, 1, 0, 0, 0); // Jan 1, 2000
|
||||
const date = new Date(1999, 11, 27); // Dec 27, 1999
|
||||
|
||||
// German
|
||||
formatRelative(date, baseDate, { locale: de });
|
||||
//=> 'letzten Montag um 00:00'
|
||||
|
||||
// French
|
||||
formatRelative(date, baseDate, { locale: fr });
|
||||
//=> 'lundi dernier à 00:00'
|
||||
|
||||
// Japanese
|
||||
formatRelative(date, baseDate, { locale: ja });
|
||||
//=> '先週の月曜日 0:00'
|
||||
```
|
||||
|
||||
## Locale Configuration Options
|
||||
|
||||
### Week Configuration
|
||||
|
||||
```typescript
|
||||
import { format, startOfWeek } from "date-fns";
|
||||
import { de, ar, enUS } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday
|
||||
|
||||
// Different week start days by locale
|
||||
startOfWeek(date, { locale: enUS }); // Sunday start
|
||||
//=> Sun Feb 09 2014
|
||||
|
||||
startOfWeek(date, { locale: de }); // Monday start
|
||||
//=> Mon Feb 10 2014
|
||||
|
||||
// Manual override
|
||||
startOfWeek(date, { locale: enUS, weekStartsOn: 1 }); // Force Monday start
|
||||
//=> Mon Feb 10 2014
|
||||
```
|
||||
|
||||
### First Week Configuration
|
||||
|
||||
```typescript
|
||||
import { getWeek } from "date-fns";
|
||||
import { de, enUS } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 0, 1); // Jan 1, 2014
|
||||
|
||||
// Different first week rules
|
||||
getWeek(date, { locale: enUS });
|
||||
//=> 1 (US: week containing Jan 1 is week 1)
|
||||
|
||||
getWeek(date, { locale: de });
|
||||
//=> 1 (German: week with 4+ days in new year is week 1)
|
||||
```
|
||||
|
||||
## Locale-Specific Patterns
|
||||
|
||||
### Date Patterns by Locale
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
import { enUS, de, ja, ar } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
// US format (M/d/yyyy)
|
||||
format(date, "P", { locale: enUS });
|
||||
//=> '2/11/2014'
|
||||
|
||||
// German format (dd.MM.yyyy)
|
||||
format(date, "P", { locale: de });
|
||||
//=> '11.02.2014'
|
||||
|
||||
// Japanese format (yyyy/MM/dd)
|
||||
format(date, "P", { locale: ja });
|
||||
//=> '2014/02/11'
|
||||
|
||||
// Arabic format (d/M/yyyy)
|
||||
format(date, "P", { locale: ar });
|
||||
//=> '11/2/2014'
|
||||
```
|
||||
|
||||
### Time Patterns by Locale
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
import { enUS, de, ja } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11, 14, 30);
|
||||
|
||||
// US format (2:30 PM)
|
||||
format(date, "p", { locale: enUS });
|
||||
//=> '2:30 PM'
|
||||
|
||||
// German format (14:30)
|
||||
format(date, "p", { locale: de });
|
||||
//=> '14:30'
|
||||
|
||||
// Japanese format (14:30)
|
||||
format(date, "p", { locale: ja });
|
||||
//=> '14:30'
|
||||
```
|
||||
|
||||
### Long Format Patterns
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
import { enUS, de, fr, ja } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11, 14, 30, 45);
|
||||
|
||||
// Full date and time formats
|
||||
format(date, "PPPPpppp", { locale: enUS });
|
||||
//=> 'Tuesday, February 11th, 2014 at 2:30:45 PM GMT+2'
|
||||
|
||||
format(date, "PPPPpppp", { locale: de });
|
||||
//=> 'Dienstag, 11. Februar 2014 um 14:30:45 GMT+2'
|
||||
|
||||
format(date, "PPPPpppp", { locale: fr });
|
||||
//=> 'mardi 11 février 2014 à 14:30:45 GMT+2'
|
||||
|
||||
format(date, "PPPPpppp", { locale: ja });
|
||||
//=> '2014年2月11日火曜日 14:30:45 GMT+2'
|
||||
```
|
||||
|
||||
## Creating Custom Locales
|
||||
|
||||
### Locale Structure
|
||||
|
||||
```typescript { .api }
|
||||
interface Locale {
|
||||
code: string;
|
||||
formatDistance: FormatDistanceFn;
|
||||
formatLong: FormatLongOptions;
|
||||
formatRelative: FormatRelativeFn;
|
||||
localize: LocalizeOptions;
|
||||
match: MatchOptions;
|
||||
options?: LocaleOptions;
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Locale Example
|
||||
|
||||
```typescript
|
||||
import { Locale } from "date-fns";
|
||||
|
||||
const customLocale: Locale = {
|
||||
code: 'custom',
|
||||
|
||||
formatDistance: (token, count, options) => {
|
||||
// Custom distance formatting logic
|
||||
const formatDistanceLocale = {
|
||||
lessThanXSeconds: 'less than {{count}} second{{s}}',
|
||||
xSeconds: '{{count}} second{{s}}',
|
||||
halfAMinute: 'half a minute',
|
||||
lessThanXMinutes: 'less than {{count}} minute{{s}}',
|
||||
xMinutes: '{{count}} minute{{s}}',
|
||||
aboutXHours: 'about {{count}} hour{{s}}',
|
||||
xHours: '{{count}} hour{{s}}',
|
||||
xDays: '{{count}} day{{s}}',
|
||||
aboutXWeeks: 'about {{count}} week{{s}}',
|
||||
xWeeks: '{{count}} week{{s}}',
|
||||
aboutXMonths: 'about {{count}} month{{s}}',
|
||||
xMonths: '{{count}} month{{s}}',
|
||||
aboutXYears: 'about {{count}} year{{s}}',
|
||||
xYears: '{{count}} year{{s}}',
|
||||
overXYears: 'over {{count}} year{{s}}',
|
||||
almostXYears: 'almost {{count}} year{{s}}'
|
||||
};
|
||||
|
||||
const result = formatDistanceLocale[token].replace(
|
||||
'{{count}}',
|
||||
count.toString()
|
||||
);
|
||||
|
||||
return result.replace('{{s}}', count === 1 ? '' : 's');
|
||||
},
|
||||
|
||||
formatLong: {
|
||||
time: 'HH:mm:ss',
|
||||
date: 'dd/MM/yyyy',
|
||||
dateTime: '{{date}} {{time}}',
|
||||
longDate: 'MMMM d, yyyy',
|
||||
fullDate: 'EEEE, MMMM do, yyyy',
|
||||
shortTime: 'HH:mm',
|
||||
longTime: 'HH:mm:ss',
|
||||
fullTime: 'HH:mm:ss'
|
||||
},
|
||||
|
||||
formatRelative: (token, date, baseDate, options) => {
|
||||
// Custom relative formatting
|
||||
const formatRelativeLocale = {
|
||||
lastWeek: "'last' eeee 'at' p",
|
||||
yesterday: "'yesterday at' p",
|
||||
today: "'today at' p",
|
||||
tomorrow: "'tomorrow at' p",
|
||||
nextWeek: "eeee 'at' p",
|
||||
other: 'P'
|
||||
};
|
||||
|
||||
return formatRelativeLocale[token];
|
||||
},
|
||||
|
||||
localize: {
|
||||
// Month names, day names, etc.
|
||||
month: (month) => ['Jan', 'Feb', 'Mar', /*...*/][month],
|
||||
day: (day) => ['Sun', 'Mon', 'Tue', /*...*/][day],
|
||||
// Additional localization functions...
|
||||
},
|
||||
|
||||
match: {
|
||||
// Parsing patterns for custom locale
|
||||
// Match functions for parsing formatted dates
|
||||
},
|
||||
|
||||
options: {
|
||||
weekStartsOn: 1, // Monday
|
||||
firstWeekContainsDate: 4
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Using Custom Locales
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
|
||||
// Use the custom locale
|
||||
format(new Date(), "PPP", { locale: customLocale });
|
||||
```
|
||||
|
||||
## Locale Helper Functions
|
||||
|
||||
### Locale Detection
|
||||
|
||||
```typescript
|
||||
// Browser locale detection
|
||||
function getBrowserLocale(): string {
|
||||
return navigator.language || navigator.languages[0] || 'en-US';
|
||||
}
|
||||
|
||||
// Map browser locale to date-fns locale
|
||||
function getDateFnsLocale(browserLocale: string): Locale {
|
||||
const localeMap: Record<string, () => Promise<Locale>> = {
|
||||
'de': () => import('date-fns/locale/de'),
|
||||
'de-DE': () => import('date-fns/locale/de'),
|
||||
'fr': () => import('date-fns/locale/fr'),
|
||||
'fr-FR': () => import('date-fns/locale/fr'),
|
||||
// Add more mappings...
|
||||
};
|
||||
|
||||
const loader = localeMap[browserLocale] ||
|
||||
localeMap[browserLocale.split('-')[0]] ||
|
||||
(() => import('date-fns/locale/en-US'));
|
||||
|
||||
return loader();
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic Locale Loading
|
||||
|
||||
```typescript
|
||||
// Lazy load locales
|
||||
class LocaleManager {
|
||||
private loadedLocales = new Map<string, Locale>();
|
||||
|
||||
async getLocale(code: string): Promise<Locale> {
|
||||
if (this.loadedLocales.has(code)) {
|
||||
return this.loadedLocales.get(code)!;
|
||||
}
|
||||
|
||||
try {
|
||||
const locale = await import(`date-fns/locale/${code}`);
|
||||
this.loadedLocales.set(code, locale.default);
|
||||
return locale.default;
|
||||
} catch {
|
||||
// Fallback to English
|
||||
const enUS = await import('date-fns/locale/en-US');
|
||||
return enUS.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Right-to-Left (RTL) Support
|
||||
|
||||
### Arabic and Hebrew Examples
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns";
|
||||
import { ar, he } from "date-fns/locale";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
// Arabic (RTL)
|
||||
format(date, "EEEE، do MMMM yyyy", { locale: ar });
|
||||
//=> 'الثلاثاء، 11 فبراير 2014'
|
||||
|
||||
// Hebrew (RTL)
|
||||
format(date, "EEEE, do MMMM yyyy", { locale: he });
|
||||
//=> 'יום שלישי, 11 פברואר 2014'
|
||||
```
|
||||
|
||||
### Handling RTL in Applications
|
||||
|
||||
```typescript
|
||||
import { ar, he, enUS } from "date-fns/locale";
|
||||
|
||||
function isRTLLocale(locale: Locale): boolean {
|
||||
const rtlCodes = ['ar', 'he', 'fa', 'ur'];
|
||||
return rtlCodes.includes(locale.code);
|
||||
}
|
||||
|
||||
function formatWithDirection(date: Date, pattern: string, locale: Locale): {
|
||||
text: string;
|
||||
direction: 'ltr' | 'rtl';
|
||||
} {
|
||||
return {
|
||||
text: format(date, pattern, { locale }),
|
||||
direction: isRTLLocale(locale) ? 'rtl' : 'ltr'
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### LocaleOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface LocaleOptions {
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatLongOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatLongOptions {
|
||||
time?: string;
|
||||
date?: string;
|
||||
dateTime?: string;
|
||||
longDate?: string;
|
||||
fullDate?: string;
|
||||
shortTime?: string;
|
||||
longTime?: string;
|
||||
fullTime?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### FormatDistanceFn
|
||||
|
||||
```typescript { .api }
|
||||
type FormatDistanceFn = (
|
||||
token: string,
|
||||
count: number,
|
||||
options?: { addSuffix?: boolean; comparison?: number }
|
||||
) => string;
|
||||
```
|
||||
|
||||
### FormatRelativeFn
|
||||
|
||||
```typescript { .api }
|
||||
type FormatRelativeFn = (
|
||||
token: 'lastWeek' | 'yesterday' | 'today' | 'tomorrow' | 'nextWeek' | 'other',
|
||||
date: Date,
|
||||
baseDate: Date,
|
||||
options?: any
|
||||
) => string;
|
||||
```
|
||||
267
.tessl/tiles/tessl/npm-date-fns/docs/index.md
Normal file
267
.tessl/tiles/tessl/npm-date-fns/docs/index.md
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
# date-fns
|
||||
|
||||
date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in both browser and Node.js environments. It offers over 200 modular functions for all date manipulation needs, supporting tree-shaking and selective imports to minimize bundle size.
|
||||
|
||||
## Package Information
|
||||
|
||||
- **Package Name**: date-fns
|
||||
- **Package Type**: npm
|
||||
- **Language**: JavaScript/TypeScript
|
||||
- **Installation**: `npm install date-fns`
|
||||
|
||||
## Core Imports
|
||||
|
||||
```typescript
|
||||
import { format, addDays, differenceInDays, isValid } from "date-fns";
|
||||
```
|
||||
|
||||
For CommonJS:
|
||||
|
||||
```javascript
|
||||
const { format, addDays, differenceInDays, isValid } = require("date-fns");
|
||||
```
|
||||
|
||||
Individual function imports (recommended for tree-shaking):
|
||||
|
||||
```typescript
|
||||
import { format } from "date-fns/format";
|
||||
import { addDays } from "date-fns/addDays";
|
||||
```
|
||||
|
||||
Constants import:
|
||||
|
||||
```typescript
|
||||
import { maxTime, minTime } from "date-fns/constants";
|
||||
```
|
||||
|
||||
Locale imports:
|
||||
|
||||
```typescript
|
||||
import { enUS, de, fr } from "date-fns/locale";
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import { compareAsc, format } from "date-fns";
|
||||
|
||||
// Format dates
|
||||
format(new Date(2014, 1, 11), "yyyy-MM-dd");
|
||||
//=> '2014-02-11'
|
||||
|
||||
// Add days and format
|
||||
const futureDate = addDays(new Date(2023, 0, 1), 10);
|
||||
format(futureDate, "PP");
|
||||
//=> 'Jan 11, 2023'
|
||||
|
||||
// Compare dates
|
||||
const dates = [
|
||||
new Date(1995, 6, 2),
|
||||
new Date(1987, 1, 11),
|
||||
new Date(1989, 6, 10),
|
||||
];
|
||||
dates.sort(compareAsc);
|
||||
//=> Sorted chronologically
|
||||
|
||||
// Validate dates
|
||||
isValid(new Date("2023-13-01")); //=> false
|
||||
isValid(new Date("2023-01-01")); //=> true
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
date-fns is built around several key principles:
|
||||
|
||||
- **Immutable & Pure**: All functions are pure and return new date instances without modifying inputs
|
||||
- **Modular Design**: Each function is a separate module enabling tree-shaking and selective imports
|
||||
- **TypeScript First**: Complete type definitions with generic support for custom date types
|
||||
- **No Extensions**: Uses native Date objects without extending prototypes for maximum compatibility
|
||||
- **Functional Programming**: Optional FP module with curried interfaces for functional composition
|
||||
- **Internationalization**: Comprehensive locale system supporting 97+ languages and regions
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Date Arithmetic
|
||||
|
||||
Core date manipulation functions for adding, subtracting, and calculating differences between dates. Essential for date calculations and scheduling applications.
|
||||
|
||||
```typescript { .api }
|
||||
function add(date: DateArg<DateType>, duration: Duration): DateType;
|
||||
function addDays(date: DateArg<DateType>, amount: number): DateType;
|
||||
function differenceInDays(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): number;
|
||||
function sub(date: DateArg<DateType>, duration: Duration): DateType;
|
||||
```
|
||||
|
||||
[Date Arithmetic](./arithmetic.md)
|
||||
|
||||
### Date Formatting
|
||||
|
||||
Comprehensive date formatting with customizable patterns, internationalization support, and multiple output formats including ISO, RFC, and relative time formatting.
|
||||
|
||||
```typescript { .api }
|
||||
function format(date: DateArg<Date>, formatStr: string, options?: FormatOptions): string;
|
||||
function formatDistance(dateLeft: DateArg<Date>, dateRight: DateArg<Date>, options?: FormatDistanceOptions): string;
|
||||
function formatISO(date: DateArg<Date>, options?: FormatISOOptions): string;
|
||||
function formatRelative(date: DateArg<Date>, baseDate: DateArg<Date>, options?: FormatRelativeOptions): string;
|
||||
```
|
||||
|
||||
[Date Formatting](./formatting.md)
|
||||
|
||||
### Date Parsing
|
||||
|
||||
Flexible date parsing from strings, ISO formats, and custom patterns with proper error handling and validation support.
|
||||
|
||||
```typescript { .api }
|
||||
function parse(dateString: string, formatString: string, referenceDate: DateArg<Date>, options?: ParseOptions): Date;
|
||||
function parseISO(argument: string, options?: ParseOptions): Date;
|
||||
function parseJSON(argument: string | number | Date): Date;
|
||||
function isMatch(dateString: string, formatString: string, options?: MatchOptions): boolean;
|
||||
```
|
||||
|
||||
[Date Parsing](./parsing.md)
|
||||
|
||||
### Date Validation and Comparison
|
||||
|
||||
Comprehensive validation and comparison utilities for checking date validity, temporal relationships, and period-based comparisons.
|
||||
|
||||
```typescript { .api }
|
||||
function isValid(date: any): boolean;
|
||||
function isAfter(date: DateArg<Date>, dateToCompare: DateArg<Date>): boolean;
|
||||
function isBefore(date: DateArg<Date>, dateToCompare: DateArg<Date>): boolean;
|
||||
function isEqual(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function compareAsc(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
[Date Validation](./validation.md)
|
||||
|
||||
### Date Component Access
|
||||
|
||||
Functions for getting and setting individual date components (year, month, day, hour, etc.) with proper handling of time zones and edge cases.
|
||||
|
||||
```typescript { .api }
|
||||
function getYear(date: DateArg<Date>): number;
|
||||
function getMonth(date: DateArg<Date>): number;
|
||||
function getDate(date: DateArg<Date>): number;
|
||||
function setYear<DateType extends Date>(date: DateArg<DateType>, year: number): DateType;
|
||||
function setMonth<DateType extends Date>(date: DateArg<DateType>, month: number): DateType;
|
||||
```
|
||||
|
||||
[Date Components](./components.md)
|
||||
|
||||
### Time Period Utilities
|
||||
|
||||
Functions for working with specific time periods like start/end of day, week, month, and iteration over date ranges.
|
||||
|
||||
```typescript { .api }
|
||||
function startOfDay<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfMonth<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function eachDayOfInterval(interval: Interval, options?: StepOptions): Date[];
|
||||
function lastDayOfMonth<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
[Time Periods](./periods.md)
|
||||
|
||||
### Internationalization
|
||||
|
||||
Complete locale system supporting 97+ languages and regions with customizable date formatting, relative time display, and cultural date conventions.
|
||||
|
||||
```typescript { .api }
|
||||
interface Locale {
|
||||
code: string;
|
||||
formatDistance: FormatDistanceFn;
|
||||
formatLong: FormatLongOptions;
|
||||
formatRelative: FormatRelativeFn;
|
||||
localize: LocalizeOptions;
|
||||
match: MatchOptions;
|
||||
options?: LocaleOptions;
|
||||
}
|
||||
```
|
||||
|
||||
[Internationalization](./i18n.md)
|
||||
|
||||
### Constants and Utilities
|
||||
|
||||
Mathematical constants for time calculations, utility functions for date construction, and helper functions for common operations.
|
||||
|
||||
```typescript { .api }
|
||||
const daysInWeek = 7;
|
||||
const millisecondsInDay = 86400000;
|
||||
const maxTime = 8640000000000000;
|
||||
function toDate(argument: DateArg<Date>): Date;
|
||||
function isDate(value: any): value is Date;
|
||||
```
|
||||
|
||||
[Constants and Utilities](./constants.md)
|
||||
|
||||
### Functional Programming
|
||||
|
||||
Curried function interfaces for functional composition and pipeline-style date operations, with automatic parameter reordering for optimal usage.
|
||||
|
||||
```typescript { .api }
|
||||
const add: CurriedFn2<Duration, DateArg<Date>, Date>;
|
||||
const format: CurriedFn2<string, DateArg<Date>, string>;
|
||||
const isAfter: CurriedFn2<DateArg<Date>, DateArg<Date>, boolean>;
|
||||
```
|
||||
|
||||
[Functional Programming](./fp.md)
|
||||
|
||||
## Types
|
||||
|
||||
### Core Types
|
||||
|
||||
```typescript { .api }
|
||||
type DateArg<DateType extends Date> = DateType | number | string;
|
||||
|
||||
interface Duration {
|
||||
years?: number;
|
||||
months?: number;
|
||||
weeks?: number;
|
||||
days?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
}
|
||||
|
||||
interface Interval {
|
||||
start: DateArg<Date>;
|
||||
end: DateArg<Date>;
|
||||
}
|
||||
|
||||
interface GenericDateConstructor<DateType extends Date = Date> {
|
||||
new (): DateType;
|
||||
new (value: DateArg<Date> & {}): DateType;
|
||||
new (
|
||||
year: number,
|
||||
month: number,
|
||||
date?: number,
|
||||
hours?: number,
|
||||
minutes?: number,
|
||||
seconds?: number,
|
||||
ms?: number,
|
||||
): DateType;
|
||||
}
|
||||
```
|
||||
|
||||
### Options Interfaces
|
||||
|
||||
```typescript { .api }
|
||||
interface FormatOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
|
||||
interface ParseOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
|
||||
interface StepOptions {
|
||||
step?: number;
|
||||
}
|
||||
```
|
||||
381
.tessl/tiles/tessl/npm-date-fns/docs/parsing.md
Normal file
381
.tessl/tiles/tessl/npm-date-fns/docs/parsing.md
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
# Date Parsing
|
||||
|
||||
Date parsing functions provide flexible conversion from strings to Date objects with support for various formats, ISO standards, and custom patterns. All parsing functions include proper error handling and validation.
|
||||
|
||||
## Core Parsing
|
||||
|
||||
### parse
|
||||
|
||||
Parse a date from a string using a format pattern.
|
||||
|
||||
```typescript { .api }
|
||||
function parse(
|
||||
dateString: string,
|
||||
formatString: string,
|
||||
referenceDate: DateArg<Date>,
|
||||
options?: ParseOptions
|
||||
): Date;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dateString` - The string to parse
|
||||
- `formatString` - The format pattern to match
|
||||
- `referenceDate` - Reference date for relative parsing
|
||||
- `options` - Optional parsing configuration
|
||||
|
||||
**Format Tokens:**
|
||||
- `yyyy` - 4-digit year
|
||||
- `MM` - 2-digit month (01-12)
|
||||
- `dd` - 2-digit day (01-31)
|
||||
- `HH` - 24-hour format hour (00-23)
|
||||
- `mm` - 2-digit minute (00-59)
|
||||
- `ss` - 2-digit second (00-59)
|
||||
- `SSS` - Milliseconds (000-999)
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { parse } from "date-fns";
|
||||
|
||||
// Basic date parsing
|
||||
parse('2014-02-11', 'yyyy-MM-dd', new Date());
|
||||
//=> Tue Feb 11 2014 00:00:00
|
||||
|
||||
// Date and time
|
||||
parse('2014-02-11 14:30:45', 'yyyy-MM-dd HH:mm:ss', new Date());
|
||||
//=> Tue Feb 11 2014 14:30:45
|
||||
|
||||
// Custom format
|
||||
parse('11.02.2014', 'dd.MM.yyyy', new Date());
|
||||
//=> Tue Feb 11 2014 00:00:00
|
||||
|
||||
// With milliseconds
|
||||
parse('2014-02-11T14:30:45.123', 'yyyy-MM-ddTHH:mm:ss.SSS', new Date());
|
||||
//=> Tue Feb 11 2014 14:30:45.123
|
||||
```
|
||||
|
||||
### parseISO
|
||||
|
||||
Parse an ISO 8601 date string.
|
||||
|
||||
```typescript { .api }
|
||||
function parseISO<ResultDate extends Date = Date>(
|
||||
argument: string,
|
||||
options?: ParseISOOptions<ResultDate>
|
||||
): ResultDate;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `argument` - The ISO 8601 string to parse
|
||||
- `options` - Optional parsing configuration with additional digits support
|
||||
|
||||
**Supported Formats:**
|
||||
- `YYYY-MM-DD` - Calendar date
|
||||
- `YYYY-MM-DDTHH:mm:ss` - Complete date and time
|
||||
- `YYYY-MM-DDTHH:mm:ss.sss` - With milliseconds
|
||||
- `YYYY-MM-DDTHH:mm:ssZ` - UTC time
|
||||
- `YYYY-MM-DDTHH:mm:ss+HH:mm` - With timezone offset
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { parseISO } from "date-fns";
|
||||
|
||||
// Date only
|
||||
parseISO('2014-02-11');
|
||||
//=> Tue Feb 11 2014 00:00:00
|
||||
|
||||
// Complete datetime
|
||||
parseISO('2014-02-11T11:30:30');
|
||||
//=> Tue Feb 11 2014 11:30:30
|
||||
|
||||
// With timezone
|
||||
parseISO('2014-02-11T11:30:30+05:00');
|
||||
//=> Tue Feb 11 2014 11:30:30
|
||||
|
||||
// UTC time
|
||||
parseISO('2014-02-11T11:30:30Z');
|
||||
//=> Tue Feb 11 2014 11:30:30
|
||||
|
||||
// With milliseconds
|
||||
parseISO('2014-02-11T11:30:30.123Z');
|
||||
//=> Tue Feb 11 2014 11:30:30.123
|
||||
```
|
||||
|
||||
### parseJSON
|
||||
|
||||
Parse a date from JSON (handles various JSON date formats).
|
||||
|
||||
```typescript { .api }
|
||||
function parseJSON<ResultDate extends Date = Date>(
|
||||
dateStr: string,
|
||||
options?: ParseJSONOptions<ResultDate>
|
||||
): ResultDate;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dateStr` - The JSON date string to parse
|
||||
- `options` - Optional parsing configuration with context support
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { parseJSON } from "date-fns";
|
||||
|
||||
// ISO string from JSON
|
||||
parseJSON('2014-02-11T11:30:30.000Z');
|
||||
//=> Tue Feb 11 2014 11:30:30
|
||||
|
||||
// Unix timestamp
|
||||
parseJSON(1392123030000);
|
||||
//=> Tue Feb 11 2014 11:30:30
|
||||
|
||||
// Already a Date object
|
||||
parseJSON(new Date(2014, 1, 11));
|
||||
//=> Tue Feb 11 2014 00:00:00
|
||||
```
|
||||
|
||||
## Validation and Matching
|
||||
|
||||
### isMatch
|
||||
|
||||
Check if a string matches a date format pattern.
|
||||
|
||||
```typescript { .api }
|
||||
function isMatch(
|
||||
dateString: string,
|
||||
formatString: string,
|
||||
options?: MatchOptions
|
||||
): boolean;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dateString` - The string to test
|
||||
- `formatString` - The format pattern to match against
|
||||
- `options` - Optional matching configuration
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isMatch } from "date-fns";
|
||||
|
||||
// Valid formats
|
||||
isMatch('2014-02-11', 'yyyy-MM-dd');
|
||||
//=> true
|
||||
|
||||
isMatch('11.02.2014', 'dd.MM.yyyy');
|
||||
//=> true
|
||||
|
||||
// Invalid formats
|
||||
isMatch('2014-02-11', 'dd.MM.yyyy');
|
||||
//=> false
|
||||
|
||||
isMatch('not-a-date', 'yyyy-MM-dd');
|
||||
//=> false
|
||||
|
||||
// Complex patterns
|
||||
isMatch('2014-02-11 14:30:45', 'yyyy-MM-dd HH:mm:ss');
|
||||
//=> true
|
||||
```
|
||||
|
||||
## Advanced Parsing Patterns
|
||||
|
||||
### Date Parts Parsing
|
||||
|
||||
Parse dates with various separators and formats:
|
||||
|
||||
```typescript
|
||||
import { parse } from "date-fns";
|
||||
|
||||
// Different separators
|
||||
parse('2014/02/11', 'yyyy/MM/dd', new Date());
|
||||
parse('2014-02-11', 'yyyy-MM-dd', new Date());
|
||||
parse('2014.02.11', 'yyyy.MM.dd', new Date());
|
||||
|
||||
// Different order
|
||||
parse('11/02/2014', 'dd/MM/yyyy', new Date());
|
||||
parse('02/11/2014', 'MM/dd/yyyy', new Date());
|
||||
|
||||
// Short year
|
||||
parse('14-02-11', 'yy-MM-dd', new Date());
|
||||
```
|
||||
|
||||
### Time Parsing
|
||||
|
||||
Parse various time formats:
|
||||
|
||||
```typescript
|
||||
import { parse } from "date-fns";
|
||||
|
||||
// 24-hour format
|
||||
parse('14:30:45', 'HH:mm:ss', new Date());
|
||||
|
||||
// 12-hour format with AM/PM
|
||||
parse('2:30:45 PM', 'h:mm:ss a', new Date());
|
||||
|
||||
// Minutes and seconds only
|
||||
parse('30:45', 'mm:ss', new Date());
|
||||
|
||||
// With milliseconds
|
||||
parse('14:30:45.123', 'HH:mm:ss.SSS', new Date());
|
||||
```
|
||||
|
||||
### Relative Date Parsing
|
||||
|
||||
Use reference date for context-dependent parsing:
|
||||
|
||||
```typescript
|
||||
import { parse } from "date-fns";
|
||||
|
||||
const referenceDate = new Date(2020, 0, 1); // Jan 1, 2020
|
||||
|
||||
// Parse relative to reference year
|
||||
parse('02-11', 'MM-dd', referenceDate);
|
||||
//=> Feb 11, 2020
|
||||
|
||||
// Day of year
|
||||
parse('42', 'D', referenceDate);
|
||||
//=> Feb 11, 2020 (42nd day of 2020)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Date Detection
|
||||
|
||||
```typescript
|
||||
import { parse, isValid } from "date-fns";
|
||||
|
||||
// Parse potentially invalid date
|
||||
const result = parse('invalid-date', 'yyyy-MM-dd', new Date());
|
||||
|
||||
// Check if parsing was successful
|
||||
if (isValid(result)) {
|
||||
console.log('Parsed successfully:', result);
|
||||
} else {
|
||||
console.log('Parsing failed');
|
||||
}
|
||||
|
||||
// Parse with validation
|
||||
function safeParse(dateString: string, format: string): Date | null {
|
||||
try {
|
||||
const parsed = parse(dateString, format, new Date());
|
||||
return isValid(parsed) ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Parsing Pitfalls
|
||||
|
||||
```typescript
|
||||
import { parse, parseISO } from "date-fns";
|
||||
|
||||
// Ambiguous formats - be explicit
|
||||
parse('01/02/2014', 'MM/dd/yyyy', new Date()); // Jan 2, 2014
|
||||
parse('01/02/2014', 'dd/MM/yyyy', new Date()); // Feb 1, 2014
|
||||
|
||||
// Timezone handling
|
||||
parseISO('2014-02-11T11:30:30'); // Local time
|
||||
parseISO('2014-02-11T11:30:30Z'); // UTC time
|
||||
|
||||
// Invalid dates return Invalid Date
|
||||
parse('2014-13-01', 'yyyy-MM-dd', new Date()); // Invalid Date
|
||||
parse('2014-02-30', 'yyyy-MM-dd', new Date()); // Invalid Date
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### ParseOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface ParseOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### MatchOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface MatchOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### ParseISOOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface ParseISOOptions<DateType extends Date = Date> extends ContextOptions<DateType> {
|
||||
additionalDigits?: 0 | 1 | 2;
|
||||
}
|
||||
```
|
||||
|
||||
### ParseJSONOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface ParseJSONOptions<DateType extends Date = Date> extends ContextOptions<DateType> {}
|
||||
```
|
||||
|
||||
## Format Token Reference
|
||||
|
||||
### Date Tokens
|
||||
|
||||
| Token | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `yyyy` | 4-digit year | 2014 |
|
||||
| `yy` | 2-digit year | 14 |
|
||||
| `y` | Year | 2014 |
|
||||
| `YYYY` | ISO week year | 2014 |
|
||||
| `YY` | 2-digit ISO week year | 14 |
|
||||
|
||||
### Month Tokens
|
||||
|
||||
| Token | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `MMMM` | Full month name | February |
|
||||
| `MMM` | Short month name | Feb |
|
||||
| `MM` | 2-digit month | 02 |
|
||||
| `M` | Month | 2 |
|
||||
|
||||
### Day Tokens
|
||||
|
||||
| Token | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `dd` | 2-digit day | 11 |
|
||||
| `d` | Day | 11 |
|
||||
| `D` | Day of year | 42 |
|
||||
| `EEEE` | Full day name | Tuesday |
|
||||
| `EEE` | Short day name | Tue |
|
||||
| `e` | Local day of week | 2 |
|
||||
| `i` | ISO day of week | 2 |
|
||||
|
||||
### Time Tokens
|
||||
|
||||
| Token | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `HH` | 24-hour hour | 14 |
|
||||
| `H` | 24-hour hour | 14 |
|
||||
| `hh` | 12-hour hour | 02 |
|
||||
| `h` | 12-hour hour | 2 |
|
||||
| `mm` | 2-digit minute | 30 |
|
||||
| `m` | Minute | 30 |
|
||||
| `ss` | 2-digit second | 45 |
|
||||
| `s` | Second | 45 |
|
||||
| `SSS` | Millisecond | 123 |
|
||||
| `S` | 1/10 second | 1 |
|
||||
| `SS` | 1/100 second | 12 |
|
||||
|
||||
### AM/PM and Timezone
|
||||
|
||||
| Token | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `a` | AM/PM | PM |
|
||||
| `aa` | AM/PM | PM |
|
||||
| `aaa` | AM/PM | PM |
|
||||
| `X` | Timezone offset | +0200 |
|
||||
| `XX` | Timezone offset | +02:00 |
|
||||
| `XXX` | Timezone offset | +02:00 |
|
||||
612
.tessl/tiles/tessl/npm-date-fns/docs/periods.md
Normal file
612
.tessl/tiles/tessl/npm-date-fns/docs/periods.md
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
# Time Periods
|
||||
|
||||
Time period utilities provide functions for working with specific time periods like start/end of time units, iteration over date ranges, and navigation to specific dates. All functions handle edge cases and timezone considerations properly.
|
||||
|
||||
## Start of Period Functions
|
||||
|
||||
### Basic Time Units
|
||||
|
||||
```typescript { .api }
|
||||
function startOfSecond<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfMinute<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfHour<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfDay<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { startOfSecond, startOfMinute, startOfHour, startOfDay } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11, 14, 30, 45, 500);
|
||||
|
||||
startOfSecond(date);
|
||||
//=> Tue Feb 11 2014 14:30:45.000
|
||||
|
||||
startOfMinute(date);
|
||||
//=> Tue Feb 11 2014 14:30:00.000
|
||||
|
||||
startOfHour(date);
|
||||
//=> Tue Feb 11 2014 14:00:00.000
|
||||
|
||||
startOfDay(date);
|
||||
//=> Tue Feb 11 2014 00:00:00.000
|
||||
```
|
||||
|
||||
### Week and Month Periods
|
||||
|
||||
```typescript { .api }
|
||||
function startOfWeek<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: WeekStartOptions
|
||||
): DateType;
|
||||
function startOfISOWeek<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfMonth<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfQuarter<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { startOfWeek, startOfISOWeek, startOfMonth, startOfQuarter } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday, Feb 11, 2014
|
||||
|
||||
startOfWeek(date);
|
||||
//=> Sun Feb 09 2014 00:00:00 (week starts Sunday by default)
|
||||
|
||||
startOfWeek(date, { weekStartsOn: 1 });
|
||||
//=> Mon Feb 10 2014 00:00:00 (week starts Monday)
|
||||
|
||||
startOfISOWeek(date);
|
||||
//=> Mon Feb 10 2014 00:00:00 (ISO week always starts Monday)
|
||||
|
||||
startOfMonth(date);
|
||||
//=> Sat Feb 01 2014 00:00:00
|
||||
|
||||
startOfQuarter(date);
|
||||
//=> Wed Jan 01 2014 00:00:00 (Q1 starts in January)
|
||||
```
|
||||
|
||||
### Year and Decade Periods
|
||||
|
||||
```typescript { .api }
|
||||
function startOfYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfISOWeekYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function startOfWeekYear<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: WeekStartOptions
|
||||
): DateType;
|
||||
function startOfDecade<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { startOfYear, startOfISOWeekYear, startOfDecade } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
startOfYear(date);
|
||||
//=> Wed Jan 01 2014 00:00:00
|
||||
|
||||
startOfISOWeekYear(date);
|
||||
//=> Mon Dec 30 2013 00:00:00 (ISO week year 2014 starts in 2013)
|
||||
|
||||
startOfDecade(date);
|
||||
//=> Fri Jan 01 2010 00:00:00 (2010s decade)
|
||||
```
|
||||
|
||||
### Special Date Functions
|
||||
|
||||
```typescript { .api }
|
||||
function startOfToday(): Date;
|
||||
function startOfTomorrow(): Date;
|
||||
function startOfYesterday(): Date;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { startOfToday, startOfTomorrow, startOfYesterday } from "date-fns";
|
||||
|
||||
// These return dates relative to the current moment
|
||||
startOfToday(); //=> Today at 00:00:00
|
||||
startOfTomorrow(); //=> Tomorrow at 00:00:00
|
||||
startOfYesterday(); //=> Yesterday at 00:00:00
|
||||
```
|
||||
|
||||
## End of Period Functions
|
||||
|
||||
### Basic Time Units
|
||||
|
||||
```typescript { .api }
|
||||
function endOfSecond<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfMinute<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfHour<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfDay<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { endOfSecond, endOfMinute, endOfHour, endOfDay } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11, 14, 30, 45, 500);
|
||||
|
||||
endOfSecond(date);
|
||||
//=> Tue Feb 11 2014 14:30:45.999
|
||||
|
||||
endOfMinute(date);
|
||||
//=> Tue Feb 11 2014 14:30:59.999
|
||||
|
||||
endOfHour(date);
|
||||
//=> Tue Feb 11 2014 14:59:59.999
|
||||
|
||||
endOfDay(date);
|
||||
//=> Tue Feb 11 2014 23:59:59.999
|
||||
```
|
||||
|
||||
### Week and Month Periods
|
||||
|
||||
```typescript { .api }
|
||||
function endOfWeek<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: WeekStartOptions
|
||||
): DateType;
|
||||
function endOfISOWeek<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfMonth<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfQuarter<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { endOfWeek, endOfISOWeek, endOfMonth, endOfQuarter } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday, Feb 11, 2014
|
||||
|
||||
endOfWeek(date);
|
||||
//=> Sat Feb 15 2014 23:59:59.999
|
||||
|
||||
endOfISOWeek(date);
|
||||
//=> Sun Feb 16 2014 23:59:59.999
|
||||
|
||||
endOfMonth(date);
|
||||
//=> Fri Feb 28 2014 23:59:59.999
|
||||
|
||||
endOfQuarter(date);
|
||||
//=> Mon Mar 31 2014 23:59:59.999 (Q1 ends in March)
|
||||
```
|
||||
|
||||
### Year and Decade Periods
|
||||
|
||||
```typescript { .api }
|
||||
function endOfYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfISOWeekYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function endOfDecade<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { endOfYear, endOfISOWeekYear, endOfDecade } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11);
|
||||
|
||||
endOfYear(date);
|
||||
//=> Wed Dec 31 2014 23:59:59.999
|
||||
|
||||
endOfISOWeekYear(date);
|
||||
//=> Sun Dec 28 2014 23:59:59.999
|
||||
|
||||
endOfDecade(date);
|
||||
//=> Tue Dec 31 2019 23:59:59.999 (2010s decade ends in 2019)
|
||||
```
|
||||
|
||||
### Special Date Functions
|
||||
|
||||
```typescript { .api }
|
||||
function endOfToday(): Date;
|
||||
function endOfTomorrow(): Date;
|
||||
function endOfYesterday(): Date;
|
||||
```
|
||||
|
||||
## Last Day Functions
|
||||
|
||||
### Period Last Days
|
||||
|
||||
```typescript { .api }
|
||||
function lastDayOfMonth<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function lastDayOfQuarter<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: QuarterOptions
|
||||
): DateType;
|
||||
function lastDayOfYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function lastDayOfDecade<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { lastDayOfMonth, lastDayOfQuarter, lastDayOfYear, lastDayOfDecade } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // February 11, 2014
|
||||
|
||||
lastDayOfMonth(date);
|
||||
//=> Fri Feb 28 2014 00:00:00
|
||||
|
||||
lastDayOfQuarter(date);
|
||||
//=> Mon Mar 31 2014 00:00:00 (Q1 ends March 31)
|
||||
|
||||
lastDayOfYear(date);
|
||||
//=> Wed Dec 31 2014 00:00:00
|
||||
|
||||
lastDayOfDecade(date);
|
||||
//=> Tue Dec 31 2019 00:00:00
|
||||
```
|
||||
|
||||
### Week Last Days
|
||||
|
||||
```typescript { .api }
|
||||
function lastDayOfWeek<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: WeekStartOptions
|
||||
): DateType;
|
||||
function lastDayOfISOWeek<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
function lastDayOfISOWeekYear<DateType extends Date>(date: DateArg<DateType>): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { lastDayOfWeek, lastDayOfISOWeek, lastDayOfISOWeekYear } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 1, 11); // Tuesday
|
||||
|
||||
lastDayOfWeek(date);
|
||||
//=> Sat Feb 15 2014 00:00:00 (Saturday ends the week)
|
||||
|
||||
lastDayOfWeek(date, { weekStartsOn: 1 });
|
||||
//=> Sun Feb 16 2014 00:00:00 (Sunday ends Monday-starting week)
|
||||
|
||||
lastDayOfISOWeek(date);
|
||||
//=> Sun Feb 16 2014 00:00:00 (ISO week ends Sunday)
|
||||
|
||||
lastDayOfISOWeekYear(date);
|
||||
//=> Sun Dec 28 2014 00:00:00
|
||||
```
|
||||
|
||||
## Iteration Functions
|
||||
|
||||
### Basic Iteration
|
||||
|
||||
```typescript { .api }
|
||||
function eachDayOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
function eachHourOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
function eachMinuteOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { eachDayOfInterval, eachHourOfInterval } from "date-fns";
|
||||
|
||||
// Each day in interval
|
||||
eachDayOfInterval({
|
||||
start: new Date(2014, 0, 1),
|
||||
end: new Date(2014, 0, 3)
|
||||
});
|
||||
//=> [
|
||||
// Wed Jan 01 2014,
|
||||
// Thu Jan 02 2014,
|
||||
// Fri Jan 03 2014
|
||||
// ]
|
||||
|
||||
// Every other day
|
||||
eachDayOfInterval({
|
||||
start: new Date(2014, 0, 1),
|
||||
end: new Date(2014, 0, 7)
|
||||
}, { step: 2 });
|
||||
//=> [Jan 01, Jan 03, Jan 05, Jan 07]
|
||||
|
||||
// Each hour
|
||||
eachHourOfInterval({
|
||||
start: new Date(2014, 0, 1, 0),
|
||||
end: new Date(2014, 0, 1, 3)
|
||||
});
|
||||
//=> [01 00:00, 01 01:00, 01 02:00, 01 03:00]
|
||||
```
|
||||
|
||||
### Period Iteration
|
||||
|
||||
```typescript { .api }
|
||||
function eachWeekOfInterval(
|
||||
interval: Interval,
|
||||
options?: WeekIterationOptions
|
||||
): Date[];
|
||||
function eachMonthOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
function eachQuarterOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
function eachYearOfInterval(
|
||||
interval: Interval,
|
||||
options?: StepOptions
|
||||
): Date[];
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { eachWeekOfInterval, eachMonthOfInterval, eachYearOfInterval } from "date-fns";
|
||||
|
||||
// Each week
|
||||
eachWeekOfInterval({
|
||||
start: new Date(2014, 0, 1),
|
||||
end: new Date(2014, 0, 21)
|
||||
});
|
||||
//=> [Dec 29 2013, Jan 05 2014, Jan 12 2014, Jan 19 2014]
|
||||
|
||||
// Each month
|
||||
eachMonthOfInterval({
|
||||
start: new Date(2014, 0, 1),
|
||||
end: new Date(2014, 3, 1)
|
||||
});
|
||||
//=> [Jan 01 2014, Feb 01 2014, Mar 01 2014, Apr 01 2014]
|
||||
|
||||
// Each year with step
|
||||
eachYearOfInterval({
|
||||
start: new Date(2010, 0, 1),
|
||||
end: new Date(2020, 0, 1)
|
||||
}, { step: 2 });
|
||||
//=> [2010, 2012, 2014, 2016, 2018, 2020]
|
||||
```
|
||||
|
||||
### Weekend Iteration
|
||||
|
||||
```typescript { .api }
|
||||
function eachWeekendOfInterval(interval: Interval): Date[];
|
||||
function eachWeekendOfMonth(date: DateArg<Date>): Date[];
|
||||
function eachWeekendOfYear(date: DateArg<Date>): Date[];
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { eachWeekendOfInterval, eachWeekendOfMonth } from "date-fns";
|
||||
|
||||
// Weekends in interval
|
||||
eachWeekendOfInterval({
|
||||
start: new Date(2018, 8, 17), // Monday
|
||||
end: new Date(2018, 8, 30) // Sunday
|
||||
});
|
||||
//=> [
|
||||
// Sat Sep 22 2018,
|
||||
// Sun Sep 23 2018,
|
||||
// Sat Sep 29 2018,
|
||||
// Sun Sep 30 2018
|
||||
// ]
|
||||
|
||||
// Weekends in month
|
||||
eachWeekendOfMonth(new Date(2022, 1, 1));
|
||||
//=> All Saturday and Sunday dates in February 2022
|
||||
```
|
||||
|
||||
## Navigation Functions
|
||||
|
||||
### Next Day Navigation
|
||||
|
||||
```typescript { .api }
|
||||
function nextDay(date: DateArg<Date>, day: Day): Date;
|
||||
function nextMonday(date: DateArg<Date>): Date;
|
||||
function nextTuesday(date: DateArg<Date>): Date;
|
||||
function nextWednesday(date: DateArg<Date>): Date;
|
||||
function nextThursday(date: DateArg<Date>): Date;
|
||||
function nextFriday(date: DateArg<Date>): Date;
|
||||
function nextSaturday(date: DateArg<Date>): Date;
|
||||
function nextSunday(date: DateArg<Date>): Date;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { nextDay, nextMonday, nextFriday } from "date-fns";
|
||||
|
||||
const date = new Date(2020, 2, 20); // Friday, March 20, 2020
|
||||
|
||||
nextDay(date, 1); // Next Monday
|
||||
//=> Mon Mar 23 2020
|
||||
|
||||
nextMonday(date);
|
||||
//=> Mon Mar 23 2020
|
||||
|
||||
nextFriday(date);
|
||||
//=> Fri Mar 27 2020 (next Friday, not same day)
|
||||
|
||||
// Using Day enum
|
||||
nextDay(date, 0); // Next Sunday
|
||||
//=> Sun Mar 22 2020
|
||||
```
|
||||
|
||||
### Previous Day Navigation
|
||||
|
||||
```typescript { .api }
|
||||
function previousDay(date: DateArg<Date>, day: Day): Date;
|
||||
function previousMonday(date: DateArg<Date>): Date;
|
||||
function previousTuesday(date: DateArg<Date>): Date;
|
||||
function previousWednesday(date: DateArg<Date>): Date;
|
||||
function previousThursday(date: DateArg<Date>): Date;
|
||||
function previousFriday(date: DateArg<Date>): Date;
|
||||
function previousSaturday(date: DateArg<Date>): Date;
|
||||
function previousSunday(date: DateArg<Date>): Date;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { previousDay, previousMonday, previousFriday } from "date-fns";
|
||||
|
||||
const date = new Date(2020, 2, 20); // Friday, March 20, 2020
|
||||
|
||||
previousDay(date, 1); // Previous Monday
|
||||
//=> Mon Mar 16 2020
|
||||
|
||||
previousMonday(date);
|
||||
//=> Mon Mar 16 2020
|
||||
|
||||
previousFriday(date);
|
||||
//=> Fri Mar 13 2020 (previous Friday, not same day)
|
||||
```
|
||||
|
||||
## Rounding Functions
|
||||
|
||||
### Time Rounding
|
||||
|
||||
```typescript { .api }
|
||||
function roundToNearestMinutes<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: RoundToNearestMinutesOptions
|
||||
): DateType;
|
||||
function roundToNearestHours<DateType extends Date>(
|
||||
date: DateArg<DateType>,
|
||||
options?: RoundToNearestHoursOptions
|
||||
): DateType;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { roundToNearestMinutes, roundToNearestHours } from "date-fns";
|
||||
|
||||
const date = new Date(2014, 6, 10, 12, 7, 30);
|
||||
|
||||
// Round to nearest 5 minutes
|
||||
roundToNearestMinutes(date, { nearestTo: 5 });
|
||||
//=> Thu Jul 10 2014 12:05:00 (rounds down)
|
||||
|
||||
// Round to nearest 15 minutes
|
||||
roundToNearestMinutes(date, { nearestTo: 15 });
|
||||
//=> Thu Jul 10 2014 12:00:00
|
||||
|
||||
// Round to nearest hour (default: 1 hour)
|
||||
roundToNearestHours(date);
|
||||
//=> Thu Jul 10 2014 12:00:00
|
||||
|
||||
// Round to nearest 4 hours
|
||||
roundToNearestHours(date, { nearestTo: 4 });
|
||||
//=> Thu Jul 10 2014 12:00:00
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### WeekStartOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface WeekStartOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
```
|
||||
|
||||
### RoundToNearestMinutesOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface RoundToNearestMinutesOptions<DateType extends Date = Date>
|
||||
extends NearestToUnitOptions<NearestMinutes>, RoundingOptions, ContextOptions<DateType> {}
|
||||
```
|
||||
|
||||
### RoundToNearestHoursOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface RoundToNearestHoursOptions<DateType extends Date = Date>
|
||||
extends NearestToUnitOptions<NearestHours>, RoundingOptions, ContextOptions<DateType> {}
|
||||
```
|
||||
|
||||
### WeekIterationOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface WeekIterationOptions extends StepOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
```
|
||||
|
||||
### QuarterOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface QuarterOptions {
|
||||
locale?: Locale;
|
||||
}
|
||||
```
|
||||
|
||||
### StepOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface StepOptions {
|
||||
step?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Day Enum
|
||||
|
||||
```typescript { .api }
|
||||
enum Day {
|
||||
Sunday = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 3,
|
||||
Thursday = 4,
|
||||
Friday = 5,
|
||||
Saturday = 6
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Working Hours Calculation
|
||||
|
||||
```typescript
|
||||
import { eachDayOfInterval, startOfDay, endOfDay, isWeekend } from "date-fns";
|
||||
|
||||
function getWorkingDays(start: Date, end: Date): Date[] {
|
||||
return eachDayOfInterval({ start, end })
|
||||
.filter(date => !isWeekend(date));
|
||||
}
|
||||
|
||||
function getWorkingHours(date: Date): { start: Date; end: Date } {
|
||||
return {
|
||||
start: setHours(startOfDay(date), 9), // 9 AM
|
||||
end: setHours(startOfDay(date), 17) // 5 PM
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Period Boundaries
|
||||
|
||||
```typescript
|
||||
import { startOfMonth, endOfMonth, eachWeekOfInterval } from "date-fns";
|
||||
|
||||
function getMonthBoundaries(date: Date) {
|
||||
return {
|
||||
start: startOfMonth(date),
|
||||
end: endOfMonth(date)
|
||||
};
|
||||
}
|
||||
|
||||
function getWeeksInMonth(date: Date): Date[] {
|
||||
const { start, end } = getMonthBoundaries(date);
|
||||
return eachWeekOfInterval({ start, end });
|
||||
}
|
||||
```
|
||||
|
||||
### Flexible Iteration
|
||||
|
||||
```typescript
|
||||
import { eachDayOfInterval } from "date-fns";
|
||||
|
||||
function getBusinessDays(start: Date, end: Date): Date[] {
|
||||
return eachDayOfInterval({ start, end })
|
||||
.filter(date => {
|
||||
const day = getDay(date);
|
||||
return day !== 0 && day !== 6; // Not Sunday or Saturday
|
||||
});
|
||||
}
|
||||
```
|
||||
467
.tessl/tiles/tessl/npm-date-fns/docs/validation.md
Normal file
467
.tessl/tiles/tessl/npm-date-fns/docs/validation.md
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
# Date Validation and Comparison
|
||||
|
||||
Date validation and comparison functions provide comprehensive utilities for checking date validity, temporal relationships, and period-based comparisons. All functions handle edge cases and invalid dates gracefully.
|
||||
|
||||
## Core Validation
|
||||
|
||||
### isValid
|
||||
|
||||
Check if a date is valid.
|
||||
|
||||
```typescript { .api }
|
||||
function isValid(date: unknown): boolean;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `date` - Any value to check for date validity
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isValid } from "date-fns";
|
||||
|
||||
// Valid dates
|
||||
isValid(new Date(2014, 1, 11)); //=> true
|
||||
isValid(new Date('2014-02-11')); //=> true
|
||||
|
||||
// Invalid dates
|
||||
isValid(new Date('invalid')); //=> false
|
||||
isValid(new Date(2014, 13, 1)); //=> false (month 13)
|
||||
isValid(new Date(2014, 1, 30)); //=> false (Feb 30)
|
||||
|
||||
// Non-date values
|
||||
isValid('2014-02-11'); //=> false
|
||||
isValid(null); //=> false
|
||||
isValid(undefined); //=> false
|
||||
```
|
||||
|
||||
### isDate
|
||||
|
||||
Check if a value is a Date object.
|
||||
|
||||
```typescript { .api }
|
||||
function isDate(value: any): value is Date;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isDate } from "date-fns";
|
||||
|
||||
isDate(new Date()); //=> true
|
||||
isDate(new Date('invalid')); //=> true (still a Date object)
|
||||
isDate('2014-02-11'); //=> false
|
||||
isDate(1392098430000); //=> false
|
||||
```
|
||||
|
||||
### isExists
|
||||
|
||||
Check if a date exists in the calendar.
|
||||
|
||||
```typescript { .api }
|
||||
function isExists(year: number, month: number, day: number): boolean;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `year` - The year to check
|
||||
- `month` - The month to check (0-11, January is 0)
|
||||
- `day` - The day to check (1-31)
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isExists } from "date-fns";
|
||||
|
||||
// Valid dates
|
||||
isExists(2014, 1, 11); //=> true
|
||||
isExists(2014, 1, 28); //=> true
|
||||
|
||||
// Invalid dates
|
||||
isExists(2014, 1, 30); //=> false (Feb 30)
|
||||
isExists(2014, 13, 1); //=> false (month 13)
|
||||
isExists(2014, 1, 0); //=> false (day 0)
|
||||
```
|
||||
|
||||
### isMatch
|
||||
|
||||
Check if a date string matches a format pattern.
|
||||
|
||||
```typescript { .api }
|
||||
function isMatch(dateStr: string, formatStr: string, options?: IsMatchOptions): boolean;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dateStr` - The date string to validate
|
||||
- `formatStr` - The format pattern to match against
|
||||
- `options` - Optional matching configuration
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isMatch } from "date-fns";
|
||||
|
||||
// Valid formats
|
||||
isMatch('2014-02-11', 'yyyy-MM-dd'); //=> true
|
||||
isMatch('11/02/2014', 'MM/dd/yyyy'); //=> true
|
||||
isMatch('Feb 11, 2014', 'MMM dd, yyyy'); //=> true
|
||||
|
||||
// Invalid formats
|
||||
isMatch('2014-02-11', 'dd/MM/yyyy'); //=> false
|
||||
isMatch('invalid date', 'yyyy-MM-dd'); //=> false
|
||||
isMatch('2014-13-01', 'yyyy-MM-dd'); //=> false (invalid month)
|
||||
```
|
||||
|
||||
## Date Comparison
|
||||
|
||||
### Basic Comparison
|
||||
|
||||
```typescript { .api }
|
||||
function isAfter(date: DateArg<Date>, dateToCompare: DateArg<Date>): boolean;
|
||||
function isBefore(date: DateArg<Date>, dateToCompare: DateArg<Date>): boolean;
|
||||
function isEqual(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isAfter, isBefore, isEqual } from "date-fns";
|
||||
|
||||
const date1 = new Date(2014, 0, 1);
|
||||
const date2 = new Date(2014, 0, 2);
|
||||
|
||||
isAfter(date2, date1); //=> true
|
||||
isBefore(date1, date2); //=> true
|
||||
isEqual(date1, date1); //=> true
|
||||
```
|
||||
|
||||
### Comparison Functions
|
||||
|
||||
```typescript { .api }
|
||||
function compareAsc(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): number;
|
||||
function compareDesc(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): number;
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
- `-1` if first date is before second date
|
||||
- `0` if dates are equal
|
||||
- `1` if first date is after second date
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { compareAsc, compareDesc } from "date-fns";
|
||||
|
||||
const date1 = new Date(2014, 0, 1);
|
||||
const date2 = new Date(2014, 0, 2);
|
||||
|
||||
compareAsc(date1, date2); //=> -1
|
||||
compareAsc(date2, date1); //=> 1
|
||||
compareAsc(date1, date1); //=> 0
|
||||
|
||||
// For sorting
|
||||
const dates = [
|
||||
new Date(1995, 6, 2),
|
||||
new Date(1987, 1, 11),
|
||||
new Date(1989, 6, 10)
|
||||
];
|
||||
dates.sort(compareAsc);
|
||||
// Now sorted chronologically
|
||||
```
|
||||
|
||||
### Min/Max Functions
|
||||
|
||||
```typescript { .api }
|
||||
function min(dates: DateArg<Date>[]): Date;
|
||||
function max(dates: DateArg<Date>[]): Date;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { min, max } from "date-fns";
|
||||
|
||||
const dates = [
|
||||
new Date(1989, 6, 10),
|
||||
new Date(1987, 1, 11),
|
||||
new Date(1995, 6, 2)
|
||||
];
|
||||
|
||||
min(dates); //=> new Date(1987, 1, 11)
|
||||
max(dates); //=> new Date(1995, 6, 2)
|
||||
```
|
||||
|
||||
## Temporal Validation
|
||||
|
||||
### Relative to Current Time
|
||||
|
||||
```typescript { .api }
|
||||
function isFuture(date: DateArg<Date>): boolean;
|
||||
function isPast(date: DateArg<Date>): boolean;
|
||||
function isToday(date: DateArg<Date>): boolean;
|
||||
function isTomorrow(date: DateArg<Date>): boolean;
|
||||
function isYesterday(date: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isFuture, isPast, isToday, isTomorrow, isYesterday } from "date-fns";
|
||||
|
||||
const now = new Date();
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
isFuture(tomorrow); //=> true
|
||||
isPast(new Date(2020, 0, 1)); //=> true (assuming current year > 2020)
|
||||
isToday(now); //=> true
|
||||
```
|
||||
|
||||
### This Period Validation
|
||||
|
||||
```typescript { .api }
|
||||
function isThisSecond(date: DateArg<Date>): boolean;
|
||||
function isThisMinute(date: DateArg<Date>): boolean;
|
||||
function isThisHour(date: DateArg<Date>): boolean;
|
||||
function isThisWeek(date: DateArg<Date>, options?: WeekOptions): boolean;
|
||||
function isThisISOWeek(date: DateArg<Date>): boolean;
|
||||
function isThisMonth(date: DateArg<Date>): boolean;
|
||||
function isThisQuarter(date: DateArg<Date>): boolean;
|
||||
function isThisYear(date: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isThisWeek, isThisMonth, isThisYear } from "date-fns";
|
||||
|
||||
const now = new Date();
|
||||
|
||||
isThisWeek(now); //=> true
|
||||
isThisMonth(now); //=> true
|
||||
isThisYear(now); //=> true
|
||||
|
||||
// Last year
|
||||
isThisYear(new Date(2020, 0, 1)); //=> false (if current year > 2020)
|
||||
```
|
||||
|
||||
## Day of Week Validation
|
||||
|
||||
### Specific Day Checks
|
||||
|
||||
```typescript { .api }
|
||||
function isMonday(date: DateArg<Date>): boolean;
|
||||
function isTuesday(date: DateArg<Date>): boolean;
|
||||
function isWednesday(date: DateArg<Date>): boolean;
|
||||
function isThursday(date: DateArg<Date>): boolean;
|
||||
function isFriday(date: DateArg<Date>): boolean;
|
||||
function isSaturday(date: DateArg<Date>): boolean;
|
||||
function isSunday(date: DateArg<Date>): boolean;
|
||||
function isWeekend(date: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isMonday, isSaturday, isWeekend } from "date-fns";
|
||||
|
||||
// Monday, Feb 10, 2014
|
||||
const monday = new Date(2014, 1, 10);
|
||||
isMonday(monday); //=> true
|
||||
isSaturday(monday); //=> false
|
||||
|
||||
// Saturday, Feb 8, 2014
|
||||
const saturday = new Date(2014, 1, 8);
|
||||
isWeekend(saturday); //=> true
|
||||
```
|
||||
|
||||
## Same Period Validation
|
||||
|
||||
### Same Time Unit Checks
|
||||
|
||||
```typescript { .api }
|
||||
function isSameSecond(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameMinute(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameHour(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameDay(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameWeek(dateLeft: DateArg<Date>, dateRight: DateArg<Date>, options?: WeekOptions): boolean;
|
||||
function isSameISOWeek(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameMonth(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameQuarter(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameYear(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
function isSameISOWeekYear(dateLeft: DateArg<Date>, dateRight: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isSameDay, isSameWeek, isSameMonth, isSameYear } from "date-fns";
|
||||
|
||||
const date1 = new Date(2014, 8, 6, 14, 0);
|
||||
const date2 = new Date(2014, 8, 6, 15, 30);
|
||||
|
||||
isSameDay(date1, date2); //=> true (same day, different time)
|
||||
isSameWeek(date1, date2); //=> true
|
||||
isSameMonth(date1, date2); //=> true
|
||||
isSameYear(date1, date2); //=> true
|
||||
|
||||
// Different days
|
||||
const date3 = new Date(2014, 8, 7);
|
||||
isSameDay(date1, date3); //=> false
|
||||
```
|
||||
|
||||
## Period Boundary Validation
|
||||
|
||||
### Month Boundaries
|
||||
|
||||
```typescript { .api }
|
||||
function isFirstDayOfMonth(date: DateArg<Date>): boolean;
|
||||
function isLastDayOfMonth(date: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isFirstDayOfMonth, isLastDayOfMonth } from "date-fns";
|
||||
|
||||
isFirstDayOfMonth(new Date(2014, 1, 1)); //=> true
|
||||
isFirstDayOfMonth(new Date(2014, 1, 2)); //=> false
|
||||
|
||||
isLastDayOfMonth(new Date(2014, 1, 28)); //=> true (Feb in non-leap year)
|
||||
isLastDayOfMonth(new Date(2014, 1, 27)); //=> false
|
||||
```
|
||||
|
||||
### Leap Year
|
||||
|
||||
```typescript { .api }
|
||||
function isLeapYear(date: DateArg<Date>): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isLeapYear } from "date-fns";
|
||||
|
||||
isLeapYear(new Date(2012, 0, 1)); //=> true
|
||||
isLeapYear(new Date(2013, 0, 1)); //=> false
|
||||
isLeapYear(new Date(2000, 0, 1)); //=> true
|
||||
isLeapYear(new Date(1900, 0, 1)); //=> false
|
||||
```
|
||||
|
||||
## Interval Validation
|
||||
|
||||
### Interval Checks
|
||||
|
||||
```typescript { .api }
|
||||
function isWithinInterval(date: DateArg<Date>, interval: Interval): boolean;
|
||||
function areIntervalsOverlapping(
|
||||
intervalLeft: Interval,
|
||||
intervalRight: Interval,
|
||||
options?: IntervalOptions
|
||||
): boolean;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { isWithinInterval, areIntervalsOverlapping } from "date-fns";
|
||||
|
||||
// Within interval
|
||||
isWithinInterval(new Date(2014, 0, 3), {
|
||||
start: new Date(2014, 0, 1),
|
||||
end: new Date(2014, 0, 7)
|
||||
}); //=> true
|
||||
|
||||
// Overlapping intervals
|
||||
areIntervalsOverlapping(
|
||||
{ start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
|
||||
{ start: new Date(2014, 0, 17), end: new Date(2014, 0, 21) }
|
||||
); //=> true
|
||||
|
||||
// Non-overlapping intervals
|
||||
areIntervalsOverlapping(
|
||||
{ start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
|
||||
{ start: new Date(2014, 0, 21), end: new Date(2014, 0, 30) }
|
||||
); //=> false
|
||||
```
|
||||
|
||||
## Utility Functions
|
||||
|
||||
### Closest Date Finding
|
||||
|
||||
```typescript { .api }
|
||||
function closestTo(dateToCompare: DateArg<Date>, dates: DateArg<Date>[]): Date;
|
||||
function closestIndexTo(dateToCompare: DateArg<Date>, dates: DateArg<Date>[]): number;
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
import { closestTo, closestIndexTo } from "date-fns";
|
||||
|
||||
const dateToCompare = new Date(2015, 8, 6);
|
||||
const dates = [
|
||||
new Date(2015, 0, 1),
|
||||
new Date(2016, 0, 1),
|
||||
new Date(2017, 0, 1)
|
||||
];
|
||||
|
||||
closestTo(dateToCompare, dates); //=> new Date(2015, 0, 1)
|
||||
closestIndexTo(dateToCompare, dates); //=> 0
|
||||
```
|
||||
|
||||
## Option Types
|
||||
|
||||
### WeekOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface WeekOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
```
|
||||
|
||||
### IsMatchOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface IsMatchOptions {
|
||||
locale?: Locale;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
useAdditionalWeekYearTokens?: boolean;
|
||||
useAdditionalDayOfYearTokens?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### IntervalOptions
|
||||
|
||||
```typescript { .api }
|
||||
interface IntervalOptions {
|
||||
inclusive?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Validation Chain
|
||||
|
||||
```typescript
|
||||
import { isValid, isAfter, isBefore } from "date-fns";
|
||||
|
||||
function validateDateRange(date: any, minDate: Date, maxDate: Date): boolean {
|
||||
return isValid(date) &&
|
||||
isAfter(date, minDate) &&
|
||||
isBefore(date, maxDate);
|
||||
}
|
||||
```
|
||||
|
||||
### Array Filtering
|
||||
|
||||
```typescript
|
||||
import { isWeekend, isFuture } from "date-fns";
|
||||
|
||||
const dates = [/* array of dates */];
|
||||
|
||||
// Filter weekend dates
|
||||
const weekends = dates.filter(isWeekend);
|
||||
|
||||
// Filter future dates
|
||||
const futureDates = dates.filter(isFuture);
|
||||
```
|
||||
|
||||
### Safe Comparison
|
||||
|
||||
```typescript
|
||||
import { isValid, compareAsc } from "date-fns";
|
||||
|
||||
function safeCompareAsc(date1: any, date2: any): number {
|
||||
if (!isValid(date1) || !isValid(date2)) {
|
||||
throw new Error('Invalid date provided');
|
||||
}
|
||||
return compareAsc(date1, date2);
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue