@nevermined-io/payments 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"organizations-api.d.ts","sourceRoot":"","sources":["../../../src/api/organizations-api/organizations-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAQrD,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACZ,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACrB,MAAM,YAAY,CAAA;AAEnB,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB;IAI7D;;;;;;OAMG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,sBAAsB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAqB9B;;;;;;;;;;;;;;;;OAgBG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAoCzE;;;;;;;OAOG;IACG,UAAU,CACd,IAAI,CAAC,EAAE,sBAAsB,EAC7B,QAAQ,CAAC,EAAE,OAAO,EAClB,IAAI,SAAI,EACR,MAAM,SAAM,GACX,OAAO,CAAC,2BAA2B,CAAC;IAuBvC;;;;;;;;;;;;;;;;;OAiBG;IACG,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAYjD;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,uBAAuB,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,wBAAwB,CAAC;IAiCpC;;;;;;;OAOG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC;CAuBjC"}
1
+ {"version":3,"file":"organizations-api.d.ts","sourceRoot":"","sources":["../../../src/api/organizations-api/organizations-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAQrD,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACZ,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACrB,MAAM,YAAY,CAAA;AAEnB,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB;IAI7D;;;;;;OAMG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,sBAAsB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAqB9B;;;;;;;;;;;;;;;;OAgBG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAwCzE;;;;;;;OAOG;IACG,UAAU,CACd,IAAI,CAAC,EAAE,sBAAsB,EAC7B,QAAQ,CAAC,EAAE,OAAO,EAClB,IAAI,SAAI,EACR,MAAM,SAAM,GACX,OAAO,CAAC,2BAA2B,CAAC;IAuBvC;;;;;;;;;;;;;;;;;OAiBG;IACG,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAYjD;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,uBAAuB,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,wBAAwB,CAAC;IAiCpC;;;;;;;OAOG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC;CAuBjC"}
@@ -65,19 +65,23 @@ export class OrganizationsAPI extends BasePaymentsAPI {
65
65
  if (response.status === 202 || wallet.consentRequired) {
66
66
  return { consentRequired: true };
67
67
  }
68
- // New account or the org's returning customer: a usable key MUST be present.
69
- // A 2xx without one means a partial/unexpected payload — fail loudly rather
70
- // than hand back a "success" carrying undefined credentials.
71
- if (!wallet.nvmApiKey) {
68
+ // New account or the org's returning customer: the Bearer MUST be present.
69
+ // The backend returns the usable credential as `walletResult.hash`
70
+ // (`<env>:<JWT>`) — the sibling `nvmApiKey` is the encrypted server-side
71
+ // blob and is NOT a valid Bearer. Surface `hash` as `nvmApiKey`, exactly as
72
+ // createMember does. A 2xx without a hash means a partial/unexpected payload
73
+ // — fail loudly rather than hand back a "success" carrying no credential.
74
+ if (!wallet.hash) {
72
75
  throw PaymentsError.internal('Customer onboarding did not return an API key (unexpected backend response)');
73
76
  }
74
77
  return {
75
78
  consentRequired: false,
76
- nvmApiKey: wallet.nvmApiKey,
79
+ nvmApiKey: wallet.hash,
77
80
  userId: wallet.userId,
78
81
  userWallet: wallet.userWallet,
79
82
  isCustomer: wallet.isCustomer,
80
83
  customerRecorded: wallet.customerRecorded,
84
+ expiresAt: wallet.expiresAt,
81
85
  };
82
86
  }
83
87
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"organizations-api.js","sourceRoot":"","sources":["../../../src/api/organizations-api/organizations-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { PaymentsError } from '../../common/payments.error.js'\nimport { PaymentOptions } from '../../common/types.js'\nimport { BasePaymentsAPI } from '../base-payments.js'\nimport {\n API_URL_CONNECT_STRIPE_ACCOUNT,\n API_URL_CREATE_USER,\n API_URL_GET_MEMBERS,\n API_URL_MY_MEMBERSHIPS,\n API_URL_ORG_ACTIVITY,\n} from '../nvm-api.js'\nimport {\n CreateUserResponse,\n CustomerOnboardingResponse,\n MyMembership,\n OrganizationActivityFilters,\n OrganizationActivityPage,\n OrganizationMemberRole,\n OrganizationMembersResponse,\n StripeCheckoutResult,\n} from './types.js'\n\nexport class OrganizationsAPI extends BasePaymentsAPI {\n static getInstance(options: PaymentOptions): OrganizationsAPI {\n return new OrganizationsAPI(options)\n }\n\n /**\n * Create a new member in the organization\n * @param userId - The unique external ID of the new member\n * @param userEmail - The email of the new member\n * @param userRole - The role of the new member\n * @returns The created member\n */\n async createMember(\n userId: string,\n userEmail?: string,\n userRole?: OrganizationMemberRole,\n ): Promise<CreateUserResponse> {\n const body = {\n uniqueExternalId: userId,\n email: userEmail,\n role: userRole,\n }\n const options = this.getBackendHTTPOptions('POST', body)\n const url = new URL(API_URL_CREATE_USER, this.environment.backend)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to create user', error)\n }\n\n const data = await response.json()\n return {\n ...data.walletResult,\n nvmApiKey: data.walletResult.hash,\n }\n }\n\n /**\n * Onboard a **white-label customer** into the organization (#2418).\n *\n * Provisions a Nevermined account for `email` under the caller's organization\n * without consuming a member seat, and returns a usable, scoped NVM API key\n * the org can use to transparently act on the customer's behalf (purchase\n * plans / redeem credits). The customer is recorded in the org's Customers\n * list.\n *\n * If the email already belongs to an account the org does NOT own, no key is\n * issued: an email challenge is sent to the owner and the result carries\n * `consentRequired: true`. Call again once the owner has consented to complete\n * onboarding.\n *\n * @param email - The customer's email address.\n * @returns The onboarding result — either `{ nvmApiKey, userId, userWallet, isCustomer, customerRecorded }` or `{ consentRequired: true }`.\n */\n async onboardCustomer(email: string): Promise<CustomerOnboardingResponse> {\n const body = { email, as: 'customer' }\n const options = this.getBackendHTTPOptions('POST', body)\n const url = new URL(API_URL_CREATE_USER, this.environment.backend)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to onboard customer', error)\n }\n\n const wallet = (await response.json())?.walletResult ?? {}\n // Existing, non-owned account: opaque consent-pending outcome. The backend\n // signals it with HTTP 202 and `walletResult.consentRequired`; treat either\n // as authoritative so a body-parsing quirk can't slip through as \"success\".\n // No identity or key is disclosed.\n if (response.status === 202 || wallet.consentRequired) {\n return { consentRequired: true }\n }\n // New account or the org's returning customer: a usable key MUST be present.\n // A 2xx without one means a partial/unexpected payload — fail loudly rather\n // than hand back a \"success\" carrying undefined credentials.\n if (!wallet.nvmApiKey) {\n throw PaymentsError.internal(\n 'Customer onboarding did not return an API key (unexpected backend response)',\n )\n }\n return {\n consentRequired: false,\n nvmApiKey: wallet.nvmApiKey,\n userId: wallet.userId,\n userWallet: wallet.userWallet,\n isCustomer: wallet.isCustomer,\n customerRecorded: wallet.customerRecorded,\n }\n }\n\n /**\n *\n * @param role - The role of the members to get\n * @param isActive - The active status of the members to get\n * @param page - The page of the members to get\n * @param offset - The number of members to get per page\n * @returns The list of members\n */\n async getMembers(\n role?: OrganizationMemberRole,\n isActive?: boolean,\n page = 1,\n offset = 100,\n ): Promise<OrganizationMembersResponse> {\n const body = {\n role,\n isActive,\n page,\n offset,\n }\n const url = new URL(API_URL_GET_MEMBERS, this.environment.backend)\n const options = this.getBackendHTTPOptions('POST', body)\n\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to get members', error)\n }\n\n const data = await response.json()\n return {\n members: data.members,\n total: data.totalResults,\n }\n }\n\n /**\n * Lists every organization the authenticated user is an active member of,\n * with their role and the organization's tier.\n *\n * Powers workspace pickers in third-party tools built on the SDK, and the\n * \"where will this publish?\" UX when a user belongs to multiple orgs.\n *\n * @returns An array of {@link MyMembership} — empty when the user has no\n * active memberships and operates as a personal account.\n *\n * @example\n * ```ts\n * const memberships = await payments.organizations.getMyMemberships()\n * for (const m of memberships) {\n * console.log(`${m.orgName} — ${m.role}`)\n * }\n * ```\n */\n async getMyMemberships(): Promise<MyMembership[]> {\n const url = new URL(API_URL_MY_MEMBERSHIPS, this.environment.backend)\n const options = this.getBackendHTTPOptions('GET')\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to fetch memberships', error)\n }\n const data = (await response.json()) as MyMembership[]\n return Array.isArray(data) ? data : []\n }\n\n /**\n * Lists events emitted into the activity feed of an organization the\n * caller is an active member of (member invites, customer events,\n * subscription transitions, webhook deliveries, …).\n *\n * Requires the caller to be a Member or Admin of `orgId`; the backend\n * returns 403 otherwise. Premium+ entitlement is enforced server-side.\n *\n * @param orgId - The organization id (e.g. `org-…`) whose feed to read.\n * @param filters - Optional filter set — see {@link OrganizationActivityFilters}.\n * @returns A paginated {@link OrganizationActivityPage}.\n *\n * @example\n * ```ts\n * const page = await payments.organizations.getOrganizationActivity(orgId, {\n * eventType: OrganizationActivityEventType.MemberInvited,\n * page: 1,\n * limit: 25,\n * })\n * ```\n */\n async getOrganizationActivity(\n orgId: string,\n filters: OrganizationActivityFilters = {},\n ): Promise<OrganizationActivityPage> {\n if (!orgId) {\n throw new PaymentsError('orgId is required')\n }\n const params = new URLSearchParams()\n if (filters.eventType !== undefined) {\n const eventType = Array.isArray(filters.eventType)\n ? filters.eventType.map(String).join(',')\n : String(filters.eventType)\n if (eventType.length > 0) params.set('eventType', eventType)\n }\n if (filters.actorUserId) params.set('actorUserId', filters.actorUserId)\n if (filters.from) params.set('from', filters.from)\n if (filters.to) params.set('to', filters.to)\n if (filters.page !== undefined) params.set('page', String(filters.page))\n if (filters.limit !== undefined) params.set('limit', String(filters.limit))\n\n const path = API_URL_ORG_ACTIVITY.replace(':orgId', encodeURIComponent(orgId))\n const queryString = params.toString()\n const url = new URL(queryString ? `${path}?${queryString}` : path, this.environment.backend)\n const options = this.getBackendHTTPOptions('GET')\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to fetch organization activity', error)\n }\n const data = (await response.json()) as Partial<OrganizationActivityPage>\n return {\n items: Array.isArray(data.items) ? data.items : [],\n total: typeof data.total === 'number' ? data.total : 0,\n }\n }\n\n /**\n * Connect user with Stripe\n * @param userEmail - The email of the user\n * @param userCountryCode - The country code of the user\n * @param returnUrl - The return URL after the Stripe connection is completed\n * @returns The Stripe checkout result including the stripe account link, stripe account id, user id, user country code, link created at and link expires at.\n * The stripe account link is the link that the user needs to click to connect with Stripe.\n */\n async connectStripeAccount(\n userEmail: string,\n userCountryCode: string,\n returnUrl: string,\n ): Promise<StripeCheckoutResult> {\n const body = {\n userEmail,\n userCountryCode,\n returnUrl,\n }\n const url = new URL(API_URL_CONNECT_STRIPE_ACCOUNT, this.environment.backend)\n const options = this.getBackendHTTPOptions('POST', body)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to connect with Stripe', error)\n }\n const data = await response.json()\n return {\n stripeAccountLink: data.stripeAccountLink,\n stripeAccountId: data.stripeAccountId,\n userId: data.userId,\n userCountryCode: data.userCountryCode,\n linkCreatedAt: data.linkCreatedAt,\n linkExpiresAt: data.linkExpiresAt,\n }\n }\n}\n"]}
1
+ {"version":3,"file":"organizations-api.js","sourceRoot":"","sources":["../../../src/api/organizations-api/organizations-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { PaymentsError } from '../../common/payments.error.js'\nimport { PaymentOptions } from '../../common/types.js'\nimport { BasePaymentsAPI } from '../base-payments.js'\nimport {\n API_URL_CONNECT_STRIPE_ACCOUNT,\n API_URL_CREATE_USER,\n API_URL_GET_MEMBERS,\n API_URL_MY_MEMBERSHIPS,\n API_URL_ORG_ACTIVITY,\n} from '../nvm-api.js'\nimport {\n CreateUserResponse,\n CustomerOnboardingResponse,\n MyMembership,\n OrganizationActivityFilters,\n OrganizationActivityPage,\n OrganizationMemberRole,\n OrganizationMembersResponse,\n StripeCheckoutResult,\n} from './types.js'\n\nexport class OrganizationsAPI extends BasePaymentsAPI {\n static getInstance(options: PaymentOptions): OrganizationsAPI {\n return new OrganizationsAPI(options)\n }\n\n /**\n * Create a new member in the organization\n * @param userId - The unique external ID of the new member\n * @param userEmail - The email of the new member\n * @param userRole - The role of the new member\n * @returns The created member\n */\n async createMember(\n userId: string,\n userEmail?: string,\n userRole?: OrganizationMemberRole,\n ): Promise<CreateUserResponse> {\n const body = {\n uniqueExternalId: userId,\n email: userEmail,\n role: userRole,\n }\n const options = this.getBackendHTTPOptions('POST', body)\n const url = new URL(API_URL_CREATE_USER, this.environment.backend)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to create user', error)\n }\n\n const data = await response.json()\n return {\n ...data.walletResult,\n nvmApiKey: data.walletResult.hash,\n }\n }\n\n /**\n * Onboard a **white-label customer** into the organization (#2418).\n *\n * Provisions a Nevermined account for `email` under the caller's organization\n * without consuming a member seat, and returns a usable, scoped NVM API key\n * the org can use to transparently act on the customer's behalf (purchase\n * plans / redeem credits). The customer is recorded in the org's Customers\n * list.\n *\n * If the email already belongs to an account the org does NOT own, no key is\n * issued: an email challenge is sent to the owner and the result carries\n * `consentRequired: true`. Call again once the owner has consented to complete\n * onboarding.\n *\n * @param email - The customer's email address.\n * @returns The onboarding result — either `{ nvmApiKey, userId, userWallet, isCustomer, customerRecorded }` or `{ consentRequired: true }`.\n */\n async onboardCustomer(email: string): Promise<CustomerOnboardingResponse> {\n const body = { email, as: 'customer' }\n const options = this.getBackendHTTPOptions('POST', body)\n const url = new URL(API_URL_CREATE_USER, this.environment.backend)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to onboard customer', error)\n }\n\n const wallet = (await response.json())?.walletResult ?? {}\n // Existing, non-owned account: opaque consent-pending outcome. The backend\n // signals it with HTTP 202 and `walletResult.consentRequired`; treat either\n // as authoritative so a body-parsing quirk can't slip through as \"success\".\n // No identity or key is disclosed.\n if (response.status === 202 || wallet.consentRequired) {\n return { consentRequired: true }\n }\n // New account or the org's returning customer: the Bearer MUST be present.\n // The backend returns the usable credential as `walletResult.hash`\n // (`<env>:<JWT>`) — the sibling `nvmApiKey` is the encrypted server-side\n // blob and is NOT a valid Bearer. Surface `hash` as `nvmApiKey`, exactly as\n // createMember does. A 2xx without a hash means a partial/unexpected payload\n // — fail loudly rather than hand back a \"success\" carrying no credential.\n if (!wallet.hash) {\n throw PaymentsError.internal(\n 'Customer onboarding did not return an API key (unexpected backend response)',\n )\n }\n return {\n consentRequired: false,\n nvmApiKey: wallet.hash,\n userId: wallet.userId,\n userWallet: wallet.userWallet,\n isCustomer: wallet.isCustomer,\n customerRecorded: wallet.customerRecorded,\n expiresAt: wallet.expiresAt,\n }\n }\n\n /**\n *\n * @param role - The role of the members to get\n * @param isActive - The active status of the members to get\n * @param page - The page of the members to get\n * @param offset - The number of members to get per page\n * @returns The list of members\n */\n async getMembers(\n role?: OrganizationMemberRole,\n isActive?: boolean,\n page = 1,\n offset = 100,\n ): Promise<OrganizationMembersResponse> {\n const body = {\n role,\n isActive,\n page,\n offset,\n }\n const url = new URL(API_URL_GET_MEMBERS, this.environment.backend)\n const options = this.getBackendHTTPOptions('POST', body)\n\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to get members', error)\n }\n\n const data = await response.json()\n return {\n members: data.members,\n total: data.totalResults,\n }\n }\n\n /**\n * Lists every organization the authenticated user is an active member of,\n * with their role and the organization's tier.\n *\n * Powers workspace pickers in third-party tools built on the SDK, and the\n * \"where will this publish?\" UX when a user belongs to multiple orgs.\n *\n * @returns An array of {@link MyMembership} — empty when the user has no\n * active memberships and operates as a personal account.\n *\n * @example\n * ```ts\n * const memberships = await payments.organizations.getMyMemberships()\n * for (const m of memberships) {\n * console.log(`${m.orgName} — ${m.role}`)\n * }\n * ```\n */\n async getMyMemberships(): Promise<MyMembership[]> {\n const url = new URL(API_URL_MY_MEMBERSHIPS, this.environment.backend)\n const options = this.getBackendHTTPOptions('GET')\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to fetch memberships', error)\n }\n const data = (await response.json()) as MyMembership[]\n return Array.isArray(data) ? data : []\n }\n\n /**\n * Lists events emitted into the activity feed of an organization the\n * caller is an active member of (member invites, customer events,\n * subscription transitions, webhook deliveries, …).\n *\n * Requires the caller to be a Member or Admin of `orgId`; the backend\n * returns 403 otherwise. Premium+ entitlement is enforced server-side.\n *\n * @param orgId - The organization id (e.g. `org-…`) whose feed to read.\n * @param filters - Optional filter set — see {@link OrganizationActivityFilters}.\n * @returns A paginated {@link OrganizationActivityPage}.\n *\n * @example\n * ```ts\n * const page = await payments.organizations.getOrganizationActivity(orgId, {\n * eventType: OrganizationActivityEventType.MemberInvited,\n * page: 1,\n * limit: 25,\n * })\n * ```\n */\n async getOrganizationActivity(\n orgId: string,\n filters: OrganizationActivityFilters = {},\n ): Promise<OrganizationActivityPage> {\n if (!orgId) {\n throw new PaymentsError('orgId is required')\n }\n const params = new URLSearchParams()\n if (filters.eventType !== undefined) {\n const eventType = Array.isArray(filters.eventType)\n ? filters.eventType.map(String).join(',')\n : String(filters.eventType)\n if (eventType.length > 0) params.set('eventType', eventType)\n }\n if (filters.actorUserId) params.set('actorUserId', filters.actorUserId)\n if (filters.from) params.set('from', filters.from)\n if (filters.to) params.set('to', filters.to)\n if (filters.page !== undefined) params.set('page', String(filters.page))\n if (filters.limit !== undefined) params.set('limit', String(filters.limit))\n\n const path = API_URL_ORG_ACTIVITY.replace(':orgId', encodeURIComponent(orgId))\n const queryString = params.toString()\n const url = new URL(queryString ? `${path}?${queryString}` : path, this.environment.backend)\n const options = this.getBackendHTTPOptions('GET')\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to fetch organization activity', error)\n }\n const data = (await response.json()) as Partial<OrganizationActivityPage>\n return {\n items: Array.isArray(data.items) ? data.items : [],\n total: typeof data.total === 'number' ? data.total : 0,\n }\n }\n\n /**\n * Connect user with Stripe\n * @param userEmail - The email of the user\n * @param userCountryCode - The country code of the user\n * @param returnUrl - The return URL after the Stripe connection is completed\n * @returns The Stripe checkout result including the stripe account link, stripe account id, user id, user country code, link created at and link expires at.\n * The stripe account link is the link that the user needs to click to connect with Stripe.\n */\n async connectStripeAccount(\n userEmail: string,\n userCountryCode: string,\n returnUrl: string,\n ): Promise<StripeCheckoutResult> {\n const body = {\n userEmail,\n userCountryCode,\n returnUrl,\n }\n const url = new URL(API_URL_CONNECT_STRIPE_ACCOUNT, this.environment.backend)\n const options = this.getBackendHTTPOptions('POST', body)\n const response = await fetch(url, options)\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Unknown error' }))\n throw PaymentsError.fromBackend('Unable to connect with Stripe', error)\n }\n const data = await response.json()\n return {\n stripeAccountLink: data.stripeAccountLink,\n stripeAccountId: data.stripeAccountId,\n userId: data.userId,\n userCountryCode: data.userCountryCode,\n linkCreatedAt: data.linkCreatedAt,\n linkExpiresAt: data.linkExpiresAt,\n }\n }\n}\n"]}
@@ -52,6 +52,12 @@ export type CustomerOnboardingResponse = {
52
52
  isCustomer: boolean;
53
53
  /** Whether the customer was written to the org's Customers list (best-effort). */
54
54
  customerRecorded: boolean;
55
+ /**
56
+ * ISO-8601 expiry of the credential (#2924). Track it and re-onboard to
57
+ * refresh before it lapses. Optional: returned for the credential outcome,
58
+ * but an older backend may omit it.
59
+ */
60
+ expiresAt?: string;
55
61
  };
56
62
  /**
57
63
  * A single organization the authenticated user is an active member of.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/api/organizations-api/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/api/organizations-api/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/api/organizations-api/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,sBAIX;AAJD,WAAY,sBAAsB;IAChC,yCAAe,CAAA;IACf,2CAAiB,CAAA;IACjB,2CAAiB,CAAA;AACnB,CAAC,EAJW,sBAAsB,KAAtB,sBAAsB,QAIjC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,uCAAmB,CAAA;IACnB,6CAAyB,CAAA;IACzB,qCAAiB,CAAA;IACjB,mCAAe,CAAA;AACjB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AAkED;;;;;GAKG;AACH,MAAM,CAAN,IAAY,6BA0BX;AA1BD,WAAY,6BAA6B;IACvC,uBAAuB;IACvB,iEAAgC,CAAA;IAChC,+DAA8B,CAAA;IAC9B,0EAAyC,CAAA;IACzC,yEAAwC,CAAA;IACxC,yEAAwC,CAAA;IACxC,iEAAgC,CAAA;IAChC,yEAAwC,CAAA;IACxC,yEAAwC,CAAA;IACxC,qBAAqB;IACrB,+DAA8B,CAAA;IAC9B,6DAA4B,CAAA;IAC5B,iEAAgC,CAAA;IAChC,qBAAqB;IACrB,iEAAgC,CAAA;IAChC,qEAAoC,CAAA;IACpC,yEAAwC,CAAA;IACxC,yBAAyB;IACzB,+EAA8C,CAAA;IAC9C,mFAAkD,CAAA;IAClD,+EAA8C,CAAA;IAC9C,2EAA0C,CAAA;IAC1C,mBAAmB;IACnB,uEAAsC,CAAA;IACtC,iEAAgC,CAAA;AAClC,CAAC,EA1BW,6BAA6B,KAA7B,6BAA6B,QA0BxC","sourcesContent":["export enum OrganizationMemberRole {\n Admin = 'Admin',\n Member = 'Member',\n Client = 'Client',\n}\n\n/**\n * Tier of an organization. Mirrors the backend `OrganizationType` enum at\n * `libs/commons/src/lib/types/types.ts` in nvm-monorepo.\n *\n * - `Premium` / `Enterprise` — the paid tiers from Epic #1339.\n * - `Lapsed` — an org whose paid subscription has expired; replaces the\n * retired `Free` bucket (the backend never emits `Free` for new rows).\n * - `Other` — legacy pre-tiered-pricing bucket still returned for orgs that\n * pre-date the tier system; inherits Premium-equivalent caps and features\n * server-side.\n */\nexport enum OrganizationType {\n Premium = 'Premium',\n Enterprise = 'Enterprise',\n Lapsed = 'Lapsed',\n Other = 'Other',\n}\n\nexport type CreateUserResponse = {\n nvmApiKey: string\n userId: string\n userWallet: string\n alreadyMember: boolean\n}\n\n/**\n * Result of onboarding a white-label customer via\n * `POST /organizations/account` with `as: 'customer'` (nvm-monorepo #2418).\n *\n * A discriminated union on `consentRequired` — narrow on it to get the right\n * shape with no impossible states:\n * - `{ consentRequired: true }` — the email belongs to an account the org does\n * NOT own. NO key is issued (HTTP 202); an email challenge was sent to the\n * owner and the account's identity is deliberately withheld. Retry once they\n * consent.\n * - `{ consentRequired: false, nvmApiKey, ... }` — a new account or the org's\n * returning customer. A usable, scoped `nvmApiKey` is always present and the\n * customer is recorded in the org's Customers list.\n */\nexport type CustomerOnboardingResponse =\n | { consentRequired: true }\n | {\n consentRequired: false\n /** The usable NVM API key (Bearer credential). Always present in this branch. */\n nvmApiKey: string\n userId: string\n userWallet: string\n /** True when a key was issued for a customer (new account or renewal). */\n isCustomer: boolean\n /** Whether the customer was written to the org's Customers list (best-effort). */\n customerRecorded: boolean\n }\n\n/**\n * A single organization the authenticated user is an active member of.\n * Returned by `OrganizationsAPI.getMyMemberships()` and used by clients to\n * power workspace pickers and \"where will this publish?\" UX.\n *\n * Shape mirrors `MyMembershipDto` in the Nevermined backend\n * (`apps/api/src/organizations/dto/my-membership.dto.ts`).\n */\nexport type MyMembership = {\n /** Stable organization id (e.g. `org-…`). */\n orgId: string\n /** Display name of the organization. */\n orgName: string\n /** Caller's role inside this organization. */\n role: OrganizationMemberRole\n /** Tier of the organization — see {@link OrganizationType}. */\n orgType: OrganizationType\n /** Whether the caller is an Admin of this organization. */\n isAdmin: boolean\n /**\n * `true` when the org has at least one `organizationSubscription` row —\n * the org has previously been associated with a paid tier (active,\n * past_due, trialing, lapsed, or canceled). Combined with\n * `orgType === Lapsed` it distinguishes \"subscription expired\" from\n * \"free org that never subscribed\".\n */\n hasSubscriptionHistory: boolean\n}\n\n/**\n * Event types emitted into the organization activity feed. Values mirror\n * the backend enum in `@nevermined-io/commons`. Stored on the wire as\n * dot-namespaced lowercase strings — the SDK accepts any string for\n * forward-compatibility when new event types are introduced server-side.\n */\nexport enum OrganizationActivityEventType {\n // Membership lifecycle\n MemberInvited = 'member.invited',\n MemberJoined = 'member.joined',\n MemberRoleChanged = 'member.role_changed',\n MemberDeactivated = 'member.deactivated',\n MemberReactivated = 'member.reactivated',\n MemberRemoved = 'member.removed',\n InvitationRevoked = 'invitation.revoked',\n InvitationExpired = 'invitation.expired',\n // Resource lifecycle\n AgentCreated = 'agent.created',\n PlanCreated = 'plan.created',\n PlanPurchased = 'plan.purchased',\n // Customer lifecycle\n CustomerAdded = 'customer.added',\n CustomerBlocked = 'customer.blocked',\n CustomerUnblocked = 'customer.unblocked',\n // Subscription lifecycle\n SubscriptionUpgraded = 'subscription.upgraded',\n SubscriptionDowngraded = 'subscription.downgraded',\n SubscriptionCanceled = 'subscription.canceled',\n SubscriptionLapsed = 'subscription.lapsed',\n // Webhook delivery\n WebhookDelivered = 'webhook.delivered',\n WebhookFailed = 'webhook.failed',\n}\n\n/**\n * The resource an activity event is about. `kind` describes the resource\n * type (`plan`, `agent`, `member`, `subscription`, `invitation`, `customer`,\n * `webhook`) and `id` is the resource's identifier. Extras vary by kind —\n * e.g. invitations include `role` + `email`, members include `role` +\n * `userId`, subscriptions include `tier`.\n */\nexport type OrganizationActivityEventSubject = {\n kind: string\n id: string\n [key: string]: unknown\n}\n\n/**\n * A single event emitted into the organization activity feed. Shape mirrors\n * `OrganizationActivityEventResponseDto` in the Nevermined backend.\n */\nexport type OrganizationActivityEvent = {\n /** Activity event ID (e.g. `ae-{uuid}`). */\n id: string\n /**\n * Backend-emitted event type. Use {@link OrganizationActivityEventType}\n * for known values; the field stays a plain string so a new server-side\n * event type doesn't break consumers.\n */\n eventType: OrganizationActivityEventType | string\n /** User who triggered the event, or `null` for system-emitted events. */\n actorUserId: string | null\n /** Resource the event is about — see {@link OrganizationActivityEventSubject}. */\n subject: OrganizationActivityEventSubject\n /** Optional payload (e.g. previous/current values on role/status changes). */\n metadata: Record<string, unknown> | null\n /** ISO-8601 timestamp of when the event occurred. */\n occurredAt: string\n}\n\n/**\n * Paginated page of activity events. The backend only echoes `items` and\n * `total`; `page` / `limit` are not in the response — they're the values\n * the caller asked for in {@link OrganizationActivityFilters}.\n */\nexport type OrganizationActivityPage = {\n items: OrganizationActivityEvent[]\n total: number\n}\n\n/** Filters accepted by `OrganizationsAPI.getOrganizationActivity`. */\nexport type OrganizationActivityFilters = {\n /**\n * Restrict to one or more event types. Accepts a single string, an enum\n * value, or an array (sent to the backend as a comma-separated list).\n */\n eventType?: OrganizationActivityEventType | string | Array<OrganizationActivityEventType | string>\n /** Restrict to events triggered by a specific user. */\n actorUserId?: string\n /** ISO-8601 lower bound (inclusive) on `occurredAt`. */\n from?: string\n /** ISO-8601 upper bound (exclusive) on `occurredAt`. */\n to?: string\n /** Page number — 1-based. */\n page?: number\n /** Page size — backend cap is 200. */\n limit?: number\n}\n\nexport type OrganizationMember = {\n createdAt: string\n updatedAt: string\n id: string\n userId: string\n orgId: string\n userAddress: string\n role: OrganizationMemberRole\n isActive: boolean\n}\n\nexport type OrganizationMembersResponse = {\n members: OrganizationMember[]\n total: number\n}\n\nexport type StripeCheckoutResult = {\n stripeAccountId: string\n stripeAccountLink: string\n userId: string\n userCountryCode: string\n linkCreatedAt: number\n linkExpiresAt: number\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/api/organizations-api/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,sBAIX;AAJD,WAAY,sBAAsB;IAChC,yCAAe,CAAA;IACf,2CAAiB,CAAA;IACjB,2CAAiB,CAAA;AACnB,CAAC,EAJW,sBAAsB,KAAtB,sBAAsB,QAIjC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,uCAAmB,CAAA;IACnB,6CAAyB,CAAA;IACzB,qCAAiB,CAAA;IACjB,mCAAe,CAAA;AACjB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AAwED;;;;;GAKG;AACH,MAAM,CAAN,IAAY,6BA0BX;AA1BD,WAAY,6BAA6B;IACvC,uBAAuB;IACvB,iEAAgC,CAAA;IAChC,+DAA8B,CAAA;IAC9B,0EAAyC,CAAA;IACzC,yEAAwC,CAAA;IACxC,yEAAwC,CAAA;IACxC,iEAAgC,CAAA;IAChC,yEAAwC,CAAA;IACxC,yEAAwC,CAAA;IACxC,qBAAqB;IACrB,+DAA8B,CAAA;IAC9B,6DAA4B,CAAA;IAC5B,iEAAgC,CAAA;IAChC,qBAAqB;IACrB,iEAAgC,CAAA;IAChC,qEAAoC,CAAA;IACpC,yEAAwC,CAAA;IACxC,yBAAyB;IACzB,+EAA8C,CAAA;IAC9C,mFAAkD,CAAA;IAClD,+EAA8C,CAAA;IAC9C,2EAA0C,CAAA;IAC1C,mBAAmB;IACnB,uEAAsC,CAAA;IACtC,iEAAgC,CAAA;AAClC,CAAC,EA1BW,6BAA6B,KAA7B,6BAA6B,QA0BxC","sourcesContent":["export enum OrganizationMemberRole {\n Admin = 'Admin',\n Member = 'Member',\n Client = 'Client',\n}\n\n/**\n * Tier of an organization. Mirrors the backend `OrganizationType` enum at\n * `libs/commons/src/lib/types/types.ts` in nvm-monorepo.\n *\n * - `Premium` / `Enterprise` — the paid tiers from Epic #1339.\n * - `Lapsed` — an org whose paid subscription has expired; replaces the\n * retired `Free` bucket (the backend never emits `Free` for new rows).\n * - `Other` — legacy pre-tiered-pricing bucket still returned for orgs that\n * pre-date the tier system; inherits Premium-equivalent caps and features\n * server-side.\n */\nexport enum OrganizationType {\n Premium = 'Premium',\n Enterprise = 'Enterprise',\n Lapsed = 'Lapsed',\n Other = 'Other',\n}\n\nexport type CreateUserResponse = {\n nvmApiKey: string\n userId: string\n userWallet: string\n alreadyMember: boolean\n}\n\n/**\n * Result of onboarding a white-label customer via\n * `POST /organizations/account` with `as: 'customer'` (nvm-monorepo #2418).\n *\n * A discriminated union on `consentRequired` — narrow on it to get the right\n * shape with no impossible states:\n * - `{ consentRequired: true }` — the email belongs to an account the org does\n * NOT own. NO key is issued (HTTP 202); an email challenge was sent to the\n * owner and the account's identity is deliberately withheld. Retry once they\n * consent.\n * - `{ consentRequired: false, nvmApiKey, ... }` — a new account or the org's\n * returning customer. A usable, scoped `nvmApiKey` is always present and the\n * customer is recorded in the org's Customers list.\n */\nexport type CustomerOnboardingResponse =\n | { consentRequired: true }\n | {\n consentRequired: false\n /** The usable NVM API key (Bearer credential). Always present in this branch. */\n nvmApiKey: string\n userId: string\n userWallet: string\n /** True when a key was issued for a customer (new account or renewal). */\n isCustomer: boolean\n /** Whether the customer was written to the org's Customers list (best-effort). */\n customerRecorded: boolean\n /**\n * ISO-8601 expiry of the credential (#2924). Track it and re-onboard to\n * refresh before it lapses. Optional: returned for the credential outcome,\n * but an older backend may omit it.\n */\n expiresAt?: string\n }\n\n/**\n * A single organization the authenticated user is an active member of.\n * Returned by `OrganizationsAPI.getMyMemberships()` and used by clients to\n * power workspace pickers and \"where will this publish?\" UX.\n *\n * Shape mirrors `MyMembershipDto` in the Nevermined backend\n * (`apps/api/src/organizations/dto/my-membership.dto.ts`).\n */\nexport type MyMembership = {\n /** Stable organization id (e.g. `org-…`). */\n orgId: string\n /** Display name of the organization. */\n orgName: string\n /** Caller's role inside this organization. */\n role: OrganizationMemberRole\n /** Tier of the organization — see {@link OrganizationType}. */\n orgType: OrganizationType\n /** Whether the caller is an Admin of this organization. */\n isAdmin: boolean\n /**\n * `true` when the org has at least one `organizationSubscription` row —\n * the org has previously been associated with a paid tier (active,\n * past_due, trialing, lapsed, or canceled). Combined with\n * `orgType === Lapsed` it distinguishes \"subscription expired\" from\n * \"free org that never subscribed\".\n */\n hasSubscriptionHistory: boolean\n}\n\n/**\n * Event types emitted into the organization activity feed. Values mirror\n * the backend enum in `@nevermined-io/commons`. Stored on the wire as\n * dot-namespaced lowercase strings — the SDK accepts any string for\n * forward-compatibility when new event types are introduced server-side.\n */\nexport enum OrganizationActivityEventType {\n // Membership lifecycle\n MemberInvited = 'member.invited',\n MemberJoined = 'member.joined',\n MemberRoleChanged = 'member.role_changed',\n MemberDeactivated = 'member.deactivated',\n MemberReactivated = 'member.reactivated',\n MemberRemoved = 'member.removed',\n InvitationRevoked = 'invitation.revoked',\n InvitationExpired = 'invitation.expired',\n // Resource lifecycle\n AgentCreated = 'agent.created',\n PlanCreated = 'plan.created',\n PlanPurchased = 'plan.purchased',\n // Customer lifecycle\n CustomerAdded = 'customer.added',\n CustomerBlocked = 'customer.blocked',\n CustomerUnblocked = 'customer.unblocked',\n // Subscription lifecycle\n SubscriptionUpgraded = 'subscription.upgraded',\n SubscriptionDowngraded = 'subscription.downgraded',\n SubscriptionCanceled = 'subscription.canceled',\n SubscriptionLapsed = 'subscription.lapsed',\n // Webhook delivery\n WebhookDelivered = 'webhook.delivered',\n WebhookFailed = 'webhook.failed',\n}\n\n/**\n * The resource an activity event is about. `kind` describes the resource\n * type (`plan`, `agent`, `member`, `subscription`, `invitation`, `customer`,\n * `webhook`) and `id` is the resource's identifier. Extras vary by kind —\n * e.g. invitations include `role` + `email`, members include `role` +\n * `userId`, subscriptions include `tier`.\n */\nexport type OrganizationActivityEventSubject = {\n kind: string\n id: string\n [key: string]: unknown\n}\n\n/**\n * A single event emitted into the organization activity feed. Shape mirrors\n * `OrganizationActivityEventResponseDto` in the Nevermined backend.\n */\nexport type OrganizationActivityEvent = {\n /** Activity event ID (e.g. `ae-{uuid}`). */\n id: string\n /**\n * Backend-emitted event type. Use {@link OrganizationActivityEventType}\n * for known values; the field stays a plain string so a new server-side\n * event type doesn't break consumers.\n */\n eventType: OrganizationActivityEventType | string\n /** User who triggered the event, or `null` for system-emitted events. */\n actorUserId: string | null\n /** Resource the event is about — see {@link OrganizationActivityEventSubject}. */\n subject: OrganizationActivityEventSubject\n /** Optional payload (e.g. previous/current values on role/status changes). */\n metadata: Record<string, unknown> | null\n /** ISO-8601 timestamp of when the event occurred. */\n occurredAt: string\n}\n\n/**\n * Paginated page of activity events. The backend only echoes `items` and\n * `total`; `page` / `limit` are not in the response — they're the values\n * the caller asked for in {@link OrganizationActivityFilters}.\n */\nexport type OrganizationActivityPage = {\n items: OrganizationActivityEvent[]\n total: number\n}\n\n/** Filters accepted by `OrganizationsAPI.getOrganizationActivity`. */\nexport type OrganizationActivityFilters = {\n /**\n * Restrict to one or more event types. Accepts a single string, an enum\n * value, or an array (sent to the backend as a comma-separated list).\n */\n eventType?: OrganizationActivityEventType | string | Array<OrganizationActivityEventType | string>\n /** Restrict to events triggered by a specific user. */\n actorUserId?: string\n /** ISO-8601 lower bound (inclusive) on `occurredAt`. */\n from?: string\n /** ISO-8601 upper bound (exclusive) on `occurredAt`. */\n to?: string\n /** Page number — 1-based. */\n page?: number\n /** Page size — backend cap is 200. */\n limit?: number\n}\n\nexport type OrganizationMember = {\n createdAt: string\n updatedAt: string\n id: string\n userId: string\n orgId: string\n userAddress: string\n role: OrganizationMemberRole\n isActive: boolean\n}\n\nexport type OrganizationMembersResponse = {\n members: OrganizationMember[]\n total: number\n}\n\nexport type StripeCheckoutResult = {\n stripeAccountId: string\n stripeAccountLink: string\n userId: string\n userCountryCode: string\n linkCreatedAt: number\n linkExpiresAt: number\n}\n"]}
@@ -56,6 +56,26 @@ export declare class SessionManager {
56
56
  getRequestContext(sessionId: string): RequestContext | undefined;
57
57
  /**
58
58
  * Get or create a transport for a session.
59
+ *
60
+ * Always builds a FRESH transport, even when a transport is already cached for
61
+ * this session id. The transports are created in stateless mode
62
+ * (`sessionIdGenerator: undefined`), and the SDK's stateless transport is
63
+ * single-request by design — reusing a cached one yields an empty-body HTTP 500
64
+ * on the second request. Spec-compliant clients (Claude, Cursor, the official
65
+ * SDK) echo the `Mcp-Session-Id` we return on `initialize` back on every
66
+ * subsequent request, which used to route them straight into that broken reuse
67
+ * path. Rebuilding per request keeps the echoed-id path working exactly like the
68
+ * no-header path.
69
+ *
70
+ * The session map still records the latest transport per id so DELETE can
71
+ * terminate it and requests for an unknown id can be rejected. This managed
72
+ * server runs in stateless JSON-response mode (`enableJsonResponse: true`) and
73
+ * does NOT support standalone GET/SSE streams — the stateless transport rejects
74
+ * them — so there is no long-lived stream for a later POST to disrupt.
75
+ *
76
+ * ponytail: one shared McpServer that every request `close()`s and reconnects —
77
+ * two in-flight requests still race (tracked in #424). Per-session server+transport
78
+ * pairs are the upgrade if concurrency ever matters.
59
79
  */
60
80
  getOrCreateSession(sessionId: string): Promise<any>;
61
81
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/mcp/http/session-manager.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAmBD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,2CAA2C;IAC3C,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,sBAAsB;IACtB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,GAAE,oBAAyB;IAI7C;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IAI/B;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS;IAI9C;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAInE;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIhE;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IA4DzD;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAsB1C;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAI7B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAQ1B;;OAEG;IACH,OAAO,CAAC,GAAG;CAGZ;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,oBAAyB,GAAG,cAAc,CAEtF"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/mcp/http/session-manager.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAmBD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,2CAA2C;IAC3C,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,sBAAsB;IACtB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,GAAE,oBAAyB;IAI7C;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IAI/B;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS;IAI9C;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAInE;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIhE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAqDzD;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAsB1C;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAI7B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAQ1B;;OAEG;IACH,OAAO,CAAC,GAAG;CAGZ;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,oBAAyB,GAAG,cAAc,CAEtF"}
@@ -71,14 +71,28 @@ export class SessionManager {
71
71
  }
72
72
  /**
73
73
  * Get or create a transport for a session.
74
+ *
75
+ * Always builds a FRESH transport, even when a transport is already cached for
76
+ * this session id. The transports are created in stateless mode
77
+ * (`sessionIdGenerator: undefined`), and the SDK's stateless transport is
78
+ * single-request by design — reusing a cached one yields an empty-body HTTP 500
79
+ * on the second request. Spec-compliant clients (Claude, Cursor, the official
80
+ * SDK) echo the `Mcp-Session-Id` we return on `initialize` back on every
81
+ * subsequent request, which used to route them straight into that broken reuse
82
+ * path. Rebuilding per request keeps the echoed-id path working exactly like the
83
+ * no-header path.
84
+ *
85
+ * The session map still records the latest transport per id so DELETE can
86
+ * terminate it and requests for an unknown id can be rejected. This managed
87
+ * server runs in stateless JSON-response mode (`enableJsonResponse: true`) and
88
+ * does NOT support standalone GET/SSE streams — the stateless transport rejects
89
+ * them — so there is no long-lived stream for a later POST to disrupt.
90
+ *
91
+ * ponytail: one shared McpServer that every request `close()`s and reconnects —
92
+ * two in-flight requests still race (tracked in #424). Per-session server+transport
93
+ * pairs are the upgrade if concurrency ever matters.
74
94
  */
75
95
  async getOrCreateSession(sessionId) {
76
- if (this.sessions.has(sessionId)) {
77
- const existingSession = this.sessions.get(sessionId);
78
- if (existingSession) {
79
- return existingSession;
80
- }
81
- }
82
96
  if (!this.mcpServer) {
83
97
  throw new Error('MCP server not set. Call setMcpServer() first.');
84
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/mcp/http/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Session manager for MCP transports.\n * Handles creation, retrieval, and cleanup of StreamableHTTPServerTransport instances.\n * Also stores request context (headers) per session for authentication.\n */\nimport { randomUUID } from 'crypto'\n\n// We'll dynamically import the SDK to avoid hard dependency issues\nlet StreamableHTTPServerTransport: any = null\n\n/**\n * Request context stored per session.\n * Contains HTTP headers and other request info needed for authentication.\n */\nexport interface RequestContext {\n headers: Record<string, string | string[] | undefined>\n method?: string\n url?: string\n ip?: string\n}\n\n/**\n * Lazily load the MCP SDK transport.\n */\nasync function getTransportClass(): Promise<any> {\n if (!StreamableHTTPServerTransport) {\n try {\n const module = await import('@modelcontextprotocol/sdk/server/streamableHttp.js')\n StreamableHTTPServerTransport = module.StreamableHTTPServerTransport\n } catch (error) {\n throw new Error(\n 'Failed to load @modelcontextprotocol/sdk. Make sure it is installed: npm install @modelcontextprotocol/sdk',\n )\n }\n }\n return StreamableHTTPServerTransport\n}\n\n/**\n * Configuration for session manager.\n */\nexport interface SessionManagerConfig {\n /** Callback when a session is created */\n onSessionCreated?: (sessionId: string) => void\n /** Callback when a session is destroyed */\n onSessionDestroyed?: (sessionId: string) => void\n /** Logger function */\n log?: (message: string) => void\n}\n\n/**\n * Manages MCP transport sessions.\n */\nexport class SessionManager {\n private sessions: Map<string, any> = new Map()\n private requestContexts: Map<string, RequestContext> = new Map()\n private mcpServer: any = null\n private config: SessionManagerConfig\n\n constructor(config: SessionManagerConfig = {}) {\n this.config = config\n }\n\n /**\n * Set the MCP server that transports will connect to.\n */\n setMcpServer(server: any): void {\n this.mcpServer = server\n }\n\n /**\n * Generate a new session ID.\n */\n generateSessionId(): string {\n return randomUUID()\n }\n\n /**\n * Check if a session exists.\n */\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId)\n }\n\n /**\n * Get an existing session's transport.\n */\n getSession(sessionId: string): any | undefined {\n return this.sessions.get(sessionId)\n }\n\n /**\n * Store request context (headers, etc.) for a session.\n * This is called when an HTTP request arrives, before dispatching to MCP.\n */\n setRequestContext(sessionId: string, context: RequestContext): void {\n this.requestContexts.set(sessionId, context)\n }\n\n /**\n * Get the stored request context for a session.\n * Returns undefined if no context is stored.\n */\n getRequestContext(sessionId: string): RequestContext | undefined {\n return this.requestContexts.get(sessionId)\n }\n\n /**\n * Get or create a transport for a session.\n */\n async getOrCreateSession(sessionId: string): Promise<any> {\n if (this.sessions.has(sessionId)) {\n const existingSession = this.sessions.get(sessionId)\n if (existingSession) {\n return existingSession\n }\n }\n\n if (!this.mcpServer) {\n throw new Error('MCP server not set. Call setMcpServer() first.')\n }\n\n // In stateless mode (sessionIdGenerator: undefined), each request gets a new transport\n // but McpServer can only have one transport at a time. Close the existing connection\n // before connecting the new transport so Protocol._transport is reset to undefined.\n // The StreamableHTTPServerTransport with enableJsonResponse does not auto-close after\n // sending a JSON response, so we must close it explicitly before each new connection.\n try {\n await this.mcpServer.close()\n } catch (_e) {\n // Ignore close errors — server may not have been connected yet\n }\n\n const TransportClass = await getTransportClass()\n\n // Use stateless mode (sessionIdGenerator: undefined) to avoid requiring\n // Mcp-Session-Id header on every request. This allows clients to make\n // direct JSON-RPC calls without the full MCP initialization handshake.\n const transport = new TransportClass({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n })\n\n // Set onclose BEFORE connect() so the SDK chains it with its own handler.\n // When the transport closes, both our cleanup and the SDK's cleanup run.\n transport.onclose = () => {\n this.sessions.delete(sessionId)\n this.requestContexts.delete(sessionId)\n this.config.onSessionDestroyed?.(sessionId)\n this.log?.(`Transport closed for session ${sessionId}`)\n }\n\n await this.mcpServer.connect(transport)\n\n // Force _initialized to true to allow requests without requiring initialize first.\n // This maintains backward compatibility with clients that don't send initialize.\n // The SDK sets _initialized = true only after processing an initialize request,\n // but we want to accept tool/resource/prompt calls directly.\n if (transport._initialized === false) {\n transport._initialized = true\n }\n\n this.sessions.set(sessionId, transport)\n\n this.log?.(`Created new transport for session ${sessionId}`)\n this.config.onSessionCreated?.(sessionId)\n\n return transport\n }\n\n /**\n * Destroy a session.\n */\n destroySession(sessionId: string): boolean {\n if (this.sessions.has(sessionId)) {\n const transport = this.sessions.get(sessionId)\n this.sessions.delete(sessionId)\n this.requestContexts.delete(sessionId) // Clean up request context\n this.log?.(`Destroyed session ${sessionId}`)\n this.config.onSessionDestroyed?.(sessionId)\n\n // Try to close the transport if it has a close method\n if (transport && typeof transport.close === 'function') {\n try {\n transport.close()\n } catch {\n // Ignore close errors\n }\n }\n\n return true\n }\n return false\n }\n\n /**\n * Get all active session IDs.\n */\n getActiveSessions(): string[] {\n return Array.from(this.sessions.keys())\n }\n\n /**\n * Get the number of active sessions.\n */\n getSessionCount(): number {\n return this.sessions.size\n }\n\n /**\n * Destroy all sessions.\n */\n destroyAllSessions(): void {\n const sessionIds = this.getActiveSessions()\n for (const sessionId of sessionIds) {\n this.destroySession(sessionId)\n }\n this.log?.(`Destroyed all ${sessionIds.length} sessions`)\n }\n\n /**\n * Internal logging helper (no-op if log not provided).\n */\n private log(message: string): void {\n this.config.log?.(message)\n }\n}\n\n/**\n * Create a new session manager.\n */\nexport function createSessionManager(config: SessionManagerConfig = {}): SessionManager {\n return new SessionManager(config)\n}\n"]}
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/mcp/http/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Session manager for MCP transports.\n * Handles creation, retrieval, and cleanup of StreamableHTTPServerTransport instances.\n * Also stores request context (headers) per session for authentication.\n */\nimport { randomUUID } from 'crypto'\n\n// We'll dynamically import the SDK to avoid hard dependency issues\nlet StreamableHTTPServerTransport: any = null\n\n/**\n * Request context stored per session.\n * Contains HTTP headers and other request info needed for authentication.\n */\nexport interface RequestContext {\n headers: Record<string, string | string[] | undefined>\n method?: string\n url?: string\n ip?: string\n}\n\n/**\n * Lazily load the MCP SDK transport.\n */\nasync function getTransportClass(): Promise<any> {\n if (!StreamableHTTPServerTransport) {\n try {\n const module = await import('@modelcontextprotocol/sdk/server/streamableHttp.js')\n StreamableHTTPServerTransport = module.StreamableHTTPServerTransport\n } catch (error) {\n throw new Error(\n 'Failed to load @modelcontextprotocol/sdk. Make sure it is installed: npm install @modelcontextprotocol/sdk',\n )\n }\n }\n return StreamableHTTPServerTransport\n}\n\n/**\n * Configuration for session manager.\n */\nexport interface SessionManagerConfig {\n /** Callback when a session is created */\n onSessionCreated?: (sessionId: string) => void\n /** Callback when a session is destroyed */\n onSessionDestroyed?: (sessionId: string) => void\n /** Logger function */\n log?: (message: string) => void\n}\n\n/**\n * Manages MCP transport sessions.\n */\nexport class SessionManager {\n private sessions: Map<string, any> = new Map()\n private requestContexts: Map<string, RequestContext> = new Map()\n private mcpServer: any = null\n private config: SessionManagerConfig\n\n constructor(config: SessionManagerConfig = {}) {\n this.config = config\n }\n\n /**\n * Set the MCP server that transports will connect to.\n */\n setMcpServer(server: any): void {\n this.mcpServer = server\n }\n\n /**\n * Generate a new session ID.\n */\n generateSessionId(): string {\n return randomUUID()\n }\n\n /**\n * Check if a session exists.\n */\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId)\n }\n\n /**\n * Get an existing session's transport.\n */\n getSession(sessionId: string): any | undefined {\n return this.sessions.get(sessionId)\n }\n\n /**\n * Store request context (headers, etc.) for a session.\n * This is called when an HTTP request arrives, before dispatching to MCP.\n */\n setRequestContext(sessionId: string, context: RequestContext): void {\n this.requestContexts.set(sessionId, context)\n }\n\n /**\n * Get the stored request context for a session.\n * Returns undefined if no context is stored.\n */\n getRequestContext(sessionId: string): RequestContext | undefined {\n return this.requestContexts.get(sessionId)\n }\n\n /**\n * Get or create a transport for a session.\n *\n * Always builds a FRESH transport, even when a transport is already cached for\n * this session id. The transports are created in stateless mode\n * (`sessionIdGenerator: undefined`), and the SDK's stateless transport is\n * single-request by design — reusing a cached one yields an empty-body HTTP 500\n * on the second request. Spec-compliant clients (Claude, Cursor, the official\n * SDK) echo the `Mcp-Session-Id` we return on `initialize` back on every\n * subsequent request, which used to route them straight into that broken reuse\n * path. Rebuilding per request keeps the echoed-id path working exactly like the\n * no-header path.\n *\n * The session map still records the latest transport per id so DELETE can\n * terminate it and requests for an unknown id can be rejected. This managed\n * server runs in stateless JSON-response mode (`enableJsonResponse: true`) and\n * does NOT support standalone GET/SSE streams — the stateless transport rejects\n * them — so there is no long-lived stream for a later POST to disrupt.\n *\n * ponytail: one shared McpServer that every request `close()`s and reconnects —\n * two in-flight requests still race (tracked in #424). Per-session server+transport\n * pairs are the upgrade if concurrency ever matters.\n */\n async getOrCreateSession(sessionId: string): Promise<any> {\n if (!this.mcpServer) {\n throw new Error('MCP server not set. Call setMcpServer() first.')\n }\n\n // In stateless mode (sessionIdGenerator: undefined), each request gets a new transport\n // but McpServer can only have one transport at a time. Close the existing connection\n // before connecting the new transport so Protocol._transport is reset to undefined.\n // The StreamableHTTPServerTransport with enableJsonResponse does not auto-close after\n // sending a JSON response, so we must close it explicitly before each new connection.\n try {\n await this.mcpServer.close()\n } catch (_e) {\n // Ignore close errors — server may not have been connected yet\n }\n\n const TransportClass = await getTransportClass()\n\n // Use stateless mode (sessionIdGenerator: undefined) to avoid requiring\n // Mcp-Session-Id header on every request. This allows clients to make\n // direct JSON-RPC calls without the full MCP initialization handshake.\n const transport = new TransportClass({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n })\n\n // Set onclose BEFORE connect() so the SDK chains it with its own handler.\n // When the transport closes, both our cleanup and the SDK's cleanup run.\n transport.onclose = () => {\n this.sessions.delete(sessionId)\n this.requestContexts.delete(sessionId)\n this.config.onSessionDestroyed?.(sessionId)\n this.log?.(`Transport closed for session ${sessionId}`)\n }\n\n await this.mcpServer.connect(transport)\n\n // Force _initialized to true to allow requests without requiring initialize first.\n // This maintains backward compatibility with clients that don't send initialize.\n // The SDK sets _initialized = true only after processing an initialize request,\n // but we want to accept tool/resource/prompt calls directly.\n if (transport._initialized === false) {\n transport._initialized = true\n }\n\n this.sessions.set(sessionId, transport)\n\n this.log?.(`Created new transport for session ${sessionId}`)\n this.config.onSessionCreated?.(sessionId)\n\n return transport\n }\n\n /**\n * Destroy a session.\n */\n destroySession(sessionId: string): boolean {\n if (this.sessions.has(sessionId)) {\n const transport = this.sessions.get(sessionId)\n this.sessions.delete(sessionId)\n this.requestContexts.delete(sessionId) // Clean up request context\n this.log?.(`Destroyed session ${sessionId}`)\n this.config.onSessionDestroyed?.(sessionId)\n\n // Try to close the transport if it has a close method\n if (transport && typeof transport.close === 'function') {\n try {\n transport.close()\n } catch {\n // Ignore close errors\n }\n }\n\n return true\n }\n return false\n }\n\n /**\n * Get all active session IDs.\n */\n getActiveSessions(): string[] {\n return Array.from(this.sessions.keys())\n }\n\n /**\n * Get the number of active sessions.\n */\n getSessionCount(): number {\n return this.sessions.size\n }\n\n /**\n * Destroy all sessions.\n */\n destroyAllSessions(): void {\n const sessionIds = this.getActiveSessions()\n for (const sessionId of sessionIds) {\n this.destroySession(sessionId)\n }\n this.log?.(`Destroyed all ${sessionIds.length} sessions`)\n }\n\n /**\n * Internal logging helper (no-op if log not provided).\n */\n private log(message: string): void {\n this.config.log?.(message)\n }\n}\n\n/**\n * Create a new session manager.\n */\nexport function createSessionManager(config: SessionManagerConfig = {}): SessionManager {\n return new SessionManager(config)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nevermined-io/payments",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Typescript SDK to interact with the Nevermined Payments Protocol",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",