{"_id":"@ascnd-gg/client","_rev":"3-ec5be9c4a5e40b60c45915e0ed844761","name":"@ascnd-gg/client","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@ascnd-gg/client","version":"1.0.0","keywords":["ascnd","leaderboard","game","api","client"],"author":{"name":"Ascnd"},"license":"MIT","_id":"@ascnd-gg/client@1.0.0","maintainers":[{"name":"roguelite","email":"hello@roguelite.tech"}],"homepage":"https://ascnd.gg/docs/sdks/typescript","bugs":{"url":"https://github.com/ascnd-gg/ascnd-client-js/issues"},"dist":{"shasum":"bc52aab648713d7edc8ba7cd8c1583e2f35c260e","tarball":"https://registry.npmjs.org/@ascnd-gg/client/-/client-1.0.0.tgz","fileCount":9,"integrity":"sha512-Tn2N9rqMb3Fjy07545p0vcxjQL5wilvg9adIr7Oq63U+iMsutDelXDFHIOOBZo0Y2PSaK2MYRktpXuoapzlKkQ==","signatures":[{"sig":"MEUCIBcol6llEVpXEboQd6zDP28AhKZxcsIqCNYya1xyluZUAiEA4Do8JP2cdd5R7tb2oW85HVsMV+XgPqrELZD80mfW53U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":129910},"main":"./dist/index.cjs","type":"module","_from":"file:ascnd-gg-client-1.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup --watch","build":"npm run generate && tsup","release":"pnpm build && changeset publish","generate":"buf generate","typecheck":"tsc --noEmit"},"_npmUser":{"name":"roguelite","email":"hello@roguelite.tech"},"_resolved":"/tmp/1051e9f591d0f317b324307daa114634/ascnd-gg-client-1.0.0.tgz","_integrity":"sha512-Tn2N9rqMb3Fjy07545p0vcxjQL5wilvg9adIr7Oq63U+iMsutDelXDFHIOOBZo0Y2PSaK2MYRktpXuoapzlKkQ==","repository":{"url":"git+https://github.com/ascnd-gg/ascnd-client-js.git","type":"git"},"_npmVersion":"10.9.4","description":"TypeScript/JavaScript client for the Ascnd leaderboard API","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@bufbuild/protobuf":"^2.10.2","@connectrpc/connect":"^2.1.1","@connectrpc/connect-web":"^2.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.7.0","@bufbuild/buf":"^1.47.0","@changesets/cli":"^2.27.0","@bufbuild/protoc-gen-es":"^2.10.2","@changesets/changelog-github":"^0.5.0"},"_npmOperationalInternal":{"tmp":"tmp/client_1.0.0_1767405128153_0.8166745534135775","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@ascnd-gg/client","version":"1.1.0","keywords":["ascnd","leaderboard","game","api","client"],"author":{"name":"Ascnd"},"license":"MIT","_id":"@ascnd-gg/client@1.1.0","maintainers":[{"name":"roguelite","email":"hello@roguelite.tech"}],"homepage":"https://ascnd.gg/docs/sdks/typescript","bugs":{"url":"https://github.com/ascnd-gg/ascnd-client-js/issues"},"dist":{"shasum":"02b141a57449584c39e328174872d52a284312ed","tarball":"https://registry.npmjs.org/@ascnd-gg/client/-/client-1.1.0.tgz","fileCount":9,"integrity":"sha512-+eVlgsZhlDQGXULexFBCvbGuvjkZCeyAeAnDIPEBZ1x+6JkI0Eh2ef2NqY/aR77HzIzE2xiXAFupKB2JyOUA3A==","signatures":[{"sig":"MEQCIA3lMKG3DzhFDEsThA73nFmsIEfpZliF62oSA8CgP5F7AiACwCX90Fljc2buGumHsum2h0KcNawp08zaiba5LJuKnw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132965},"main":"./dist/index.cjs","type":"module","_from":"file:ascnd-gg-client-1.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup --watch","build":"npm run generate && tsup","release":"pnpm build && changeset publish","generate":"buf generate","typecheck":"tsc --noEmit"},"_npmUser":{"name":"roguelite","email":"hello@roguelite.tech"},"_resolved":"/tmp/0adc97fc9a20ded11346bff16d0a48ec/ascnd-gg-client-1.1.0.tgz","_integrity":"sha512-+eVlgsZhlDQGXULexFBCvbGuvjkZCeyAeAnDIPEBZ1x+6JkI0Eh2ef2NqY/aR77HzIzE2xiXAFupKB2JyOUA3A==","repository":{"url":"git+https://github.com/ascnd-gg/ascnd-client-js.git","type":"git"},"_npmVersion":"10.9.4","description":"TypeScript/JavaScript client for the Ascnd leaderboard API","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@bufbuild/protobuf":"^2.10.2","@connectrpc/connect":"^2.1.1","@connectrpc/connect-web":"^2.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.7.0","@bufbuild/buf":"^1.47.0","@changesets/cli":"^2.27.0","@bufbuild/protoc-gen-es":"^2.10.2","@changesets/changelog-github":"^0.5.0"},"_npmOperationalInternal":{"tmp":"tmp/client_1.1.0_1767955207018_0.8040178830146825","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@ascnd-gg/client","version":"1.1.1","description":"TypeScript/JavaScript client for the Ascnd leaderboard API","author":{"name":"Ascnd"},"license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"dependencies":{"@connectrpc/connect":"^2.1.1","@connectrpc/connect-web":"^2.1.1","@bufbuild/protobuf":"^2.10.2"},"devDependencies":{"@bufbuild/buf":"^1.47.0","@bufbuild/protoc-gen-es":"^2.10.2","@changesets/changelog-github":"^0.5.0","@changesets/cli":"^2.27.0","tsup":"^8.0.0","typescript":"^5.7.0"},"keywords":["ascnd","leaderboard","game","api","client"],"repository":{"type":"git","url":"git+https://github.com/ascnd-gg/ascnd-client-js.git"},"homepage":"https://ascnd.gg/docs/sdks/typescript","bugs":{"url":"https://github.com/ascnd-gg/ascnd-client-js/issues"},"engines":{"node":">=18.0.0"},"publishConfig":{"access":"public"},"scripts":{"generate":"buf generate","build":"npm run generate && tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","release":"pnpm build && changeset publish"},"_id":"@ascnd-gg/client@1.1.1","_integrity":"sha512-hlNqS76CUwZxvYt9L8Bte2S7y8lht4MTB+D1LEooccFJ3kuQ0C+vFqSJmFMyLjEzdmVnV5jFi12WAf23X/Eyug==","_resolved":"/tmp/1ea110260b63b91cfba5817e33d000cb/ascnd-gg-client-1.1.1.tgz","_from":"file:ascnd-gg-client-1.1.1.tgz","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-hlNqS76CUwZxvYt9L8Bte2S7y8lht4MTB+D1LEooccFJ3kuQ0C+vFqSJmFMyLjEzdmVnV5jFi12WAf23X/Eyug==","shasum":"ca3f55449f8808c613626013addfc29fa1aafadf","tarball":"https://registry.npmjs.org/@ascnd-gg/client/-/client-1.1.1.tgz","fileCount":9,"unpackedSize":132965,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGnwhL9h4ojXA5HJ3JZK8Kxi8135Dg1W8di2eseKOiP3AiATEdrGHVenQV4BBQ3q/RJCMf4+g5pjC0FRrvPNjrziIQ=="}]},"_npmUser":{"name":"roguelite","email":"hello@roguelite.tech"},"directories":{},"maintainers":[{"name":"roguelite","email":"hello@roguelite.tech"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client_1.1.1_1767959050327_0.0934590408428928"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-03T01:52:08.058Z","modified":"2026-01-09T11:44:10.737Z","1.0.0":"2026-01-03T01:52:08.344Z","1.1.0":"2026-01-09T10:40:07.179Z","1.1.1":"2026-01-09T11:44:10.481Z"},"bugs":{"url":"https://github.com/ascnd-gg/ascnd-client-js/issues"},"author":{"name":"Ascnd"},"license":"MIT","homepage":"https://ascnd.gg/docs/sdks/typescript","keywords":["ascnd","leaderboard","game","api","client"],"repository":{"type":"git","url":"git+https://github.com/ascnd-gg/ascnd-client-js.git"},"description":"TypeScript/JavaScript client for the Ascnd leaderboard API","maintainers":[{"name":"roguelite","email":"hello@roguelite.tech"}],"readme":"# @ascnd-gg/client\n\n> [!WARNING]\n> This project is under active development. Expect bugs. Please report issues via the [issue tracker](../../issues).\n\nTypeScript/JavaScript client library for the [Ascnd](https://ascnd.gg) leaderboard API. Uses gRPC-Web for efficient, type-safe communication.\n\n## Installation\n\n```bash\nnpm install @ascnd-gg/client\n# or\nyarn add @ascnd-gg/client\n# or\npnpm add @ascnd-gg/client\n```\n\n## Quick Start\n\n```typescript\nimport { AscndClient, create, SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n\nconst client = new AscndClient({\n  baseUrl: \"https://api.ascnd.gg\",\n  apiKey: \"your-api-key\",\n});\n\n// Submit a score\nconst result = await client.submitScore(\n  create(SubmitScoreRequestSchema, {\n    leaderboardId: \"high-scores\",\n    playerId: \"player-123\",\n    score: 1000n,\n  })\n);\n\nconsole.log(`Rank: #${result.rank}`);\n```\n\n## Examples\n\nStand-alone example projects are available in the [`examples/`](./examples) directory:\n\n| Example | Description |\n|---------|-------------|\n| [`submit-score`](./examples/submit-score) | Submit a score and display the resulting rank |\n| [`leaderboard`](./examples/leaderboard) | Fetch and display the top 10 leaderboard entries |\n| [`metadata-periods`](./examples/metadata-periods) | Submit scores with metadata and query by time period |\n\nEach example is a self-contained project. To run:\n\n```bash\ncd examples/submit-score\nnpm install\nexport ASCND_API_KEY=your_api_key\nexport LEADERBOARD_ID=your_leaderboard_id\nnpm start\n```\n\n## Usage\n\n### Creating a Client\n\n```typescript\nimport { AscndClient } from \"@ascnd-gg/client\";\n\nconst client = new AscndClient({\n  baseUrl: \"https://api.ascnd.gg\", // API base URL\n  apiKey: \"your-api-key\",          // Your API key\n  timeout: 30000,                  // Optional: request timeout in ms (default: 30000)\n});\n```\n\n### Submit a Score\n\nSubmit a player's score to a leaderboard:\n\n```typescript\nimport { create, SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n\nconst result = await client.submitScore(\n  create(SubmitScoreRequestSchema, {\n    leaderboardId: \"high-scores\",\n    playerId: \"player-123\",\n    score: 1000n,\n    metadata: new TextEncoder().encode(JSON.stringify({\n      level: 5,\n      character: \"warrior\",\n    })),\n    idempotencyKey: \"unique-key\",  // Optional: prevent duplicate submissions\n  })\n);\n\nconsole.log(`Score ID: ${result.scoreId}`);\nconsole.log(`Rank: #${result.rank}`);\nconsole.log(`New personal best: ${result.isNewBest}`);\nconsole.log(`Was deduplicated: ${result.wasDeduplicated}`);\n\n// Check anticheat results (if enabled)\nif (result.anticheat) {\n  console.log(`Anticheat passed: ${result.anticheat.passed}`);\n  if (!result.anticheat.passed) {\n    console.log(`Action taken: ${result.anticheat.action}`);\n    for (const violation of result.anticheat.violations) {\n      console.log(`  - ${violation.flagType}: ${violation.reason}`);\n    }\n  }\n}\n```\n\n### Get Leaderboard\n\nRetrieve the top scores for a leaderboard:\n\n```typescript\nimport { create, GetLeaderboardRequestSchema } from \"@ascnd-gg/client\";\n\nconst leaderboard = await client.getLeaderboard(\n  create(GetLeaderboardRequestSchema, {\n    leaderboardId: \"high-scores\",\n    limit: 10,                     // Optional: max entries (default: 10, max: 100)\n    offset: 0,                     // Optional: pagination offset\n    period: \"current\",             // Optional: \"current\", \"previous\", or timestamp\n    viewSlug: \"platform-pc\",       // Optional: filter by metadata view\n  })\n);\n\nconsole.log(`Total players: ${leaderboard.totalEntries}`);\nconsole.log(`Period: ${leaderboard.periodStart} - ${leaderboard.periodEnd}`);\n\nfor (const entry of leaderboard.entries) {\n  console.log(`#${entry.rank}: ${entry.playerId} - ${entry.score}`);\n  if (entry.bracket) {\n    console.log(`  Bracket: ${entry.bracket.name} (${entry.bracket.color})`);\n  }\n}\n\nif (leaderboard.hasMore) {\n  // Fetch next page with offset: 10\n}\n\n// View info (if filtering by viewSlug)\nif (leaderboard.view) {\n  console.log(`Viewing: ${leaderboard.view.name}`);\n}\n```\n\n### Get Player Rank\n\nGet a specific player's rank and score:\n\n```typescript\nimport { create, GetPlayerRankRequestSchema } from \"@ascnd-gg/client\";\n\nconst playerRank = await client.getPlayerRank(\n  create(GetPlayerRankRequestSchema, {\n    leaderboardId: \"high-scores\",\n    playerId: \"player-123\",\n    period: \"current\",             // Optional\n    viewSlug: \"platform-pc\",       // Optional: filter by metadata view\n  })\n);\n\nif (playerRank.rank !== undefined) {\n  console.log(`Rank: #${playerRank.rank}`);\n  console.log(`Score: ${playerRank.score}`);\n  console.log(`Best score: ${playerRank.bestScore}`);\n  console.log(`Percentile: ${playerRank.percentile}`);\n  console.log(`Total players: ${playerRank.totalEntries}`);\n\n  // Bracket info (if brackets are enabled)\n  if (playerRank.bracket) {\n    console.log(`Bracket: ${playerRank.bracket.name}`);\n  }\n\n  // Global rank when filtering by view\n  if (playerRank.globalRank !== undefined) {\n    console.log(`Global Rank: #${playerRank.globalRank}`);\n  }\n} else {\n  console.log(\"Player has no score on this leaderboard\");\n}\n```\n\n## Features\n\n### Anticheat\n\nWhen anticheat is enabled for a leaderboard, the `submitScore` response includes validation results:\n\n```typescript\nconst result = await client.submitScore(request);\n\nif (result.anticheat && !result.anticheat.passed) {\n  console.log(`Score flagged: ${result.anticheat.action}`);\n  // Possible actions: \"none\", \"flag\", \"shadow_ban\", \"reject\"\n\n  for (const violation of result.anticheat.violations) {\n    console.log(`  ${violation.flagType}: ${violation.reason}`);\n    // Flag types: \"bounds_exceeded\", \"velocity_exceeded\",\n    //             \"duplicate_idempotency\", \"missing_idempotency_key\"\n  }\n}\n```\n\n### Brackets\n\nLeaderboards with brackets enabled include tier information for each entry:\n\n```typescript\nconst leaderboard = await client.getLeaderboard(request);\n\nfor (const entry of leaderboard.entries) {\n  if (entry.bracket) {\n    console.log(`${entry.playerId} is in ${entry.bracket.name}`);\n    // entry.bracket.color contains hex color code like \"#FFD700\"\n  }\n}\n```\n\n### Metadata Views\n\nFilter leaderboards by metadata views to create segmented rankings:\n\n```typescript\nimport { create, GetLeaderboardRequestSchema } from \"@ascnd-gg/client\";\n\n// Get PC-only leaderboard\nconst pcLeaderboard = await client.getLeaderboard(\n  create(GetLeaderboardRequestSchema, {\n    leaderboardId: \"high-scores\",\n    viewSlug: \"platform-pc\",\n  })\n);\n\n// Rankings are within the view\n// Use globalRank in GetPlayerRank to see overall position\n```\n\n## Error Handling\n\nThe client throws `AscndError` for API errors:\n\n```typescript\nimport { AscndClient, AscndError } from \"@ascnd-gg/client\";\n\ntry {\n  await client.submitScore(request);\n} catch (error) {\n  if (error instanceof AscndError) {\n    console.error(`API Error: ${error.message}`);\n    console.error(`Code: ${error.code}`);\n    console.error(`Details:`, error.details);\n  } else {\n    throw error;\n  }\n}\n```\n\n## API Reference\n\n### AscndClient\n\n#### Constructor\n\n```typescript\nnew AscndClient(config: AscndClientConfig)\n```\n\n| Option | Type | Required | Description |\n|--------|------|----------|-------------|\n| `baseUrl` | `string` | Yes | The base URL of the Ascnd API |\n| `apiKey` | `string` | Yes | Your API key for authentication |\n| `timeout` | `number` | No | Request timeout in milliseconds (default: 30000) |\n\n#### Methods\n\n##### `submitScore(request: SubmitScoreRequest): Promise<SubmitScoreResponse>`\n\nSubmit a player's score to a leaderboard.\n\n##### `getLeaderboard(request: GetLeaderboardRequest): Promise<GetLeaderboardResponse>`\n\nRetrieve the top scores for a leaderboard.\n\n##### `getPlayerRank(request: GetPlayerRankRequest): Promise<GetPlayerRankResponse>`\n\nGet a specific player's rank and score.\n\n### Message Creation\n\nUse `create()` with schemas to create properly typed request messages:\n\n```typescript\nimport { create, SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n\nconst request = create(SubmitScoreRequestSchema, {\n  leaderboardId: \"high-scores\",\n  playerId: \"player-123\",\n  score: 1000n, // Note: scores are bigint\n});\n```\n\n## Requirements\n\n- Node.js 18+ (for native fetch support)\n- Or any runtime with `fetch` available (browsers, Deno, Bun, Cloudflare Workers, etc.)\n\n## Links\n\n- [Documentation](https://docs.ascnd.gg/sdks/typescript)\n- [GitHub](https://github.com/ascnd-gg/ascnd-client-js)\n- [npm](https://www.npmjs.com/package/@ascnd-gg/client)\n\n## License\n\nMIT\n","readmeFilename":"README.md"}