@ai-billing/polar 0.1.1 → 0.1.3

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
@@ -40,6 +40,7 @@ function createPolarDestination(options) {
40
40
  console.warn(
41
41
  "[ai-billing] Polar: No identity found in tags. Skipping event."
42
42
  );
43
+ return;
43
44
  }
44
45
  const eventName = typeof options.eventName === "function" ? options.eventName(event) : options.eventName;
45
46
  let metadata;
@@ -54,17 +55,9 @@ function createPolarDestination(options) {
54
55
  } : {}
55
56
  };
56
57
  }
58
+ const eventPayload = internalId ? { name: eventName, customerId: String(internalId), metadata } : { name: eventName, externalCustomerId: String(externalId), metadata };
57
59
  try {
58
- await polar.events.ingest({
59
- events: [
60
- {
61
- name: eventName,
62
- customerId: String(internalId),
63
- ...externalId ? { externalId: String(externalId) } : {},
64
- metadata
65
- }
66
- ]
67
- });
60
+ await polar.events.ingest({ events: [eventPayload] });
68
61
  } catch (error) {
69
62
  console.error("[ai-billing] Failed to ingest event to Polar:", error);
70
63
  }
@@ -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:** Resolves an internal Polar `customerId` and an optional `externalId` from tags. When both are\n * missing, a warning is logged (`No identity found in tags. Skipping event.`), but the destination does not\n * return: it still calls Polar with `customerId: String(internalId)`, which becomes the literal string\n * `\"undefined\"` when the internal id is absent, and it only adds `externalId` when that tag value is truthy.\n * When the internal id is absent but `externalId` is present, no warning is logged, yet `customerId` is\n * still the string `\"undefined\"`.\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 }\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 try {\n await polar.events.ingest({\n events: [\n {\n name: eventName,\n customerId: String(internalId),\n ...(externalId ? { externalId: String(externalId) } : {}),\n metadata,\n },\n ],\n });\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;AAqEA,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;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,QAAI;AACF,YAAM,MAAM,OAAO,OAAO;AAAA,QACxB,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,YAAY,OAAO,UAAU;AAAA,YAC7B,GAAI,aAAa,EAAE,YAAY,OAAO,UAAU,EAAE,IAAI,CAAC;AAAA,YACvD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,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 * - `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":[]}
package/dist/index.d.cts CHANGED
@@ -49,12 +49,9 @@ interface PolarDestinationOptions<TTags extends DefaultTags = DefaultTags> {
49
49
  /**
50
50
  * Creates a {@link Destination} that ingests billing events into Polar.
51
51
  *
52
- * **Identity:** Resolves an internal Polar `customerId` and an optional `externalId` from tags. When both are
53
- * missing, a warning is logged (`No identity found in tags. Skipping event.`), but the destination does not
54
- * return: it still calls Polar with `customerId: String(internalId)`, which becomes the literal string
55
- * `"undefined"` when the internal id is absent, and it only adds `externalId` when that tag value is truthy.
56
- * When the internal id is absent but `externalId` is present, no warning is logged, yet `customerId` is
57
- * still the string `"undefined"`.
52
+ * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only
53
+ * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are
54
+ * missing, logs a warning and skips the event.
58
55
  *
59
56
  * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.
60
57
  * @param options - Destination configuration; see {@link PolarDestinationOptions}.
package/dist/index.d.ts CHANGED
@@ -49,12 +49,9 @@ interface PolarDestinationOptions<TTags extends DefaultTags = DefaultTags> {
49
49
  /**
50
50
  * Creates a {@link Destination} that ingests billing events into Polar.
51
51
  *
52
- * **Identity:** Resolves an internal Polar `customerId` and an optional `externalId` from tags. When both are
53
- * missing, a warning is logged (`No identity found in tags. Skipping event.`), but the destination does not
54
- * return: it still calls Polar with `customerId: String(internalId)`, which becomes the literal string
55
- * `"undefined"` when the internal id is absent, and it only adds `externalId` when that tag value is truthy.
56
- * When the internal id is absent but `externalId` is present, no warning is logged, yet `customerId` is
57
- * still the string `"undefined"`.
52
+ * **Identity:** When `internalId` is present, sends `EventCreateCustomer` with `customerId`. When only
53
+ * `externalId` is present, sends `EventCreateExternalCustomer` with `externalCustomerId`. When both are
54
+ * missing, logs a warning and skips the event.
58
55
  *
59
56
  * @typeParam TTags - The shape of the tags object, extending {@link DefaultTags}.
60
57
  * @param options - Destination configuration; see {@link PolarDestinationOptions}.
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ function createPolarDestination(options) {
18
18
  console.warn(
19
19
  "[ai-billing] Polar: No identity found in tags. Skipping event."
20
20
  );
21
+ return;
21
22
  }
22
23
  const eventName = typeof options.eventName === "function" ? options.eventName(event) : options.eventName;
23
24
  let metadata;
@@ -32,17 +33,9 @@ function createPolarDestination(options) {
32
33
  } : {}
33
34
  };
34
35
  }
36
+ const eventPayload = internalId ? { name: eventName, customerId: String(internalId), metadata } : { name: eventName, externalCustomerId: String(externalId), metadata };
35
37
  try {
36
- await polar.events.ingest({
37
- events: [
38
- {
39
- name: eventName,
40
- customerId: String(internalId),
41
- ...externalId ? { externalId: String(externalId) } : {},
42
- metadata
43
- }
44
- ]
45
- });
38
+ await polar.events.ingest({ events: [eventPayload] });
46
39
  } catch (error) {
47
40
  console.error("[ai-billing] Failed to ingest event to Polar:", error);
48
41
  }
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:** Resolves an internal Polar `customerId` and an optional `externalId` from tags. When both are\n * missing, a warning is logged (`No identity found in tags. Skipping event.`), but the destination does not\n * return: it still calls Polar with `customerId: String(internalId)`, which becomes the literal string\n * `\"undefined\"` when the internal id is absent, and it only adds `externalId` when that tag value is truthy.\n * When the internal id is absent but `externalId` is present, no warning is logged, yet `customerId` is\n * still the string `\"undefined\"`.\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 }\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 try {\n await polar.events.ingest({\n events: [\n {\n name: eventName,\n customerId: String(internalId),\n ...(externalId ? { externalId: String(externalId) } : {}),\n metadata,\n },\n ],\n });\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;AAqEA,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;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,QAAI;AACF,YAAM,MAAM,OAAO,OAAO;AAAA,QACxB,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,YAAY,OAAO,UAAU;AAAA,YAC7B,GAAI,aAAa,EAAE,YAAY,OAAO,UAAU,EAAE,IAAI,CAAC;AAAA,YACvD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,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 * - `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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-billing/polar",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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": {