diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ace48a1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +jobs: + verify: + name: Build & Verify SDK Runtime + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v3 + with: + version: 10.34.1 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 25.9.4 + cache: 'pnpm' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Run Build + run: pnpm build + + - name: Run Unit & Compatibility Tests + run: pnpm test:run + + - name: Verify Public API Report + run: pnpm api-report:ci \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 322945e..2c53eec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -146,3 +146,14 @@ pnpm build # Build must succeed - GitHub Issues: preferred for all task discussion - Contact: cerealboxx123@gmail.com + +--- + +## Public API Changes + +We monitor changes to our public API surface closely to prevent accidental breaking changes. + +If you modify exported classes, interfaces, or types: +1. Run `pnpm api-report` locally to update the API baseline file (`api-report/guildpass-sdk.api.md`). +2. Commit the updated `guildpass-sdk.api.md` file alongside your code changes. +3. Your pull request will display a diff of the API changes for maintainer review. \ No newline at end of file diff --git a/README.md b/README.md index cd2e166..671836f 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ GuildPass is a Web3 membership and access-control protocol designed for token-ga - **⚡ Pluggable Caching**: Built-in TTL cache with optional custom adapter support (Redis, etc.). - **🧩 Modular Architecture**: Clean service-based design for minimal bundle size. - **💪 Type Safe**: First-class TypeScript support with comprehensive definitions. -- **🌐 Universal**: Seamless integration with Node.js, modern browsers, and Edge runtimes. +- **🌐 Universal**: Seamless integration with Node.js (18+), modern browsers (JSDOM verified), and Edge runtimes (V8/Cloudflare Workers verified). Fully tested in continuous integration across all environments. ## 📦 Installation diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 0000000..be068f7 --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "/dist/index.d.ts", + "apiReport": { + "enabled": true, + "reportFolder": "/api-report/", + "reportFileName": "guildpass-sdk.api.md" + }, + "docModel": { + "enabled": false + }, + "dtsRollup": { + "enabled": false + }, + "tsdocMetadata": { + "enabled": false + }, + "messages": { + "compilerMessageReporting": { + "default": { + "logLevel": "warning" + } + }, + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + }, + "ae-missing-release-tag": { + "logLevel": "none" + } + }, + "tsdocMessageReporting": { + "default": { + "logLevel": "none" + } + } + } +} \ No newline at end of file diff --git a/api-report/guildpass-sdk.api.md b/api-report/guildpass-sdk.api.md new file mode 100644 index 0000000..61e538b --- /dev/null +++ b/api-report/guildpass-sdk.api.md @@ -0,0 +1,475 @@ +## API Report File for "@guildpass/sdk" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export type AccessCheckBatchOptions = { + concurrency?: number; + failFast?: boolean; +}; + +// @public (undocumented) +export type AccessCheckBatchResult = { + input: AccessCheckParams; + status: 'fulfilled' | 'rejected'; + value?: AccessCheckResult; + error?: Error; +}; + +// @public (undocumented) +export type AccessCheckParams = { + walletAddress: string; + guildId: string; + resourceId: string; +}; + +// @public (undocumented) +export type AccessCheckResult = { + hasAccess: boolean; + walletAddress: string; + guildId: string; + resourceId: string; + requiredRoles: string[]; + matchedRoles: string[]; + reason?: string; +}; + +// @public (undocumented) +export type AccessRequirement = { + type: 'TOKEN' | 'NFT' | 'ROLE' | 'WHITELIST'; + address?: Address; + id?: string; + minAmount?: string; +}; + +// @public (undocumented) +export class AccessService { + // Warning: (ae-forgotten-export) The symbol "HttpClient" needs to be exported by the entry point index.d.ts + constructor(http: HttpClient, validateResponses?: boolean); + checkAccess(params: AccessCheckParams, options?: RequestOptions): Promise; + checkAccessBatch(items: AccessCheckParams[], options?: AccessCheckBatchOptions & RequestOptions): Promise; + checkRoleAccess(params: RoleAccessCheckParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export type Address = string; + +// @public +export const areAddressesEqual: (addr1: string, addr2: string) => boolean; + +// @public +export function assertValidResponse(value: unknown, guard: (value: unknown) => value is T, typeName: string): T; + +// @public (undocumented) +export interface CacheAdapter { + // (undocumented) + clear(): Promise; + // (undocumented) + delete(key: string): Promise; + deleteByPrefix?(prefix: string): Promise; + // (undocumented) + get(key: string): Promise; + // (undocumented) + set(key: string, value: T, ttl?: number): Promise; +} + +// @public +export const capitalise: (str: string) => string; + +// @public +export type ChainConfig = { + rpcUrl?: string; + contractAddress?: string; +}; + +// @public (undocumented) +export function connectWallet(provider: EIP1193Provider): Promise; + +// @public (undocumented) +export class ContractClient { + constructor(config: GuildPassClientConfig); + getChainConfig(chainId?: number): ChainConfig; + getGuildOwner(params: GuildOwnerParams): Promise; + getMembershipTokenBalance(params: TokenBalanceParams): Promise; + validateRoleRequirement(params: RoleRequirementParams): Promise; +} + +// @public (undocumented) +export const DEFAULT_CONFIG: Partial; + +// @public +export interface EIP1193Provider { + // (undocumented) + on?(eventName: string, listener: (...args: any[]) => void): void; + // (undocumented) + removeListener?(eventName: string, listener: (...args: any[]) => void): void; + // (undocumented) + request(args: { + method: string; + params?: any[]; + }): Promise; +} + +// @public +export const encodePathSegment: (segment: string) => string; + +// @public (undocumented) +export type ErrorHookPayload = RequestHookPayload & { + error: Error; + durationMs: number; +}; + +// @public (undocumented) +export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise; + +// @public +export const formatIsoDate: (date: string | number | Date) => string; + +// @public +export function getChainId(provider: EIP1193Provider): Promise; + +// @public (undocumented) +export type GetGuildParams = { + guildId: string; +}; + +// @public (undocumented) +export type GetRolesParams = { + guildId: string; +}; + +// @public (undocumented) +export type GetUserRolesParams = { + walletAddress: string; + guildId: string; +}; + +// @public (undocumented) +export type Guild = { + id: string; + name: string; + description?: string; + ownerAddress: string; + contractAddress?: string; + chainId: number; +}; + +// @public (undocumented) +export type GuildConfig = { + id: string; + theme?: string; + logoUrl?: string; + bannerUrl?: string; + socialLinks?: Record; +}; + +// @public (undocumented) +export type GuildOwnerParams = { + guildId: string; + chainId?: number; + contractAddress?: string; +}; + +// @public +export class GuildPassClient { + constructor(config: GuildPassClientConfig); + // (undocumented) + readonly access: AccessService; + clearCache(): Promise; + // (undocumented) + readonly contracts: ContractClient; + getConfig(): Omit; + // (undocumented) + readonly guilds: GuildsService; + invalidateGuildCache(guildId: string): Promise; + invalidateWalletCache(walletAddress: string): Promise; + // (undocumented) + readonly membership: MembershipService; + // (undocumented) + readonly roles: RolesService; +} + +// @public (undocumented) +export type GuildPassClientConfig = { + apiUrl: string; + chainId?: number; + rpcUrl?: string; + contractAddress?: string; + chains?: Record; + apiKey?: string; + timeoutMs?: number; + retry?: RetryConfig; + hooks?: HttpHooks; + fetch?: FetchLike; + validateResponses?: boolean; + cache?: CacheAdapter; + cacheTtl?: number; +}; + +// @public (undocumented) +export class GuildPassError extends Error { + constructor(message: string, code: GuildPassErrorCode, status?: number, details?: any); + // (undocumented) + readonly code: GuildPassErrorCode; + // (undocumented) + readonly details?: any; + // (undocumented) + static fromHttpError(status: number, details?: any): GuildPassError; + // (undocumented) + readonly status?: number; +} + +// @public (undocumented) +export enum GuildPassErrorCode { + // (undocumented) + ABORTED = "ABORTED", + // (undocumented) + CONFLICT = "CONFLICT", + // (undocumented) + HTTP_ERROR = "HTTP_ERROR", + // (undocumented) + INVALID_ADDRESS = "INVALID_ADDRESS", + // (undocumented) + INVALID_CONFIG = "INVALID_CONFIG", + // (undocumented) + INVALID_INPUT = "INVALID_INPUT", + // (undocumented) + INVALID_RESPONSE = "INVALID_RESPONSE", + // (undocumented) + MISSING_FETCH = "MISSING_FETCH", + // (undocumented) + NOT_FOUND = "NOT_FOUND", + // (undocumented) + NOT_IMPLEMENTED = "NOT_IMPLEMENTED", + // (undocumented) + RATE_LIMITED = "RATE_LIMITED", + // (undocumented) + REQUEST_CANCELLED = "REQUEST_CANCELLED", + // (undocumented) + SERVER_ERROR = "SERVER_ERROR", + // (undocumented) + TIMEOUT = "TIMEOUT", + // (undocumented) + UNAUTHORISED = "UNAUTHORISED", + // (undocumented) + UNKNOWN_ERROR = "UNKNOWN_ERROR" +} + +// @public (undocumented) +export type GuildRole = { + id: string; + name: string; + description?: string; + requirements?: AccessRequirement[]; +}; + +// @public (undocumented) +export class GuildsService { + constructor(http: HttpClient, validateResponses?: boolean); + getGuild(params: GetGuildParams, options?: RequestOptions): Promise; + getGuildConfig(params: GetGuildParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export function hasInjectedWallet(): boolean; + +// @public (undocumented) +export type HttpClientConfig = { + retry?: RetryConfig; + hooks?: HttpHooks; + fetch?: FetchLike; +}; + +// @public (undocumented) +export interface HttpHooks { + // (undocumented) + onError?: (payload: ErrorHookPayload) => void | Promise; + // (undocumented) + onRequest?: (payload: RequestHookPayload) => void | Promise; + // (undocumented) + onResponse?: (payload: ResponseHookPayload) => void | Promise; +} + +// @public (undocumented) +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; + +// @public (undocumented) +export type HttpRequestOptions = { + method?: HttpMethod; + headers?: Record; + body?: any; + params?: Record; + timeoutMs?: number; + retry?: RetryConfig; + signal?: AbortSignal; +}; + +// @public (undocumented) +export type HttpResponse = { + data: T; + status: number; + headers: Headers; +}; + +// @public +export class InMemoryCacheAdapter implements CacheAdapter { + // (undocumented) + clear(): Promise; + // (undocumented) + delete(key: string): Promise; + // (undocumented) + deleteByPrefix(prefix: string): Promise; + // (undocumented) + get(key: string): Promise; + // (undocumented) + set(key: string, value: T, ttl?: number): Promise; +} + +// @public +export function isAccessCheckResult(value: unknown): value is AccessCheckResult; + +// @public +export const isChecksumAddress: (address: string) => boolean; + +// @public (undocumented) +export function isGuild(value: unknown): value is Guild; + +// @public (undocumented) +export function isGuildConfig(value: unknown): value is GuildConfig; + +// @public (undocumented) +export function isGuildRole(value: unknown): value is GuildRole; + +// @public (undocumented) +export function isGuildRoleArray(value: unknown): value is GuildRole[]; + +// @public (undocumented) +export function isMembership(value: unknown): value is Membership; + +// @public (undocumented) +export type Membership = { + walletAddress: string; + guildId: string; + isActive: boolean; + roles: string[]; + joinedAt?: string; + expiresAt?: string; +}; + +// @public (undocumented) +export type MembershipParams = { + walletAddress: string; + guildId: string; +}; + +// @public (undocumented) +export class MembershipService { + constructor(http: HttpClient, validateResponses?: boolean); + getMembership(params: MembershipParams, options?: RequestOptions): Promise; + isMember(params: MembershipParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export type NetworkConfig = { + chainId: number; + name: string; + rpcUrl?: string; + explorerUrl?: string; +}; + +// @public +export const normaliseAddress: (address: string) => string; + +// @public (undocumented) +export type RequestHookPayload = { + method: HttpMethod; + path: string; + headers: Record; +}; + +// @public (undocumented) +export type RequestOptions = { + timeoutMs?: number; + retry?: RetryConfig; +}; + +// @public +export function resolveChainConfig(config: GuildPassClientConfig, chainId: number): ChainConfig; + +// @public (undocumented) +export type ResponseHookPayload = RequestHookPayload & { + status: number; + durationMs: number; + responseHeaders: Record; +}; + +// @public (undocumented) +export type RetryConfig = { + maxRetries?: number; + baseDelayMs?: number; + maxDelayMs?: number; + retryableStatuses?: number[]; + allowMutatingRetry?: boolean; +}; + +// @public (undocumented) +export type RoleAccessCheckParams = { + walletAddress: string; + guildId: string; + roleId: string; +}; + +// @public (undocumented) +export type RoleRequirementParams = { + walletAddress: string; + requirement: AccessRequirement; +}; + +// @public (undocumented) +export class RolesService { + constructor(http: HttpClient, validateResponses?: boolean); + getRoles(params: GetRolesParams, options?: RequestOptions): Promise; + getUserRoles(params: GetUserRolesParams, options?: RequestOptions): Promise; +} + +// @public +export const shortenAddress: (address: string, chars?: number) => string; + +// @public (undocumented) +export const SUPPORTED_NETWORKS: Record; + +// @public (undocumented) +export function switchChain(provider: EIP1193Provider, chainId: string): Promise; + +// @public +export const toChecksumAddress: (address: string) => string; + +// @public (undocumented) +export type TokenBalanceParams = { + walletAddress: string; + chainId?: number; + contractAddress?: string; +}; + +// @public +export const validateAddress: (address: string, options?: { + strict?: boolean; +}) => void; + +// @public (undocumented) +export function validateConfig(config: GuildPassClientConfig): void; + +// @public +export const validateGuildId: (guildId: string) => void; + +// @public +export const validateResourceId: (resourceId: string) => void; + +// @public +export const validateRoleId: (roleId: string) => void; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/package.json b/package.json index 5387ddb..d8abfb7 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,15 @@ "build": "tsup", "test": "vitest", "test:run": "vitest run", + "test:compat": "vitest run --workspace=browser-jsdom --workspace=edge", "test:smoke": "node tests/smoke/esm.smoke.mjs && node tests/smoke/cjs.smoke.cjs && tsc -p tsconfig.smoke.json", "lint": "eslint .", "format": "prettier --write .", "typecheck": "tsc --noEmit", "docs": "typedoc src/index.ts", "validate": "npx --yes publint", - "release:check": "pnpm lint && pnpm typecheck && pnpm test:run && pnpm build && pnpm test:smoke && pnpm docs && pnpm validate" + "api-report": "api-extractor run --local --verbose", + "api-report:ci": "pnpm build && api-extractor run --verbose" }, "keywords": [ "web3", @@ -78,6 +80,7 @@ "js-sha3": "^0.9.3" }, "devDependencies": { + "@microsoft/api-extractor": "^7.58.9", "@types/node": "^25.9.3", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5f9450..3893350 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,9 @@ importers: specifier: ^0.9.3 version: 0.9.3 devDependencies: + '@microsoft/api-extractor': + specifier: ^7.58.9 + version: 7.58.9(@types/node@25.9.4) '@types/node': specifier: ^25.9.3 version: 25.9.4 @@ -29,7 +32,7 @@ importers: version: 3.8.5 tsup: specifier: ^8.0.2 - version: 8.5.1(postcss@8.5.15)(typescript@5.9.3) + version: 8.5.1(@microsoft/api-extractor@7.58.9(@types/node@25.9.4))(postcss@8.5.15)(typescript@5.9.3) typedoc: specifier: ^0.25.8 version: 0.25.13(typescript@5.9.3) @@ -384,6 +387,19 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@microsoft/api-extractor-model@7.33.8': + resolution: {integrity: sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w==} + + '@microsoft/api-extractor@7.58.9': + resolution: {integrity: sha512-S2UF4yza5GoxCmf7hJQNxJNZN9ltOVuOQv8Dy+Z21aol5ERoBNMdWcQHm4MJMPPItW4H/4rZD906iaf4mUojJA==} + hasBin: true + + '@microsoft/tsdoc-config@0.18.1': + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -534,9 +550,42 @@ packages: cpu: [x64] os: [win32] + '@rushstack/node-core-library@5.23.1': + resolution: {integrity: sha512-wlKmIKIYCKuCASbITvOxLZXepPbwXvrv7S6ig6XNWFchSyhL/E2txmVXspHY49Wu2dzf7nI27a2k/yV5BA3EiA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.7.3': + resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==} + + '@rushstack/terminal@0.24.0': + resolution: {integrity: sha512-8ZQS4MMaGsv27EXCBiH7WMPkRZrffeDoIevs6z9TM5dzqiY6+Hn4evfK/G+gvgBTjfvfkHIZPQQmalmI2sM4TQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@5.3.10': + resolution: {integrity: sha512-fwI076HYknC0IrMXdY6UmjDv+PH7NHhNJX3/pY2UblSE5XrXgndXZPiOe/6ZtuFpn6DvVDVNhtkIzQ+Qu/MhVQ==} + '@sinclair/typebox@0.27.10': resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -633,9 +682,28 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -654,6 +722,9 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -667,12 +738,20 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + brace-expansion@1.1.15: resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -751,6 +830,10 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -759,6 +842,10 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -827,6 +914,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -861,6 +951,10 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + fs-extra@11.3.6: + resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} + engines: {node: '>=14.14'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -869,6 +963,9 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} @@ -896,6 +993,9 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -903,6 +1003,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -915,6 +1019,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -926,6 +1034,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -949,6 +1061,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -969,12 +1084,18 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1033,6 +1154,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -1108,6 +1233,9 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -1188,6 +1316,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1196,6 +1328,11 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1213,6 +1350,11 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -1244,16 +1386,27 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + source-map@0.7.6: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1278,6 +1431,14 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -1372,6 +1533,10 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1669,6 +1834,41 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@microsoft/api-extractor-model@7.33.8(@types/node@25.9.4)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.23.1(@types/node@25.9.4) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.58.9(@types/node@25.9.4)': + dependencies: + '@microsoft/api-extractor-model': 7.33.8(@types/node@25.9.4) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.23.1(@types/node@25.9.4) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.0(@types/node@25.9.4) + '@rushstack/ts-command-line': 5.3.10(@types/node@25.9.4) + diff: 8.0.4 + minimatch: 10.2.3 + resolve: 1.22.12 + semver: 7.7.4 + source-map: 0.6.1 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/tsdoc-config@0.18.1': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.12 + + '@microsoft/tsdoc@0.16.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1756,8 +1956,49 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true + '@rushstack/node-core-library@5.23.1(@types/node@25.9.4)': + dependencies: + ajv: 8.18.0 + ajv-draft-04: 1.0.0(ajv@8.18.0) + ajv-formats: 3.0.1(ajv@8.18.0) + fs-extra: 11.3.6 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.12 + semver: 7.7.4 + optionalDependencies: + '@types/node': 25.9.4 + + '@rushstack/problem-matcher@0.2.1(@types/node@25.9.4)': + optionalDependencies: + '@types/node': 25.9.4 + + '@rushstack/rig-package@0.7.3': + dependencies: + jju: 1.4.0 + resolve: 1.22.12 + + '@rushstack/terminal@0.24.0(@types/node@25.9.4)': + dependencies: + '@rushstack/node-core-library': 5.23.1(@types/node@25.9.4) + '@rushstack/problem-matcher': 0.2.1(@types/node@25.9.4) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 25.9.4 + + '@rushstack/ts-command-line@5.3.10(@types/node@25.9.4)': + dependencies: + '@rushstack/terminal': 0.24.0(@types/node@25.9.4) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + '@sinclair/typebox@0.27.10': {} + '@types/argparse@1.0.38': {} + '@types/estree@1.0.9': {} '@types/node@25.9.4': @@ -1886,6 +2127,14 @@ snapshots: acorn@8.17.0: {} + ajv-draft-04@1.0.0(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -1893,6 +2142,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-sequence-parser@1.1.3: {} @@ -1905,6 +2161,10 @@ snapshots: any-promise@1.3.0: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + argparse@2.0.1: {} array-union@2.1.0: {} @@ -1913,6 +2173,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 @@ -1922,6 +2184,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.7: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -1990,6 +2256,8 @@ snapshots: diff-sequences@29.6.3: {} + diff@8.0.4: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -1998,6 +2266,8 @@ snapshots: dependencies: esutils: 2.0.3 + es-errors@1.3.0: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -2153,6 +2423,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.3: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -2188,11 +2460,19 @@ snapshots: flatted@3.4.2: {} + fs-extra@11.3.6: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + fs.realpath@1.0.0: {} fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + get-func-name@2.0.2: {} get-stream@8.0.1: {} @@ -2227,10 +2507,16 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} has-flag@4.0.0: {} + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + human-signals@5.0.0: {} ignore@5.3.2: {} @@ -2240,6 +2526,8 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-lazy@4.0.0: {} + imurmurhash@0.1.4: {} inflight@1.0.6: @@ -2249,6 +2537,10 @@ snapshots: inherits@2.0.4: {} + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + is-extglob@2.1.1: {} is-glob@4.0.3: @@ -2263,6 +2555,8 @@ snapshots: isexe@2.0.0: {} + jju@1.4.0: {} + joycon@3.1.1: {} js-sha3@0.9.3: {} @@ -2277,10 +2571,18 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} jsonc-parser@3.3.1: {} + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2330,6 +2632,10 @@ snapshots: mimic-fn@4.0.0: {} + minimatch@10.2.3: + dependencies: + brace-expansion: 5.0.7 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.15 @@ -2404,6 +2710,8 @@ snapshots: path-key@4.0.0: {} + path-parse@1.0.7: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -2456,10 +2764,19 @@ snapshots: readdirp@4.1.2: {} + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} rimraf@3.0.2: @@ -2501,6 +2818,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + semver@7.7.4: {} + semver@7.8.5: {} shebang-command@2.0.0: @@ -2524,12 +2843,18 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.6.1: {} + source-map@0.7.6: {} + sprintf-js@1.0.3: {} + stackback@0.0.2: {} std-env@3.10.0: {} + string-argv@0.3.2: {} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -2556,6 +2881,12 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -2591,7 +2922,7 @@ snapshots: ts-interface-checker@0.1.13: {} - tsup@8.5.1(postcss@8.5.15)(typescript@5.9.3): + tsup@8.5.1(@microsoft/api-extractor@7.58.9(@types/node@25.9.4))(postcss@8.5.15)(typescript@5.9.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 @@ -2611,6 +2942,7 @@ snapshots: tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: + '@microsoft/api-extractor': 7.58.9(@types/node@25.9.4) postcss: 8.5.15 typescript: 5.9.3 transitivePeerDependencies: @@ -2641,6 +2973,8 @@ snapshots: undici-types@7.24.6: {} + universalify@2.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/temp/guildpass-sdk.api.md b/temp/guildpass-sdk.api.md new file mode 100644 index 0000000..61e538b --- /dev/null +++ b/temp/guildpass-sdk.api.md @@ -0,0 +1,475 @@ +## API Report File for "@guildpass/sdk" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export type AccessCheckBatchOptions = { + concurrency?: number; + failFast?: boolean; +}; + +// @public (undocumented) +export type AccessCheckBatchResult = { + input: AccessCheckParams; + status: 'fulfilled' | 'rejected'; + value?: AccessCheckResult; + error?: Error; +}; + +// @public (undocumented) +export type AccessCheckParams = { + walletAddress: string; + guildId: string; + resourceId: string; +}; + +// @public (undocumented) +export type AccessCheckResult = { + hasAccess: boolean; + walletAddress: string; + guildId: string; + resourceId: string; + requiredRoles: string[]; + matchedRoles: string[]; + reason?: string; +}; + +// @public (undocumented) +export type AccessRequirement = { + type: 'TOKEN' | 'NFT' | 'ROLE' | 'WHITELIST'; + address?: Address; + id?: string; + minAmount?: string; +}; + +// @public (undocumented) +export class AccessService { + // Warning: (ae-forgotten-export) The symbol "HttpClient" needs to be exported by the entry point index.d.ts + constructor(http: HttpClient, validateResponses?: boolean); + checkAccess(params: AccessCheckParams, options?: RequestOptions): Promise; + checkAccessBatch(items: AccessCheckParams[], options?: AccessCheckBatchOptions & RequestOptions): Promise; + checkRoleAccess(params: RoleAccessCheckParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export type Address = string; + +// @public +export const areAddressesEqual: (addr1: string, addr2: string) => boolean; + +// @public +export function assertValidResponse(value: unknown, guard: (value: unknown) => value is T, typeName: string): T; + +// @public (undocumented) +export interface CacheAdapter { + // (undocumented) + clear(): Promise; + // (undocumented) + delete(key: string): Promise; + deleteByPrefix?(prefix: string): Promise; + // (undocumented) + get(key: string): Promise; + // (undocumented) + set(key: string, value: T, ttl?: number): Promise; +} + +// @public +export const capitalise: (str: string) => string; + +// @public +export type ChainConfig = { + rpcUrl?: string; + contractAddress?: string; +}; + +// @public (undocumented) +export function connectWallet(provider: EIP1193Provider): Promise; + +// @public (undocumented) +export class ContractClient { + constructor(config: GuildPassClientConfig); + getChainConfig(chainId?: number): ChainConfig; + getGuildOwner(params: GuildOwnerParams): Promise; + getMembershipTokenBalance(params: TokenBalanceParams): Promise; + validateRoleRequirement(params: RoleRequirementParams): Promise; +} + +// @public (undocumented) +export const DEFAULT_CONFIG: Partial; + +// @public +export interface EIP1193Provider { + // (undocumented) + on?(eventName: string, listener: (...args: any[]) => void): void; + // (undocumented) + removeListener?(eventName: string, listener: (...args: any[]) => void): void; + // (undocumented) + request(args: { + method: string; + params?: any[]; + }): Promise; +} + +// @public +export const encodePathSegment: (segment: string) => string; + +// @public (undocumented) +export type ErrorHookPayload = RequestHookPayload & { + error: Error; + durationMs: number; +}; + +// @public (undocumented) +export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise; + +// @public +export const formatIsoDate: (date: string | number | Date) => string; + +// @public +export function getChainId(provider: EIP1193Provider): Promise; + +// @public (undocumented) +export type GetGuildParams = { + guildId: string; +}; + +// @public (undocumented) +export type GetRolesParams = { + guildId: string; +}; + +// @public (undocumented) +export type GetUserRolesParams = { + walletAddress: string; + guildId: string; +}; + +// @public (undocumented) +export type Guild = { + id: string; + name: string; + description?: string; + ownerAddress: string; + contractAddress?: string; + chainId: number; +}; + +// @public (undocumented) +export type GuildConfig = { + id: string; + theme?: string; + logoUrl?: string; + bannerUrl?: string; + socialLinks?: Record; +}; + +// @public (undocumented) +export type GuildOwnerParams = { + guildId: string; + chainId?: number; + contractAddress?: string; +}; + +// @public +export class GuildPassClient { + constructor(config: GuildPassClientConfig); + // (undocumented) + readonly access: AccessService; + clearCache(): Promise; + // (undocumented) + readonly contracts: ContractClient; + getConfig(): Omit; + // (undocumented) + readonly guilds: GuildsService; + invalidateGuildCache(guildId: string): Promise; + invalidateWalletCache(walletAddress: string): Promise; + // (undocumented) + readonly membership: MembershipService; + // (undocumented) + readonly roles: RolesService; +} + +// @public (undocumented) +export type GuildPassClientConfig = { + apiUrl: string; + chainId?: number; + rpcUrl?: string; + contractAddress?: string; + chains?: Record; + apiKey?: string; + timeoutMs?: number; + retry?: RetryConfig; + hooks?: HttpHooks; + fetch?: FetchLike; + validateResponses?: boolean; + cache?: CacheAdapter; + cacheTtl?: number; +}; + +// @public (undocumented) +export class GuildPassError extends Error { + constructor(message: string, code: GuildPassErrorCode, status?: number, details?: any); + // (undocumented) + readonly code: GuildPassErrorCode; + // (undocumented) + readonly details?: any; + // (undocumented) + static fromHttpError(status: number, details?: any): GuildPassError; + // (undocumented) + readonly status?: number; +} + +// @public (undocumented) +export enum GuildPassErrorCode { + // (undocumented) + ABORTED = "ABORTED", + // (undocumented) + CONFLICT = "CONFLICT", + // (undocumented) + HTTP_ERROR = "HTTP_ERROR", + // (undocumented) + INVALID_ADDRESS = "INVALID_ADDRESS", + // (undocumented) + INVALID_CONFIG = "INVALID_CONFIG", + // (undocumented) + INVALID_INPUT = "INVALID_INPUT", + // (undocumented) + INVALID_RESPONSE = "INVALID_RESPONSE", + // (undocumented) + MISSING_FETCH = "MISSING_FETCH", + // (undocumented) + NOT_FOUND = "NOT_FOUND", + // (undocumented) + NOT_IMPLEMENTED = "NOT_IMPLEMENTED", + // (undocumented) + RATE_LIMITED = "RATE_LIMITED", + // (undocumented) + REQUEST_CANCELLED = "REQUEST_CANCELLED", + // (undocumented) + SERVER_ERROR = "SERVER_ERROR", + // (undocumented) + TIMEOUT = "TIMEOUT", + // (undocumented) + UNAUTHORISED = "UNAUTHORISED", + // (undocumented) + UNKNOWN_ERROR = "UNKNOWN_ERROR" +} + +// @public (undocumented) +export type GuildRole = { + id: string; + name: string; + description?: string; + requirements?: AccessRequirement[]; +}; + +// @public (undocumented) +export class GuildsService { + constructor(http: HttpClient, validateResponses?: boolean); + getGuild(params: GetGuildParams, options?: RequestOptions): Promise; + getGuildConfig(params: GetGuildParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export function hasInjectedWallet(): boolean; + +// @public (undocumented) +export type HttpClientConfig = { + retry?: RetryConfig; + hooks?: HttpHooks; + fetch?: FetchLike; +}; + +// @public (undocumented) +export interface HttpHooks { + // (undocumented) + onError?: (payload: ErrorHookPayload) => void | Promise; + // (undocumented) + onRequest?: (payload: RequestHookPayload) => void | Promise; + // (undocumented) + onResponse?: (payload: ResponseHookPayload) => void | Promise; +} + +// @public (undocumented) +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; + +// @public (undocumented) +export type HttpRequestOptions = { + method?: HttpMethod; + headers?: Record; + body?: any; + params?: Record; + timeoutMs?: number; + retry?: RetryConfig; + signal?: AbortSignal; +}; + +// @public (undocumented) +export type HttpResponse = { + data: T; + status: number; + headers: Headers; +}; + +// @public +export class InMemoryCacheAdapter implements CacheAdapter { + // (undocumented) + clear(): Promise; + // (undocumented) + delete(key: string): Promise; + // (undocumented) + deleteByPrefix(prefix: string): Promise; + // (undocumented) + get(key: string): Promise; + // (undocumented) + set(key: string, value: T, ttl?: number): Promise; +} + +// @public +export function isAccessCheckResult(value: unknown): value is AccessCheckResult; + +// @public +export const isChecksumAddress: (address: string) => boolean; + +// @public (undocumented) +export function isGuild(value: unknown): value is Guild; + +// @public (undocumented) +export function isGuildConfig(value: unknown): value is GuildConfig; + +// @public (undocumented) +export function isGuildRole(value: unknown): value is GuildRole; + +// @public (undocumented) +export function isGuildRoleArray(value: unknown): value is GuildRole[]; + +// @public (undocumented) +export function isMembership(value: unknown): value is Membership; + +// @public (undocumented) +export type Membership = { + walletAddress: string; + guildId: string; + isActive: boolean; + roles: string[]; + joinedAt?: string; + expiresAt?: string; +}; + +// @public (undocumented) +export type MembershipParams = { + walletAddress: string; + guildId: string; +}; + +// @public (undocumented) +export class MembershipService { + constructor(http: HttpClient, validateResponses?: boolean); + getMembership(params: MembershipParams, options?: RequestOptions): Promise; + isMember(params: MembershipParams, options?: RequestOptions): Promise; +} + +// @public (undocumented) +export type NetworkConfig = { + chainId: number; + name: string; + rpcUrl?: string; + explorerUrl?: string; +}; + +// @public +export const normaliseAddress: (address: string) => string; + +// @public (undocumented) +export type RequestHookPayload = { + method: HttpMethod; + path: string; + headers: Record; +}; + +// @public (undocumented) +export type RequestOptions = { + timeoutMs?: number; + retry?: RetryConfig; +}; + +// @public +export function resolveChainConfig(config: GuildPassClientConfig, chainId: number): ChainConfig; + +// @public (undocumented) +export type ResponseHookPayload = RequestHookPayload & { + status: number; + durationMs: number; + responseHeaders: Record; +}; + +// @public (undocumented) +export type RetryConfig = { + maxRetries?: number; + baseDelayMs?: number; + maxDelayMs?: number; + retryableStatuses?: number[]; + allowMutatingRetry?: boolean; +}; + +// @public (undocumented) +export type RoleAccessCheckParams = { + walletAddress: string; + guildId: string; + roleId: string; +}; + +// @public (undocumented) +export type RoleRequirementParams = { + walletAddress: string; + requirement: AccessRequirement; +}; + +// @public (undocumented) +export class RolesService { + constructor(http: HttpClient, validateResponses?: boolean); + getRoles(params: GetRolesParams, options?: RequestOptions): Promise; + getUserRoles(params: GetUserRolesParams, options?: RequestOptions): Promise; +} + +// @public +export const shortenAddress: (address: string, chars?: number) => string; + +// @public (undocumented) +export const SUPPORTED_NETWORKS: Record; + +// @public (undocumented) +export function switchChain(provider: EIP1193Provider, chainId: string): Promise; + +// @public +export const toChecksumAddress: (address: string) => string; + +// @public (undocumented) +export type TokenBalanceParams = { + walletAddress: string; + chainId?: number; + contractAddress?: string; +}; + +// @public +export const validateAddress: (address: string, options?: { + strict?: boolean; +}) => void; + +// @public (undocumented) +export function validateConfig(config: GuildPassClientConfig): void; + +// @public +export const validateGuildId: (guildId: string) => void; + +// @public +export const validateResourceId: (resourceId: string) => void; + +// @public +export const validateRoleId: (roleId: string) => void; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/tests/compat/browser/browser.test.ts b/tests/compat/browser/browser.test.ts new file mode 100644 index 0000000..97ee482 --- /dev/null +++ b/tests/compat/browser/browser.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect, vi } from 'vitest'; +import { GuildPassClient } from '../../../src'; + +describe('GuildPass SDK - Browser (JSDOM) Compatibility', () => { + it('should initialize without Node.js globals', () => { + expect(() => new GuildPassClient({ apiUrl: 'https://api.guildpass.xyz' })).not.toThrow(); + }); + + it('should support a custom fetch transport', async () => { + const mockFetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ hasAccess: true }), + }); + + const originalFetch = globalThis.fetch; + globalThis.fetch = mockFetch; + + const client = new GuildPassClient({ apiUrl: 'https://api.guildpass.xyz' }); + const result = await client.access.checkAccess({ + walletAddress: '0x1234567890123456789012345678901234567890', + guildId: 'test-guild', + resourceId: 'test-resource', + }); + + expect(mockFetch).toHaveBeenCalled(); + expect(result.hasAccess).toBe(true); + + globalThis.fetch = originalFetch; + }); +}); \ No newline at end of file diff --git a/tests/compat/edge/edge.test.ts b/tests/compat/edge/edge.test.ts new file mode 100644 index 0000000..2e9eef0 --- /dev/null +++ b/tests/compat/edge/edge.test.ts @@ -0,0 +1,14 @@ +import { describe, it, expect } from 'vitest'; +import { GuildPassClient } from '../../../src'; + +describe('GuildPass SDK - Edge Runtime Compatibility', () => { + it('should correctly build and run in V8 edge constraints', () => { + const client = new GuildPassClient({ + apiUrl: 'https://api.guildpass.xyz', + chainId: 8453, + }); + + expect(client.access).toBeDefined(); + expect(typeof client.invalidateGuildCache).toBe('function'); + }); +}); \ No newline at end of file diff --git a/vitest.workspace.ts b/vitest.workspace.ts new file mode 100644 index 0000000..3485609 --- /dev/null +++ b/vitest.workspace.ts @@ -0,0 +1,26 @@ +import { defineWorkspace } from 'vitest/config'; + +export default defineWorkspace([ + { + test: { + name: 'node', + environment: 'node', + include: ['tests/**/*.test.ts'], + exclude: ['tests/compat/**'], + }, + }, + { + test: { + name: 'browser', + environment: 'jsdom', + include: ['tests/compat/browser/**/*.test.ts'], + }, + }, + { + test: { + name: 'edge', + environment: 'edge-runtime', + include: ['tests/compat/edge/**/*.test.ts'], + }, + }, +]); \ No newline at end of file