@ai-billing/polar 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -7,4 +7,4 @@ npm install @ai-billing/core @ai-billing/polar
7
7
  ```
8
8
 
9
9
  **Documentation**
10
- For full usage instructions and examples, please refer to the [Github page](https://github.com/narevai/ai-billing#readme).
10
+ For full usage instructions and examples, please refer to the [Documentation](https://www.narev.ai/docs/sdk/ai-billing).
package/dist/index.cjs CHANGED
@@ -50,7 +50,7 @@ function createPolarDestination(options) {
50
50
  metadata = {
51
51
  ...(0, import_core.buildMeterMetadata)(event),
52
52
  ...event.cost ? {
53
- cost_nanos: (0, import_core.costToNumber)(event.cost, "nanos"),
53
+ credits_consumed: (0, import_core.costToNumber)(event.cost, "base"),
54
54
  cost_currency: event.cost.currency
55
55
  } : {}
56
56
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/destination/polar-destination.ts"],"sourcesContent":["export * from './destination/index.js';\n","import { Polar } from '@polar-sh/sdk';\nimport {\n createDestination,\n costToNumber,\n buildMeterMetadata,\n} from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\nimport { EventMetadataInput } from '@polar-sh/sdk/models/components/eventmetadatainput.js';\n\n/**\n * Options for {@link createPolarDestination}.\n *\n * Polar ingests events keyed by a `customerId` (and optionally an `externalId`) plus a metadata map. This\n * destination extracts identity from billing event tags, builds default metadata from the event (usage +\n * tags), and includes cost when present.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n */\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n /**\n * Optional pre-configured Polar SDK client. When omitted, a client is constructed from `accessToken` and\n * `server`.\n */\n client?: Polar;\n /** Access token used when creating a Polar SDK client. */\n accessToken?: string;\n /** Polar environment used when creating a Polar SDK client. */\n server?: 'sandbox' | 'production';\n /**\n * Event name to ingest into Polar, or a function that derives the name from the billing event.\n *\n * Use a function when you want different Polar events per model/provider while keeping one destination.\n */\n eventName: string | ((event: BillingEvent<TTags>) => string);\n /**\n * Tag key used to read the Polar `customerId` (internal ID). When omitted, common tag keys are checked:\n * `customerId`, `polarCustomerId`, `customer_id`.\n */\n customerIdKey?: keyof TTags;\n /**\n * Tag key used to read the Polar `externalId`. When omitted, common tag keys are checked: `userId`,\n * `externalId`, `user_id`.\n */\n externalCustomerIdKey?: keyof TTags;\n\n /**\n * Optional override for the metadata payload sent to Polar.\n *\n * When omitted, metadata is built from {@link buildMeterMetadata} and includes:\n * - token/usage dimensions\n * - `tag_*` values from event tags\n * - `cost_nanos` / `cost_currency` when `event.cost` is present\n */\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\n\n/**\n * Creates a {@link Destination} that ingests billing events into Polar.\n *\n * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only\n * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are\n * missing, logs a warning and skips the event.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n * @param options - Destination configuration; see {@link PolarDestinationOptions}.\n * @returns A destination function that sends events to Polar.\n */\nexport function createPolarDestination<TTags extends DefaultTags = DefaultTags>(\n options: PolarDestinationOptions<TTags>,\n): Destination<TTags> {\n const polar =\n options.client ??\n new Polar({\n accessToken: options.accessToken,\n server: options.server,\n });\n\n return createDestination<TTags>('polar', async event => {\n const tags = (event.tags ?? {}) as Record<\n string,\n string | number | boolean\n >;\n\n const internalId = options.customerIdKey\n ? tags[options.customerIdKey as string]\n : (tags.customerId ?? tags.polarCustomerId ?? tags.customer_id);\n\n const externalId = options.externalCustomerIdKey\n ? tags[options.externalCustomerIdKey as string]\n : (tags.userId ?? tags.externalId ?? tags.user_id);\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n return;\n }\n\n const eventName =\n typeof options.eventName === 'function'\n ? options.eventName(event)\n : options.eventName;\n\n let metadata: Record<string, EventMetadataInput>;\n\n if (options.mapMetadata) {\n metadata = options.mapMetadata(event);\n } else {\n metadata = {\n ...(buildMeterMetadata(event) as Record<\n string,\n string | number | boolean\n >),\n ...(event.cost\n ? {\n cost_nanos: costToNumber(event.cost, 'nanos'),\n cost_currency: event.cost.currency,\n }\n : {}),\n };\n }\n\n const eventPayload = internalId\n ? { name: eventName, customerId: String(internalId), metadata }\n : { name: eventName, externalCustomerId: String(externalId), metadata };\n\n try {\n await polar.events.ingest({ events: [eventPayload] });\n } catch (error) {\n console.error('[ai-billing] Failed to ingest event to Polar:', error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAsB;AACtB,kBAIO;AAkEA,SAAS,uBACd,SACoB;AACpB,QAAM,QACJ,QAAQ,UACR,IAAI,iBAAM;AAAA,IACR,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAEH,aAAO,+BAAyB,SAAS,OAAM,UAAS;AACtD,UAAM,OAAQ,MAAM,QAAQ,CAAC;AAK7B,UAAM,aAAa,QAAQ,gBACvB,KAAK,QAAQ,aAAuB,IACnC,KAAK,cAAc,KAAK,mBAAmB,KAAK;AAErD,UAAM,aAAa,QAAQ,wBACvB,KAAK,QAAQ,qBAA+B,IAC3C,KAAK,UAAU,KAAK,cAAc,KAAK;AAE5C,QAAI,CAAC,cAAc,CAAC,YAAY;AAC9B,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,YACJ,OAAO,QAAQ,cAAc,aACzB,QAAQ,UAAU,KAAK,IACvB,QAAQ;AAEd,QAAI;AAEJ,QAAI,QAAQ,aAAa;AACvB,iBAAW,QAAQ,YAAY,KAAK;AAAA,IACtC,OAAO;AACL,iBAAW;AAAA,QACT,OAAI,gCAAmB,KAAK;AAAA,QAI5B,GAAI,MAAM,OACN;AAAA,UACE,gBAAY,0BAAa,MAAM,MAAM,OAAO;AAAA,UAC5C,eAAe,MAAM,KAAK;AAAA,QAC5B,IACA,CAAC;AAAA,MACP;AAAA,IACF;AAEA,UAAM,eAAe,aACjB,EAAE,MAAM,WAAW,YAAY,OAAO,UAAU,GAAG,SAAS,IAC5D,EAAE,MAAM,WAAW,oBAAoB,OAAO,UAAU,GAAG,SAAS;AAExE,QAAI;AACF,YAAM,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;AAAA,IACtD,SAAS,OAAO;AACd,cAAQ,MAAM,iDAAiD,KAAK;AAAA,IACtE;AAAA,EACF,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/destination/polar-destination.ts"],"sourcesContent":["export * from './destination/index.js';\n","import { Polar } from '@polar-sh/sdk';\nimport {\n createDestination,\n costToNumber,\n buildMeterMetadata,\n} from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\nimport { EventMetadataInput } from '@polar-sh/sdk/models/components/eventmetadatainput.js';\n\n/**\n * Options for {@link createPolarDestination}.\n *\n * Polar ingests events keyed by a `customerId` (and optionally an `externalId`) plus a metadata map. This\n * destination extracts identity from billing event tags, builds default metadata from the event (usage +\n * tags), and includes cost when present.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n */\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n /**\n * Optional pre-configured Polar SDK client. When omitted, a client is constructed from `accessToken` and\n * `server`.\n */\n client?: Polar;\n /** Access token used when creating a Polar SDK client. */\n accessToken?: string;\n /** Polar environment used when creating a Polar SDK client. */\n server?: 'sandbox' | 'production';\n /**\n * Event name to ingest into Polar, or a function that derives the name from the billing event.\n *\n * Use a function when you want different Polar events per model/provider while keeping one destination.\n */\n eventName: string | ((event: BillingEvent<TTags>) => string);\n /**\n * Tag key used to read the Polar `customerId` (internal ID). When omitted, common tag keys are checked:\n * `customerId`, `polarCustomerId`, `customer_id`.\n */\n customerIdKey?: keyof TTags;\n /**\n * Tag key used to read the Polar `externalId`. When omitted, common tag keys are checked: `userId`,\n * `externalId`, `user_id`.\n */\n externalCustomerIdKey?: keyof TTags;\n\n /**\n * Optional override for the metadata payload sent to Polar.\n *\n * When omitted, metadata is built from {@link buildMeterMetadata} and includes:\n * - token/usage dimensions\n * - `tag_*` values from event tags\n * - `credits_consumed` / `cost_currency` when `event.cost` is present\n */\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\n\n/**\n * Creates a {@link Destination} that ingests billing events into Polar.\n *\n * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only\n * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are\n * missing, logs a warning and skips the event.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n * @param options - Destination configuration; see {@link PolarDestinationOptions}.\n * @returns A destination function that sends events to Polar.\n */\nexport function createPolarDestination<TTags extends DefaultTags = DefaultTags>(\n options: PolarDestinationOptions<TTags>,\n): Destination<TTags> {\n const polar =\n options.client ??\n new Polar({\n accessToken: options.accessToken,\n server: options.server,\n });\n\n return createDestination<TTags>('polar', async event => {\n const tags = (event.tags ?? {}) as Record<\n string,\n string | number | boolean\n >;\n\n const internalId = options.customerIdKey\n ? tags[options.customerIdKey as string]\n : (tags.customerId ?? tags.polarCustomerId ?? tags.customer_id);\n\n const externalId = options.externalCustomerIdKey\n ? tags[options.externalCustomerIdKey as string]\n : (tags.userId ?? tags.externalId ?? tags.user_id);\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n return;\n }\n\n const eventName =\n typeof options.eventName === 'function'\n ? options.eventName(event)\n : options.eventName;\n\n let metadata: Record<string, EventMetadataInput>;\n\n if (options.mapMetadata) {\n metadata = options.mapMetadata(event);\n } else {\n metadata = {\n ...(buildMeterMetadata(event) as Record<\n string,\n string | number | boolean\n >),\n ...(event.cost\n ? {\n credits_consumed: costToNumber(event.cost, 'base'),\n cost_currency: event.cost.currency,\n }\n : {}),\n };\n }\n\n const eventPayload = internalId\n ? { name: eventName, customerId: String(internalId), metadata }\n : { name: eventName, externalCustomerId: String(externalId), metadata };\n\n try {\n await polar.events.ingest({ events: [eventPayload] });\n } catch (error) {\n console.error('[ai-billing] Failed to ingest event to Polar:', error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAsB;AACtB,kBAIO;AAkEA,SAAS,uBACd,SACoB;AACpB,QAAM,QACJ,QAAQ,UACR,IAAI,iBAAM;AAAA,IACR,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAEH,aAAO,+BAAyB,SAAS,OAAM,UAAS;AACtD,UAAM,OAAQ,MAAM,QAAQ,CAAC;AAK7B,UAAM,aAAa,QAAQ,gBACvB,KAAK,QAAQ,aAAuB,IACnC,KAAK,cAAc,KAAK,mBAAmB,KAAK;AAErD,UAAM,aAAa,QAAQ,wBACvB,KAAK,QAAQ,qBAA+B,IAC3C,KAAK,UAAU,KAAK,cAAc,KAAK;AAE5C,QAAI,CAAC,cAAc,CAAC,YAAY;AAC9B,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,YACJ,OAAO,QAAQ,cAAc,aACzB,QAAQ,UAAU,KAAK,IACvB,QAAQ;AAEd,QAAI;AAEJ,QAAI,QAAQ,aAAa;AACvB,iBAAW,QAAQ,YAAY,KAAK;AAAA,IACtC,OAAO;AACL,iBAAW;AAAA,QACT,OAAI,gCAAmB,KAAK;AAAA,QAI5B,GAAI,MAAM,OACN;AAAA,UACE,sBAAkB,0BAAa,MAAM,MAAM,MAAM;AAAA,UACjD,eAAe,MAAM,KAAK;AAAA,QAC5B,IACA,CAAC;AAAA,MACP;AAAA,IACF;AAEA,UAAM,eAAe,aACjB,EAAE,MAAM,WAAW,YAAY,OAAO,UAAU,GAAG,SAAS,IAC5D,EAAE,MAAM,WAAW,oBAAoB,OAAO,UAAU,GAAG,SAAS;AAExE,QAAI;AACF,YAAM,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;AAAA,IACtD,SAAS,OAAO;AACd,cAAQ,MAAM,iDAAiD,KAAK;AAAA,IACtE;AAAA,EACF,CAAC;AACH;","names":[]}
package/dist/index.d.cts CHANGED
@@ -42,7 +42,7 @@ interface PolarDestinationOptions<TTags extends DefaultTags = DefaultTags> {
42
42
  * When omitted, metadata is built from {@link buildMeterMetadata} and includes:
43
43
  * - token/usage dimensions
44
44
  * - `tag_*` values from event tags
45
- * - `cost_nanos` / `cost_currency` when `event.cost` is present
45
+ * - `credits_consumed` / `cost_currency` when `event.cost` is present
46
46
  */
47
47
  mapMetadata?: (event: BillingEvent<TTags>) => Record<string, string | number | boolean>;
48
48
  }
package/dist/index.d.ts CHANGED
@@ -42,7 +42,7 @@ interface PolarDestinationOptions<TTags extends DefaultTags = DefaultTags> {
42
42
  * When omitted, metadata is built from {@link buildMeterMetadata} and includes:
43
43
  * - token/usage dimensions
44
44
  * - `tag_*` values from event tags
45
- * - `cost_nanos` / `cost_currency` when `event.cost` is present
45
+ * - `credits_consumed` / `cost_currency` when `event.cost` is present
46
46
  */
47
47
  mapMetadata?: (event: BillingEvent<TTags>) => Record<string, string | number | boolean>;
48
48
  }
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ function createPolarDestination(options) {
28
28
  metadata = {
29
29
  ...buildMeterMetadata(event),
30
30
  ...event.cost ? {
31
- cost_nanos: costToNumber(event.cost, "nanos"),
31
+ credits_consumed: costToNumber(event.cost, "base"),
32
32
  cost_currency: event.cost.currency
33
33
  } : {}
34
34
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/destination/polar-destination.ts"],"sourcesContent":["import { Polar } from '@polar-sh/sdk';\nimport {\n createDestination,\n costToNumber,\n buildMeterMetadata,\n} from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\nimport { EventMetadataInput } from '@polar-sh/sdk/models/components/eventmetadatainput.js';\n\n/**\n * Options for {@link createPolarDestination}.\n *\n * Polar ingests events keyed by a `customerId` (and optionally an `externalId`) plus a metadata map. This\n * destination extracts identity from billing event tags, builds default metadata from the event (usage +\n * tags), and includes cost when present.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n */\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n /**\n * Optional pre-configured Polar SDK client. When omitted, a client is constructed from `accessToken` and\n * `server`.\n */\n client?: Polar;\n /** Access token used when creating a Polar SDK client. */\n accessToken?: string;\n /** Polar environment used when creating a Polar SDK client. */\n server?: 'sandbox' | 'production';\n /**\n * Event name to ingest into Polar, or a function that derives the name from the billing event.\n *\n * Use a function when you want different Polar events per model/provider while keeping one destination.\n */\n eventName: string | ((event: BillingEvent<TTags>) => string);\n /**\n * Tag key used to read the Polar `customerId` (internal ID). When omitted, common tag keys are checked:\n * `customerId`, `polarCustomerId`, `customer_id`.\n */\n customerIdKey?: keyof TTags;\n /**\n * Tag key used to read the Polar `externalId`. When omitted, common tag keys are checked: `userId`,\n * `externalId`, `user_id`.\n */\n externalCustomerIdKey?: keyof TTags;\n\n /**\n * Optional override for the metadata payload sent to Polar.\n *\n * When omitted, metadata is built from {@link buildMeterMetadata} and includes:\n * - token/usage dimensions\n * - `tag_*` values from event tags\n * - `cost_nanos` / `cost_currency` when `event.cost` is present\n */\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\n\n/**\n * Creates a {@link Destination} that ingests billing events into Polar.\n *\n * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only\n * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are\n * missing, logs a warning and skips the event.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n * @param options - Destination configuration; see {@link PolarDestinationOptions}.\n * @returns A destination function that sends events to Polar.\n */\nexport function createPolarDestination<TTags extends DefaultTags = DefaultTags>(\n options: PolarDestinationOptions<TTags>,\n): Destination<TTags> {\n const polar =\n options.client ??\n new Polar({\n accessToken: options.accessToken,\n server: options.server,\n });\n\n return createDestination<TTags>('polar', async event => {\n const tags = (event.tags ?? {}) as Record<\n string,\n string | number | boolean\n >;\n\n const internalId = options.customerIdKey\n ? tags[options.customerIdKey as string]\n : (tags.customerId ?? tags.polarCustomerId ?? tags.customer_id);\n\n const externalId = options.externalCustomerIdKey\n ? tags[options.externalCustomerIdKey as string]\n : (tags.userId ?? tags.externalId ?? tags.user_id);\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n return;\n }\n\n const eventName =\n typeof options.eventName === 'function'\n ? options.eventName(event)\n : options.eventName;\n\n let metadata: Record<string, EventMetadataInput>;\n\n if (options.mapMetadata) {\n metadata = options.mapMetadata(event);\n } else {\n metadata = {\n ...(buildMeterMetadata(event) as Record<\n string,\n string | number | boolean\n >),\n ...(event.cost\n ? {\n cost_nanos: costToNumber(event.cost, 'nanos'),\n cost_currency: event.cost.currency,\n }\n : {}),\n };\n }\n\n const eventPayload = internalId\n ? { name: eventName, customerId: String(internalId), metadata }\n : { name: eventName, externalCustomerId: String(externalId), metadata };\n\n try {\n await polar.events.ingest({ events: [eventPayload] });\n } catch (error) {\n console.error('[ai-billing] Failed to ingest event to Polar:', error);\n }\n });\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkEA,SAAS,uBACd,SACoB;AACpB,QAAM,QACJ,QAAQ,UACR,IAAI,MAAM;AAAA,IACR,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAEH,SAAO,kBAAyB,SAAS,OAAM,UAAS;AACtD,UAAM,OAAQ,MAAM,QAAQ,CAAC;AAK7B,UAAM,aAAa,QAAQ,gBACvB,KAAK,QAAQ,aAAuB,IACnC,KAAK,cAAc,KAAK,mBAAmB,KAAK;AAErD,UAAM,aAAa,QAAQ,wBACvB,KAAK,QAAQ,qBAA+B,IAC3C,KAAK,UAAU,KAAK,cAAc,KAAK;AAE5C,QAAI,CAAC,cAAc,CAAC,YAAY;AAC9B,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,YACJ,OAAO,QAAQ,cAAc,aACzB,QAAQ,UAAU,KAAK,IACvB,QAAQ;AAEd,QAAI;AAEJ,QAAI,QAAQ,aAAa;AACvB,iBAAW,QAAQ,YAAY,KAAK;AAAA,IACtC,OAAO;AACL,iBAAW;AAAA,QACT,GAAI,mBAAmB,KAAK;AAAA,QAI5B,GAAI,MAAM,OACN;AAAA,UACE,YAAY,aAAa,MAAM,MAAM,OAAO;AAAA,UAC5C,eAAe,MAAM,KAAK;AAAA,QAC5B,IACA,CAAC;AAAA,MACP;AAAA,IACF;AAEA,UAAM,eAAe,aACjB,EAAE,MAAM,WAAW,YAAY,OAAO,UAAU,GAAG,SAAS,IAC5D,EAAE,MAAM,WAAW,oBAAoB,OAAO,UAAU,GAAG,SAAS;AAExE,QAAI;AACF,YAAM,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;AAAA,IACtD,SAAS,OAAO;AACd,cAAQ,MAAM,iDAAiD,KAAK;AAAA,IACtE;AAAA,EACF,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/destination/polar-destination.ts"],"sourcesContent":["import { Polar } from '@polar-sh/sdk';\nimport {\n createDestination,\n costToNumber,\n buildMeterMetadata,\n} from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\nimport { EventMetadataInput } from '@polar-sh/sdk/models/components/eventmetadatainput.js';\n\n/**\n * Options for {@link createPolarDestination}.\n *\n * Polar ingests events keyed by a `customerId` (and optionally an `externalId`) plus a metadata map. This\n * destination extracts identity from billing event tags, builds default metadata from the event (usage +\n * tags), and includes cost when present.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n */\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n /**\n * Optional pre-configured Polar SDK client. When omitted, a client is constructed from `accessToken` and\n * `server`.\n */\n client?: Polar;\n /** Access token used when creating a Polar SDK client. */\n accessToken?: string;\n /** Polar environment used when creating a Polar SDK client. */\n server?: 'sandbox' | 'production';\n /**\n * Event name to ingest into Polar, or a function that derives the name from the billing event.\n *\n * Use a function when you want different Polar events per model/provider while keeping one destination.\n */\n eventName: string | ((event: BillingEvent<TTags>) => string);\n /**\n * Tag key used to read the Polar `customerId` (internal ID). When omitted, common tag keys are checked:\n * `customerId`, `polarCustomerId`, `customer_id`.\n */\n customerIdKey?: keyof TTags;\n /**\n * Tag key used to read the Polar `externalId`. When omitted, common tag keys are checked: `userId`,\n * `externalId`, `user_id`.\n */\n externalCustomerIdKey?: keyof TTags;\n\n /**\n * Optional override for the metadata payload sent to Polar.\n *\n * When omitted, metadata is built from {@link buildMeterMetadata} and includes:\n * - token/usage dimensions\n * - `tag_*` values from event tags\n * - `credits_consumed` / `cost_currency` when `event.cost` is present\n */\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\n\n/**\n * Creates a {@link Destination} that ingests billing events into Polar.\n *\n * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only\n * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are\n * missing, logs a warning and skips the event.\n *\n * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.\n * @param options - Destination configuration; see {@link PolarDestinationOptions}.\n * @returns A destination function that sends events to Polar.\n */\nexport function createPolarDestination<TTags extends DefaultTags = DefaultTags>(\n options: PolarDestinationOptions<TTags>,\n): Destination<TTags> {\n const polar =\n options.client ??\n new Polar({\n accessToken: options.accessToken,\n server: options.server,\n });\n\n return createDestination<TTags>('polar', async event => {\n const tags = (event.tags ?? {}) as Record<\n string,\n string | number | boolean\n >;\n\n const internalId = options.customerIdKey\n ? tags[options.customerIdKey as string]\n : (tags.customerId ?? tags.polarCustomerId ?? tags.customer_id);\n\n const externalId = options.externalCustomerIdKey\n ? tags[options.externalCustomerIdKey as string]\n : (tags.userId ?? tags.externalId ?? tags.user_id);\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n return;\n }\n\n const eventName =\n typeof options.eventName === 'function'\n ? options.eventName(event)\n : options.eventName;\n\n let metadata: Record<string, EventMetadataInput>;\n\n if (options.mapMetadata) {\n metadata = options.mapMetadata(event);\n } else {\n metadata = {\n ...(buildMeterMetadata(event) as Record<\n string,\n string | number | boolean\n >),\n ...(event.cost\n ? {\n credits_consumed: costToNumber(event.cost, 'base'),\n cost_currency: event.cost.currency,\n }\n : {}),\n };\n }\n\n const eventPayload = internalId\n ? { name: eventName, customerId: String(internalId), metadata }\n : { name: eventName, externalCustomerId: String(externalId), metadata };\n\n try {\n await polar.events.ingest({ events: [eventPayload] });\n } catch (error) {\n console.error('[ai-billing] Failed to ingest event to Polar:', error);\n }\n });\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkEA,SAAS,uBACd,SACoB;AACpB,QAAM,QACJ,QAAQ,UACR,IAAI,MAAM;AAAA,IACR,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAEH,SAAO,kBAAyB,SAAS,OAAM,UAAS;AACtD,UAAM,OAAQ,MAAM,QAAQ,CAAC;AAK7B,UAAM,aAAa,QAAQ,gBACvB,KAAK,QAAQ,aAAuB,IACnC,KAAK,cAAc,KAAK,mBAAmB,KAAK;AAErD,UAAM,aAAa,QAAQ,wBACvB,KAAK,QAAQ,qBAA+B,IAC3C,KAAK,UAAU,KAAK,cAAc,KAAK;AAE5C,QAAI,CAAC,cAAc,CAAC,YAAY;AAC9B,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,YACJ,OAAO,QAAQ,cAAc,aACzB,QAAQ,UAAU,KAAK,IACvB,QAAQ;AAEd,QAAI;AAEJ,QAAI,QAAQ,aAAa;AACvB,iBAAW,QAAQ,YAAY,KAAK;AAAA,IACtC,OAAO;AACL,iBAAW;AAAA,QACT,GAAI,mBAAmB,KAAK;AAAA,QAI5B,GAAI,MAAM,OACN;AAAA,UACE,kBAAkB,aAAa,MAAM,MAAM,MAAM;AAAA,UACjD,eAAe,MAAM,KAAK;AAAA,QAC5B,IACA,CAAC;AAAA,MACP;AAAA,IACF;AAEA,UAAM,eAAe,aACjB,EAAE,MAAM,WAAW,YAAY,OAAO,UAAU,GAAG,SAAS,IAC5D,EAAE,MAAM,WAAW,oBAAoB,OAAO,UAAU,GAAG,SAAS;AAExE,QAAI;AACF,YAAM,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;AAAA,IACtD,SAAS,OAAO;AACd,cAAQ,MAAM,iDAAiD,KAAK;AAAA,IACtE;AAAA,EACF,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-billing/polar",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "repository": {
@@ -42,7 +42,7 @@
42
42
  "@ai-billing/testing": "0.1.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@ai-billing/core": "0.1.1",
45
+ "@ai-billing/core": "0.1.2",
46
46
  "@polar-sh/sdk": "^0.46.7"
47
47
  },
48
48
  "engines": {