fix(ts-sdk): await identity before building project-scoped URLs (#6802)

This commit is contained in:
pratik
2026-08-04 16:31:18 -07:00
committed by GitHub
parent 1112be3e5e
commit fad0e0e415
2 changed files with 251 additions and 3 deletions
+23 -3
View File
@@ -81,6 +81,8 @@ class APIError extends Error {
interface ClientOptions {
apiKey: string;
host?: string;
/** Max cached identities per process. Defaults to 50. */
identityCacheMax?: number;
}
interface ClientIdentity {
@@ -90,7 +92,7 @@ interface ClientIdentity {
}
// Shares one ping per (host, api key) across clients; FIFO-capped.
const IDENTITY_CACHE_MAX = 50;
const IDENTITY_CACHE_MAX_DEFAULT = 50;
const identityByCredentials = new Map<string, Promise<ClientIdentity>>();
export default class MemoryClient {
@@ -102,6 +104,7 @@ export default class MemoryClient {
client: any;
telemetryId: string;
private initialized: Promise<void>;
private identityCacheMax: number;
_validateApiKey(): any {
if (!this.apiKey) {
@@ -120,6 +123,8 @@ export default class MemoryClient {
this.host = options.host || "https://api.mem0.ai";
this.organizationId = null;
this.projectId = null;
this.identityCacheMax =
options.identityCacheMax ?? IDENTITY_CACHE_MAX_DEFAULT;
this.headers = {
Authorization: `Token ${this.apiKey}`,
@@ -136,7 +141,7 @@ export default class MemoryClient {
this.telemetryId = "";
// Requests never wait on this; only telemetry does.
// Memory requests never wait on this; telemetry and _awaitIdentity do.
this.initialized = this._resolveIdentity();
}
@@ -146,7 +151,7 @@ export default class MemoryClient {
let shared = identityByCredentials.get(credentials);
if (!shared) {
shared = this._initializeClient();
if (identityByCredentials.size >= IDENTITY_CACHE_MAX) {
if (identityByCredentials.size >= this.identityCacheMax) {
identityByCredentials.delete(
identityByCredentials.keys().next().value!,
);
@@ -165,6 +170,11 @@ export default class MemoryClient {
});
}
// Blocks until the ping has populated organizationId/projectId.
private async _awaitIdentity(): Promise<void> {
await this.initialized;
}
private async _initializeClient(): Promise<ClientIdentity> {
try {
await this.ping();
@@ -616,6 +626,7 @@ export default class MemoryClient {
const payloadKeys = Object.keys(options || {});
this._captureEvent("get_project", [payloadKeys]);
const { fields } = options;
await this._awaitIdentity();
if (!(this.organizationId && this.projectId)) {
throw new Error(
@@ -639,6 +650,7 @@ export default class MemoryClient {
prompts: PromptUpdatePayload,
): Promise<Record<string, any>> {
this._captureEvent("update_project", []);
await this._awaitIdentity();
if (!(this.organizationId && this.projectId)) {
throw new Error(
"organizationId and projectId must be set to update instructions or categories",
@@ -659,7 +671,11 @@ export default class MemoryClient {
// WebHooks
async getWebhooks(data?: { projectId?: string }): Promise<Array<Webhook>> {
this._captureEvent("get_webhooks", []);
if (!data?.projectId) await this._awaitIdentity();
const project_id = data?.projectId || this.projectId;
if (!project_id) {
throw new Error("projectId must be set to access webhooks");
}
const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/projects/${project_id}/`,
{
@@ -671,6 +687,10 @@ export default class MemoryClient {
async createWebhook(webhook: WebhookCreatePayload): Promise<Webhook> {
this._captureEvent("create_webhook", []);
await this._awaitIdentity();
if (!this.projectId) {
throw new Error("projectId must be set to create a webhook");
}
const body = {
name: webhook.name,
url: webhook.url,