@ai-billing/polar 0.0.1 → 0.0.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/dist/index.cjs +14 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +14 -16
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/index.cjs
CHANGED
|
@@ -33,22 +33,20 @@ function mapEventToPolarMetadata(event) {
|
|
|
33
33
|
model_id: event.modelId,
|
|
34
34
|
provider: event.provider
|
|
35
35
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
metadata[polarKey] = value;
|
|
51
|
-
}
|
|
36
|
+
const usageFields = {
|
|
37
|
+
usage_input_tokens: "inputTokens",
|
|
38
|
+
usage_output_tokens: "outputTokens",
|
|
39
|
+
usage_total_tokens: "totalTokens",
|
|
40
|
+
usage_reasoning_tokens: "reasoningTokens",
|
|
41
|
+
usage_cache_read_tokens: "cacheReadTokens",
|
|
42
|
+
usage_cache_write_tokens: "cacheWriteTokens",
|
|
43
|
+
usage_request_count: "requestCount",
|
|
44
|
+
usage_raw_provider_cost: "rawProviderCost"
|
|
45
|
+
};
|
|
46
|
+
for (const [polarKey, internalKey] of Object.entries(usageFields)) {
|
|
47
|
+
const value = event.usage?.[internalKey];
|
|
48
|
+
if (value !== void 0) {
|
|
49
|
+
metadata[polarKey] = value;
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
if (event.cost) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 { createDestination, costToNumber } from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\n\nfunction mapEventToPolarMetadata<TTags extends DefaultTags = DefaultTags>(\n event: BillingEvent<TTags>,\n): Record<string, string | number | boolean> {\n const metadata: Record<string, string | number | boolean> = {\n generation_id: event.generationId,\n model_id: event.modelId,\n provider: event.provider,\n };\n\n
|
|
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 { createDestination, costToNumber } from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\n\nfunction mapEventToPolarMetadata<TTags extends DefaultTags = DefaultTags>(\n event: BillingEvent<TTags>,\n): Record<string, string | number | boolean> {\n const metadata: Record<string, string | number | boolean> = {\n generation_id: event.generationId,\n model_id: event.modelId,\n provider: event.provider,\n };\n\n const usageFields: Record<string, keyof typeof event.usage> = {\n usage_input_tokens: 'inputTokens',\n usage_output_tokens: 'outputTokens',\n usage_total_tokens: 'totalTokens',\n usage_reasoning_tokens: 'reasoningTokens',\n usage_cache_read_tokens: 'cacheReadTokens',\n usage_cache_write_tokens: 'cacheWriteTokens',\n usage_request_count: 'requestCount',\n usage_raw_provider_cost: 'rawProviderCost',\n };\n\n for (const [polarKey, internalKey] of Object.entries(usageFields)) {\n const value = event.usage?.[internalKey];\n if (value !== undefined) {\n metadata[polarKey] = value;\n }\n }\n\n if (event.cost) {\n metadata.cost_amount_base = costToNumber(event.cost, 'base');\n metadata.cost_amount_cents = costToNumber(event.cost, 'cents');\n metadata.cost_amount_micros = costToNumber(event.cost, 'micros');\n metadata.cost_amount_nanos = costToNumber(event.cost, 'nanos');\n metadata.cost_currency = event.cost.currency;\n }\n\n if (!event.tags) return metadata;\n\n for (const [key, value] of Object.entries(event.tags)) {\n if (value == null) continue; // Skip null/undefined immediately\n\n const metadataKey = `ai-billing-tag_${key}`;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n metadata[metadataKey] = value;\n } else {\n // If it's an object/array, stringify it\n metadata[metadataKey] = JSON.stringify(value);\n }\n }\n\n return metadata;\n}\n\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n client?: Polar;\n accessToken?: string;\n server?: 'sandbox' | 'production';\n meterName: string | ((event: BillingEvent<TTags>) => string);\n\n /** * Custom key to look for in tags for Polar's internal customer ID (cus_...).\n * Defaults to: 'customerId' | 'polarCustomerId'\n */\n customerIdKey?: keyof TTags;\n\n /** * Custom key to look for in tags for your system's ID.\n * Defaults to: 'userId' | 'externalId'\n */\n externalCustomerIdKey?: keyof TTags;\n\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\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 = tags[options.customerIdKey as string] ?? tags.customerId;\n const externalId =\n tags[options.externalCustomerIdKey as string] ?? tags.userId;\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n }\n\n const meterName =\n typeof options.meterName === 'function'\n ? options.meterName(event)\n : options.meterName;\n\n const metadata = options.mapMetadata\n ? options.mapMetadata(event)\n : mapEventToPolarMetadata(event);\n\n await polar.events.ingest({\n events: [\n {\n name: meterName,\n // Priority: Internal Polar ID always wins if both are present\n ...(internalId\n ? { customerId: String(internalId) }\n : { externalCustomerId: String(externalId) }),\n metadata,\n },\n ],\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAsB;AACtB,kBAAgD;AAGhD,SAAS,wBACP,OAC2C;AAC3C,QAAM,WAAsD;AAAA,IAC1D,eAAe,MAAM;AAAA,IACrB,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,EAClB;AAEA,QAAM,cAAwD;AAAA,IAC5D,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,IACxB,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,qBAAqB;AAAA,IACrB,yBAAyB;AAAA,EAC3B;AAEA,aAAW,CAAC,UAAU,WAAW,KAAK,OAAO,QAAQ,WAAW,GAAG;AACjE,UAAM,QAAQ,MAAM,QAAQ,WAAW;AACvC,QAAI,UAAU,QAAW;AACvB,eAAS,QAAQ,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,MAAM,MAAM;AACd,aAAS,uBAAmB,0BAAa,MAAM,MAAM,MAAM;AAC3D,aAAS,wBAAoB,0BAAa,MAAM,MAAM,OAAO;AAC7D,aAAS,yBAAqB,0BAAa,MAAM,MAAM,QAAQ;AAC/D,aAAS,wBAAoB,0BAAa,MAAM,MAAM,OAAO;AAC7D,aAAS,gBAAgB,MAAM,KAAK;AAAA,EACtC;AAEA,MAAI,CAAC,MAAM,KAAM,QAAO;AAExB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,IAAI,GAAG;AACrD,QAAI,SAAS,KAAM;AAEnB,UAAM,cAAc,kBAAkB,GAAG;AACzC,QACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WACjB;AACA,eAAS,WAAW,IAAI;AAAA,IAC1B,OAAO;AAEL,eAAS,WAAW,IAAI,KAAK,UAAU,KAAK;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;AAyBO,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,KAAK,QAAQ,aAAuB,KAAK,KAAK;AACjE,UAAM,aACJ,KAAK,QAAQ,qBAA+B,KAAK,KAAK;AAExD,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,UAAM,WAAW,QAAQ,cACrB,QAAQ,YAAY,KAAK,IACzB,wBAAwB,KAAK;AAEjC,UAAM,MAAM,OAAO,OAAO;AAAA,MACxB,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA;AAAA,UAEN,GAAI,aACA,EAAE,YAAY,OAAO,UAAU,EAAE,IACjC,EAAE,oBAAoB,OAAO,UAAU,EAAE;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -7,22 +7,20 @@ function mapEventToPolarMetadata(event) {
|
|
|
7
7
|
model_id: event.modelId,
|
|
8
8
|
provider: event.provider
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
metadata[polarKey] = value;
|
|
25
|
-
}
|
|
10
|
+
const usageFields = {
|
|
11
|
+
usage_input_tokens: "inputTokens",
|
|
12
|
+
usage_output_tokens: "outputTokens",
|
|
13
|
+
usage_total_tokens: "totalTokens",
|
|
14
|
+
usage_reasoning_tokens: "reasoningTokens",
|
|
15
|
+
usage_cache_read_tokens: "cacheReadTokens",
|
|
16
|
+
usage_cache_write_tokens: "cacheWriteTokens",
|
|
17
|
+
usage_request_count: "requestCount",
|
|
18
|
+
usage_raw_provider_cost: "rawProviderCost"
|
|
19
|
+
};
|
|
20
|
+
for (const [polarKey, internalKey] of Object.entries(usageFields)) {
|
|
21
|
+
const value = event.usage?.[internalKey];
|
|
22
|
+
if (value !== void 0) {
|
|
23
|
+
metadata[polarKey] = value;
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
if (event.cost) {
|
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 { createDestination, costToNumber } from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\n\nfunction mapEventToPolarMetadata<TTags extends DefaultTags = DefaultTags>(\n event: BillingEvent<TTags>,\n): Record<string, string | number | boolean> {\n const metadata: Record<string, string | number | boolean> = {\n generation_id: event.generationId,\n model_id: event.modelId,\n provider: event.provider,\n };\n\n
|
|
1
|
+
{"version":3,"sources":["../src/destination/polar-destination.ts"],"sourcesContent":["import { Polar } from '@polar-sh/sdk';\nimport { createDestination, costToNumber } from '@ai-billing/core';\nimport type { BillingEvent, DefaultTags, Destination } from '@ai-billing/core';\n\nfunction mapEventToPolarMetadata<TTags extends DefaultTags = DefaultTags>(\n event: BillingEvent<TTags>,\n): Record<string, string | number | boolean> {\n const metadata: Record<string, string | number | boolean> = {\n generation_id: event.generationId,\n model_id: event.modelId,\n provider: event.provider,\n };\n\n const usageFields: Record<string, keyof typeof event.usage> = {\n usage_input_tokens: 'inputTokens',\n usage_output_tokens: 'outputTokens',\n usage_total_tokens: 'totalTokens',\n usage_reasoning_tokens: 'reasoningTokens',\n usage_cache_read_tokens: 'cacheReadTokens',\n usage_cache_write_tokens: 'cacheWriteTokens',\n usage_request_count: 'requestCount',\n usage_raw_provider_cost: 'rawProviderCost',\n };\n\n for (const [polarKey, internalKey] of Object.entries(usageFields)) {\n const value = event.usage?.[internalKey];\n if (value !== undefined) {\n metadata[polarKey] = value;\n }\n }\n\n if (event.cost) {\n metadata.cost_amount_base = costToNumber(event.cost, 'base');\n metadata.cost_amount_cents = costToNumber(event.cost, 'cents');\n metadata.cost_amount_micros = costToNumber(event.cost, 'micros');\n metadata.cost_amount_nanos = costToNumber(event.cost, 'nanos');\n metadata.cost_currency = event.cost.currency;\n }\n\n if (!event.tags) return metadata;\n\n for (const [key, value] of Object.entries(event.tags)) {\n if (value == null) continue; // Skip null/undefined immediately\n\n const metadataKey = `ai-billing-tag_${key}`;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n metadata[metadataKey] = value;\n } else {\n // If it's an object/array, stringify it\n metadata[metadataKey] = JSON.stringify(value);\n }\n }\n\n return metadata;\n}\n\nexport interface PolarDestinationOptions<\n TTags extends DefaultTags = DefaultTags,\n> {\n client?: Polar;\n accessToken?: string;\n server?: 'sandbox' | 'production';\n meterName: string | ((event: BillingEvent<TTags>) => string);\n\n /** * Custom key to look for in tags for Polar's internal customer ID (cus_...).\n * Defaults to: 'customerId' | 'polarCustomerId'\n */\n customerIdKey?: keyof TTags;\n\n /** * Custom key to look for in tags for your system's ID.\n * Defaults to: 'userId' | 'externalId'\n */\n externalCustomerIdKey?: keyof TTags;\n\n mapMetadata?: (\n event: BillingEvent<TTags>,\n ) => Record<string, string | number | boolean>;\n}\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 = tags[options.customerIdKey as string] ?? tags.customerId;\n const externalId =\n tags[options.externalCustomerIdKey as string] ?? tags.userId;\n\n if (!internalId && !externalId) {\n console.warn(\n '[ai-billing] Polar: No identity found in tags. Skipping event.',\n );\n }\n\n const meterName =\n typeof options.meterName === 'function'\n ? options.meterName(event)\n : options.meterName;\n\n const metadata = options.mapMetadata\n ? options.mapMetadata(event)\n : mapEventToPolarMetadata(event);\n\n await polar.events.ingest({\n events: [\n {\n name: meterName,\n // Priority: Internal Polar ID always wins if both are present\n ...(internalId\n ? { customerId: String(internalId) }\n : { externalCustomerId: String(externalId) }),\n metadata,\n },\n ],\n });\n });\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB,SAAS,mBAAmB,oBAAoB;AAGhD,SAAS,wBACP,OAC2C;AAC3C,QAAM,WAAsD;AAAA,IAC1D,eAAe,MAAM;AAAA,IACrB,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,EAClB;AAEA,QAAM,cAAwD;AAAA,IAC5D,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,IACxB,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,qBAAqB;AAAA,IACrB,yBAAyB;AAAA,EAC3B;AAEA,aAAW,CAAC,UAAU,WAAW,KAAK,OAAO,QAAQ,WAAW,GAAG;AACjE,UAAM,QAAQ,MAAM,QAAQ,WAAW;AACvC,QAAI,UAAU,QAAW;AACvB,eAAS,QAAQ,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,MAAM,MAAM;AACd,aAAS,mBAAmB,aAAa,MAAM,MAAM,MAAM;AAC3D,aAAS,oBAAoB,aAAa,MAAM,MAAM,OAAO;AAC7D,aAAS,qBAAqB,aAAa,MAAM,MAAM,QAAQ;AAC/D,aAAS,oBAAoB,aAAa,MAAM,MAAM,OAAO;AAC7D,aAAS,gBAAgB,MAAM,KAAK;AAAA,EACtC;AAEA,MAAI,CAAC,MAAM,KAAM,QAAO;AAExB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,IAAI,GAAG;AACrD,QAAI,SAAS,KAAM;AAEnB,UAAM,cAAc,kBAAkB,GAAG;AACzC,QACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WACjB;AACA,eAAS,WAAW,IAAI;AAAA,IAC1B,OAAO;AAEL,eAAS,WAAW,IAAI,KAAK,UAAU,KAAK;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;AAyBO,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,KAAK,QAAQ,aAAuB,KAAK,KAAK;AACjE,UAAM,aACJ,KAAK,QAAQ,qBAA+B,KAAK,KAAK;AAExD,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,UAAM,WAAW,QAAQ,cACrB,QAAQ,YAAY,KAAK,IACzB,wBAAwB,KAAK;AAEjC,UAAM,MAAM,OAAO,OAAO;AAAA,MACxB,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA;AAAA,UAEN,GAAI,aACA,EAAE,YAAY,OAAO,UAAU,EAAE,IACjC,EAAE,oBAAoB,OAAO,UAAU,EAAE;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-billing/polar",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/narevai/ai-billing.git",
|
|
9
|
-
"directory": "packages/
|
|
9
|
+
"directory": "packages/polar"
|
|
10
10
|
},
|
|
11
11
|
"main": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
"typescript": "5.9.2",
|
|
36
36
|
"vitest": "4.1.1",
|
|
37
37
|
"@edge-runtime/vm": "^5.0.0",
|
|
38
|
-
"
|
|
38
|
+
"zod": "^4.3.6",
|
|
39
|
+
"@ai-billing/typescript-config": "0.0.1",
|
|
40
|
+
"@ai-billing/testing": "0.0.2"
|
|
39
41
|
},
|
|
40
42
|
"peerDependencies": {
|
|
41
|
-
"@ai-billing/core": "0.0.
|
|
43
|
+
"@ai-billing/core": "0.0.3",
|
|
42
44
|
"@polar-sh/sdk": "^0.46.7"
|
|
43
45
|
},
|
|
44
46
|
"engines": {
|
|
@@ -50,6 +52,7 @@
|
|
|
50
52
|
"lint": "oxlint",
|
|
51
53
|
"dev": "tsup --watch",
|
|
52
54
|
"test": "vitest run",
|
|
53
|
-
"test:watch": "vitest"
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"test:coverage": "vitest run --coverage"
|
|
54
57
|
}
|
|
55
58
|
}
|