@openmeter/sdk 1.0.0-alpha.4 → 1.0.0-beta.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.
Files changed (88) hide show
  1. package/README.md +42 -46
  2. package/clients/client.ts +153 -0
  3. package/clients/event.ts +109 -0
  4. package/clients/meter.ts +84 -0
  5. package/dist/clients/client.d.ts +40 -0
  6. package/dist/clients/client.js +101 -0
  7. package/dist/clients/event.d.ts +65 -0
  8. package/dist/clients/event.js +38 -0
  9. package/dist/clients/meter.d.ts +52 -0
  10. package/dist/clients/meter.js +52 -0
  11. package/dist/index.d.ts +10 -22
  12. package/dist/index.js +7 -25
  13. package/dist/schemas/openapi.d.ts +414 -0
  14. package/dist/schemas/openapi.js +5 -0
  15. package/dist/test/agent.d.ts +2 -0
  16. package/dist/test/agent.js +105 -0
  17. package/dist/test/mocks.d.ts +11 -0
  18. package/dist/test/mocks.js +21 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/index.ts +13 -49
  21. package/package.json +53 -56
  22. package/schemas/openapi.ts +421 -0
  23. package/dist/generated/HttpService.d.ts +0 -14
  24. package/dist/generated/HttpService.js +0 -27
  25. package/dist/generated/core/ApiError.d.ts +0 -10
  26. package/dist/generated/core/ApiError.js +0 -17
  27. package/dist/generated/core/ApiRequestOptions.d.ts +0 -13
  28. package/dist/generated/core/ApiRequestOptions.js +0 -2
  29. package/dist/generated/core/ApiResult.d.ts +0 -7
  30. package/dist/generated/core/ApiResult.js +0 -2
  31. package/dist/generated/core/BaseHttpRequest.d.ts +0 -8
  32. package/dist/generated/core/BaseHttpRequest.js +0 -7
  33. package/dist/generated/core/CancelablePromise.d.ts +0 -20
  34. package/dist/generated/core/CancelablePromise.js +0 -93
  35. package/dist/generated/core/NodeHttpRequest.d.ts +0 -14
  36. package/dist/generated/core/NodeHttpRequest.js +0 -17
  37. package/dist/generated/core/OpenAPI.d.ts +0 -16
  38. package/dist/generated/core/OpenAPI.js +0 -12
  39. package/dist/generated/core/request.d.ts +0 -34
  40. package/dist/generated/core/request.js +0 -268
  41. package/dist/generated/index.d.ts +0 -29
  42. package/dist/generated/index.js +0 -22
  43. package/dist/generated/models/Event.d.ts +0 -49
  44. package/dist/generated/models/Event.js +0 -11
  45. package/dist/generated/models/IdOrSlug.d.ts +0 -1
  46. package/dist/generated/models/IdOrSlug.js +0 -2
  47. package/dist/generated/models/Meter.d.ts +0 -30
  48. package/dist/generated/models/Meter.js +0 -2
  49. package/dist/generated/models/MeterAggregation.d.ts +0 -10
  50. package/dist/generated/models/MeterAggregation.js +0 -12
  51. package/dist/generated/models/MeterValue.d.ts +0 -10
  52. package/dist/generated/models/MeterValue.js +0 -2
  53. package/dist/generated/models/Namespace.d.ts +0 -6
  54. package/dist/generated/models/Namespace.js +0 -2
  55. package/dist/generated/models/Problem.d.ts +0 -4
  56. package/dist/generated/models/Problem.js +0 -2
  57. package/dist/generated/models/WindowSize.d.ts +0 -5
  58. package/dist/generated/models/WindowSize.js +0 -7
  59. package/dist/generated/models/meterIdOrSlug.d.ts +0 -5
  60. package/dist/generated/models/meterIdOrSlug.js +0 -2
  61. package/dist/generated/models/namespaceParam.d.ts +0 -4
  62. package/dist/generated/models/namespaceParam.js +0 -2
  63. package/dist/generated/schemas/$Event.d.ts +0 -59
  64. package/dist/generated/schemas/$Event.js +0 -60
  65. package/dist/generated/schemas/$IdOrSlug.d.ts +0 -13
  66. package/dist/generated/schemas/$IdOrSlug.js +0 -14
  67. package/dist/generated/schemas/$Meter.d.ts +0 -44
  68. package/dist/generated/schemas/$Meter.js +0 -45
  69. package/dist/generated/schemas/$MeterAggregation.d.ts +0 -3
  70. package/dist/generated/schemas/$MeterAggregation.js +0 -4
  71. package/dist/generated/schemas/$MeterValue.d.ts +0 -26
  72. package/dist/generated/schemas/$MeterValue.js +0 -27
  73. package/dist/generated/schemas/$Namespace.d.ts +0 -9
  74. package/dist/generated/schemas/$Namespace.js +0 -10
  75. package/dist/generated/schemas/$Problem.d.ts +0 -6
  76. package/dist/generated/schemas/$Problem.js +0 -7
  77. package/dist/generated/schemas/$WindowSize.d.ts +0 -3
  78. package/dist/generated/schemas/$WindowSize.js +0 -4
  79. package/dist/generated/schemas/$meterIdOrSlug.d.ts +0 -4
  80. package/dist/generated/schemas/$meterIdOrSlug.js +0 -5
  81. package/dist/generated/schemas/$namespaceParam.d.ts +0 -5
  82. package/dist/generated/schemas/$namespaceParam.js +0 -6
  83. package/dist/generated/services/DefaultService.d.ts +0 -16
  84. package/dist/generated/services/DefaultService.js +0 -22
  85. package/dist/generated/services/EventsService.d.ts +0 -17
  86. package/dist/generated/services/EventsService.js +0 -29
  87. package/dist/generated/services/MetersService.d.ts +0 -75
  88. package/dist/generated/services/MetersService.js +0 -140
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ## Install
4
4
 
5
5
  ```sh
6
- npm install --save @openmeter/sdk@alpha
6
+ npm install --save @openmeter/sdk@beta
7
7
  ```
8
8
 
9
9
  ## Example
@@ -15,78 +15,74 @@ const openmeter = new OpenMeter({ baseUrl: 'http://localhost:8888' })
15
15
 
16
16
  // Ingesting an event
17
17
  const event: Event = {
18
- specversion: '1.0',
19
- id: 'id-1',
20
- source: 'my-app',
21
- type: 'my-type',
22
- subject: 'my-awesome-user-id',
23
- time: new Date().toISOString(),
24
- data: {
25
- api_calls: 1,
26
- },
18
+ specversion: '1.0',
19
+ id: 'id-1',
20
+ source: 'my-app',
21
+ type: 'my-type',
22
+ subject: 'my-awesome-user-id',
23
+ time: new Date(),
24
+ data: {
25
+ api_calls: 1,
26
+ },
27
27
  }
28
- await openmeter.events.ingestEvents(event)
28
+ await openmeter.events.ingest(event)
29
29
 
30
30
  // Fetching a meter
31
- const meter = await openmeter.meters.getMeter('m1')
31
+ const meter = await openmeter.meters.get('m1')
32
32
  ```
33
33
 
34
34
  ## API
35
35
 
36
- The OpenMeter SDK uses [openapi-typescript-codegen](https://www.npmjs.com/package/openapi-typescript-codegen) under the hood to generate the HTTP client.
37
-
38
36
  ### Events
39
37
 
40
- #### ingestEvents
38
+ #### ingest
41
39
 
42
40
  ```ts
43
41
  import { type Event } from '@openmeter/sdk'
44
42
 
45
43
  const event: Event = {
46
- specversion: '1.0',
47
- id: 'id-1',
48
- source: 'my-app',
49
- type: 'my-type',
50
- subject: 'my-awesome-user-id',
51
- time: new Date().toISOString(),
52
- data: {
53
- api_calls: 1,
54
- },
44
+ specversion: '1.0',
45
+ id: 'id-1',
46
+ source: 'my-app',
47
+ type: 'my-type',
48
+ subject: 'my-awesome-user-id',
49
+ time: new Date(),
50
+ data: {
51
+ api_calls: 1,
52
+ },
55
53
  }
56
- await openmeter.events.ingestEvents(event)
54
+ await openmeter.events.ingest(event)
57
55
  ```
58
56
 
59
57
  ### Meters
60
58
 
61
- #### listMeters
59
+ #### list
60
+
61
+ List meters.
62
62
 
63
63
  ```ts
64
- const meters = await openmeter.meters.listMeters()
64
+ const meters = await openmeter.meters.list()
65
65
  ```
66
66
 
67
- #### getMeter
67
+ #### get
68
+
69
+ Get one meter by slug.
68
70
 
69
71
  ```ts
70
- const meter = await openmeter.meters.getMeter('m1')
72
+ const meter = await openmeter.meters.get('m1')
71
73
  ```
72
74
 
73
- #### getMeterValues
75
+ #### values
76
+
77
+ Get back meter values.
74
78
 
75
79
  ```ts
76
- import { type WindowSize } from '@openmeter/sdk'
77
-
78
- const meterSlug = 'm2'
79
- const namespace = undefined
80
- const subject = 'user-1'
81
- const from = new Date('2021-01-01').toISOString()
82
- const to = new Date('2021-01-02').toISOString()
83
- const windowSize = WindowSize.HOUR
84
- const values = await openmeter.meters.getMeterValues(
85
- meterSlug,
86
- namespace,
87
- subject,
88
- from,
89
- to,
90
- windowSize
91
- )
80
+ import { WindowSize } from '@openmeter/sdk'
81
+
82
+ const values = await openmeter.meters.values('my-meter-slug', {
83
+ subject: 'user-1',
84
+ from: new Date('2021-01-01'),
85
+ to: new Date('2021-01-02'),
86
+ windowSize: WindowSize.HOUR
87
+ })
92
88
  ```
@@ -0,0 +1,153 @@
1
+ import { IncomingHttpHeaders } from 'http'
2
+ import { Dispatcher, request } from 'undici'
3
+ import { components } from '../schemas/openapi.js'
4
+
5
+ export type OpenMeterConfig = {
6
+ baseUrl: string
7
+ token?: string
8
+ username?: string
9
+ password?: string
10
+ headers?: IncomingHttpHeaders
11
+ }
12
+
13
+ export type RequestOptions = {
14
+ headers?: IncomingHttpHeaders
15
+ }
16
+
17
+ export type Problem = components['schemas']['Problem']
18
+
19
+ type UndiciRequestOptions = { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>
20
+
21
+ export class BaseClient {
22
+ protected config: OpenMeterConfig
23
+
24
+ constructor(config: OpenMeterConfig) {
25
+ this.config = config
26
+ }
27
+
28
+ protected async request<T>({
29
+ path,
30
+ method,
31
+ searchParams,
32
+ headers,
33
+ body,
34
+ options
35
+ }: {
36
+ path: string
37
+ method: Dispatcher.HttpMethod,
38
+ searchParams?: URLSearchParams,
39
+ headers?: IncomingHttpHeaders,
40
+ body?: string | Buffer | Uint8Array,
41
+ options?: RequestOptions
42
+ }): Promise<T> {
43
+ // Building URL
44
+ const url = this.getUrl(path, searchParams)
45
+
46
+ // Request options
47
+ const reqHeaders: IncomingHttpHeaders = {
48
+ Accept: 'application/json',
49
+ ...headers,
50
+ ...this.getAuthHeaders(),
51
+ ...this.config.headers,
52
+ ...options?.headers,
53
+ }
54
+ const reqOpts: UndiciRequestOptions = {
55
+ method,
56
+ headers: reqHeaders
57
+ }
58
+
59
+ // Optional body
60
+ if (body) {
61
+ if (!reqHeaders['Content-Type'] && !reqHeaders['content-type']) {
62
+ throw new Error('Content Type is required with body')
63
+ }
64
+
65
+ reqOpts.body = body
66
+ }
67
+
68
+ const resp = await request(url, reqOpts)
69
+
70
+ // Error handling
71
+ if (resp.statusCode > 399) {
72
+ if (resp.headers['content-type'] === 'application/problem+json') {
73
+ const problem = await resp.body.json() as Problem
74
+ throw new HttpError({
75
+ statusCode: resp.statusCode,
76
+ problem,
77
+ })
78
+ }
79
+
80
+ // Requests can fail before API, in this case we only have a status code
81
+ throw new HttpError({
82
+ statusCode: resp.statusCode,
83
+ })
84
+ }
85
+
86
+ // Response parsing
87
+ if (resp.statusCode === 204) {
88
+ return undefined as unknown as T
89
+ }
90
+ if (resp.headers['content-type'] === 'application/json') {
91
+ return await resp.body.json() as T
92
+ }
93
+ if (!resp.headers['content-type']) {
94
+ throw new Error('Missing content type')
95
+ }
96
+
97
+ throw new Error(`Unknown content type: ${resp.headers['content-type']}`)
98
+ }
99
+
100
+ protected getUrl(path: string, searchParams?: URLSearchParams) {
101
+ let qs = searchParams ? searchParams.toString() : ''
102
+ qs = qs.length > 0 ? `?${qs}` : ''
103
+ const url = new URL(`${path}${qs}`, this.config.baseUrl)
104
+ return url
105
+ }
106
+
107
+ protected getAuthHeaders(): IncomingHttpHeaders {
108
+ if (this.config.token) {
109
+ return {
110
+ authorization: `Bearer ${this.config.token} `,
111
+ }
112
+ }
113
+
114
+ if (this.config.username && this.config.password) {
115
+ const encoded = Buffer.from(
116
+ `${this.config.username}:${this.config.password} `
117
+ ).toString('base64')
118
+ return {
119
+ authorization: `Basic ${encoded} `,
120
+ }
121
+ }
122
+
123
+ return {}
124
+ }
125
+
126
+ protected static toURLSearchParams(params: Record<string, string | number | Date | string[]>): URLSearchParams {
127
+ const searchParams = new URLSearchParams()
128
+
129
+ for (const [key, value] of Object.entries(params)) {
130
+ if (Array.isArray(value)) {
131
+ searchParams.append(key, value.join(','))
132
+ } else if (value instanceof Date) {
133
+ searchParams.append(key, value.toISOString())
134
+ } else {
135
+ searchParams.append(key, value.toString())
136
+ }
137
+ }
138
+
139
+ return searchParams
140
+ }
141
+ }
142
+
143
+ export class HttpError extends Error {
144
+ public statusCode: number
145
+ public problem?: Problem
146
+
147
+ constructor({ statusCode, problem }: { statusCode: number; problem?: Problem }) {
148
+ super(problem?.type || 'unexpected status code')
149
+ this.name = 'HttpError'
150
+ this.statusCode = statusCode
151
+ this.problem = problem
152
+ }
153
+ }
@@ -0,0 +1,109 @@
1
+ import crypto from 'crypto'
2
+ import { components } from '../schemas/openapi.js'
3
+ import { RequestOptions, BaseClient, OpenMeterConfig } from './client.js'
4
+
5
+ // We export Event instead
6
+ type CloudEvents = components['schemas']['Event']
7
+
8
+ /**
9
+ * Usage Event
10
+ */
11
+ export type Event = {
12
+ /**
13
+ * @description The version of the CloudEvents specification which the event uses.
14
+ * @example 1.0
15
+ */
16
+ specversion?: string
17
+ /**
18
+ * @description Unique identifier for the event, defaults to uuid v4.
19
+ * @example "5c10fade-1c9e-4d6c-8275-c52c36731d3c"
20
+ */
21
+ id?: string
22
+ /**
23
+ * Format: uri-reference
24
+ * @description Identifies the context in which an event happened, defaults to: @openmeter/sdk
25
+ * @example services/service-0
26
+ */
27
+ source?: string
28
+ /**
29
+ * @description Describes the type of event related to the originating occurrence.
30
+ * @example "api_request"
31
+ */
32
+ type: string
33
+ /**
34
+ * @description Describes the subject of the event in the context of the event producer (identified by source).
35
+ * @example "customer_id"
36
+ */
37
+ subject: string
38
+ /**
39
+ * Format: date-time
40
+ * @description Date of when the occurrence happened.
41
+ * @example new Date('2023-01-01T01:01:01.001Z')
42
+ */
43
+ time?: Date
44
+ /**
45
+ * Format: uri
46
+ * @description Identifies the schema that data adheres to.
47
+ */
48
+ dataschema?: string
49
+ /**
50
+ * @description Content type of the data value. Must adhere to RFC 2046 format.
51
+ * @example application/json
52
+ * @enum {string|null}
53
+ */
54
+ datacontenttype?: 'application/json'
55
+ /**
56
+ * @description The event payload.
57
+ * @example {
58
+ * "duration_ms": "12",
59
+ * "path": "/hello"
60
+ * }
61
+ */
62
+ data: Record<string, string | number | Record<string, string | number>>
63
+ }
64
+
65
+ export class EventsClient extends BaseClient {
66
+ constructor(config: OpenMeterConfig) {
67
+ super(config)
68
+ }
69
+
70
+ /**
71
+ * Ingest usage event in a CloudEvents format
72
+ * @see https://cloudevents.io
73
+ */
74
+ public async ingest(
75
+ usageEvent: Event,
76
+ options?: RequestOptions
77
+ ): Promise<void> {
78
+ if (usageEvent.datacontenttype && usageEvent.datacontenttype !== 'application/json') {
79
+ throw new TypeError(
80
+ `Unsupported datacontenttype: ${usageEvent.datacontenttype}`
81
+ )
82
+ }
83
+
84
+ // We default where we can to lower the barrier to use CloudEvents
85
+ const body: CloudEvents = {
86
+ specversion: usageEvent.specversion ?? '1.0',
87
+ id: usageEvent.id ?? crypto.randomUUID(),
88
+ source: usageEvent.source ?? '@openmeter/sdk',
89
+ type: usageEvent.type,
90
+ subject: usageEvent.subject,
91
+ time: usageEvent.time?.toISOString(),
92
+ datacontenttype: usageEvent.datacontenttype,
93
+ dataschema: usageEvent.dataschema,
94
+ data: usageEvent.data
95
+ }
96
+
97
+ // Making Request
98
+ return await this.request({
99
+ path: '/api/v1/events',
100
+ method: 'POST',
101
+ body: JSON.stringify(body),
102
+ headers: {
103
+ 'Content-Type': 'application/cloudevents+json',
104
+ },
105
+ options
106
+ })
107
+ }
108
+ }
109
+
@@ -0,0 +1,84 @@
1
+ import { paths, components } from '../schemas/openapi.js'
2
+ import { BaseClient, OpenMeterConfig, RequestOptions } from './client.js'
3
+
4
+ export enum WindowSize {
5
+ MINUTE = 'MINUTE',
6
+ HOUR = 'HOUR',
7
+ DAY = 'DAY'
8
+ }
9
+
10
+ export enum MeterAggregation {
11
+ SUM = 'SUM',
12
+ COUNT = 'COUNT',
13
+ AVG = 'AVG',
14
+ MIN = 'MIN',
15
+ MAX = 'MAX',
16
+ }
17
+
18
+ export type MeterQueryParams = {
19
+ subject?: string
20
+ /**
21
+ * @description Start date.
22
+ * Must be aligned with the window size.
23
+ * Inclusive.
24
+ */
25
+ from?: Date
26
+ /**
27
+ * @description End date.
28
+ * Must be aligned with the window size.
29
+ * Inclusive.
30
+ */
31
+ to?: Date
32
+ /** @description If not specified, a single usage aggregate will be returned for the entirety of the specified period for each subject and group. */
33
+ windowSize?: WindowSizeType
34
+ /** @description If not specified a single aggregate will be returned for each subject and time window. */
35
+ groupBy?: string[]
36
+ }
37
+
38
+ export type MeterQueryResponse = paths['/api/v1/meters/{meterIdOrSlug}/values']['get']['responses']['200']['content']['application/json']
39
+
40
+ export type MeterValue = components['schemas']['MeterValue']
41
+ export type Meter = components['schemas']['Meter']
42
+ export type WindowSizeType = components['schemas']['WindowSize']
43
+
44
+ export class MetersClient extends BaseClient {
45
+ constructor(config: OpenMeterConfig) {
46
+ super(config)
47
+ }
48
+
49
+ /**
50
+ * Get one meter by slug
51
+ */
52
+ public async get(slug: string, options?: RequestOptions): Promise<Meter> {
53
+ return this.request<Meter>({
54
+ method: 'GET',
55
+ path: `/api/v1/meters/${slug}`,
56
+ options,
57
+ })
58
+ }
59
+
60
+ /**
61
+ * List meters
62
+ */
63
+ public async list(options?: RequestOptions): Promise<Meter[]> {
64
+ return this.request<Meter[]>({
65
+ method: 'GET',
66
+ path: `/api/v1/meters`,
67
+ options,
68
+ })
69
+ }
70
+
71
+ /**
72
+ * Get aggregated values of a meter
73
+ */
74
+ public async values(slug: string, params?: MeterQueryParams, options?: RequestOptions): Promise<MeterQueryResponse> {
75
+ const searchParams = params ? BaseClient.toURLSearchParams(params) : undefined
76
+ return this.request<MeterQueryResponse>({
77
+ method: 'GET',
78
+ path: `/api/v1/meters/${slug}/values`,
79
+ searchParams,
80
+ options,
81
+ })
82
+ }
83
+ }
84
+
@@ -0,0 +1,40 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ import { IncomingHttpHeaders } from 'http';
5
+ import { Dispatcher } from 'undici';
6
+ import { components } from '../schemas/openapi.js';
7
+ export type OpenMeterConfig = {
8
+ baseUrl: string;
9
+ token?: string;
10
+ username?: string;
11
+ password?: string;
12
+ headers?: IncomingHttpHeaders;
13
+ };
14
+ export type RequestOptions = {
15
+ headers?: IncomingHttpHeaders;
16
+ };
17
+ export type Problem = components['schemas']['Problem'];
18
+ export declare class BaseClient {
19
+ protected config: OpenMeterConfig;
20
+ constructor(config: OpenMeterConfig);
21
+ protected request<T>({ path, method, searchParams, headers, body, options }: {
22
+ path: string;
23
+ method: Dispatcher.HttpMethod;
24
+ searchParams?: URLSearchParams;
25
+ headers?: IncomingHttpHeaders;
26
+ body?: string | Buffer | Uint8Array;
27
+ options?: RequestOptions;
28
+ }): Promise<T>;
29
+ protected getUrl(path: string, searchParams?: URLSearchParams): import("url").URL;
30
+ protected getAuthHeaders(): IncomingHttpHeaders;
31
+ protected static toURLSearchParams(params: Record<string, string | number | Date | string[]>): URLSearchParams;
32
+ }
33
+ export declare class HttpError extends Error {
34
+ statusCode: number;
35
+ problem?: Problem;
36
+ constructor({ statusCode, problem }: {
37
+ statusCode: number;
38
+ problem?: Problem;
39
+ });
40
+ }
@@ -0,0 +1,101 @@
1
+ import { request } from 'undici';
2
+ export class BaseClient {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ async request({ path, method, searchParams, headers, body, options }) {
8
+ // Building URL
9
+ const url = this.getUrl(path, searchParams);
10
+ // Request options
11
+ const reqHeaders = {
12
+ Accept: 'application/json',
13
+ ...headers,
14
+ ...this.getAuthHeaders(),
15
+ ...this.config.headers,
16
+ ...options?.headers,
17
+ };
18
+ const reqOpts = {
19
+ method,
20
+ headers: reqHeaders
21
+ };
22
+ // Optional body
23
+ if (body) {
24
+ if (!reqHeaders['Content-Type'] && !reqHeaders['content-type']) {
25
+ throw new Error('Content Type is required with body');
26
+ }
27
+ reqOpts.body = body;
28
+ }
29
+ const resp = await request(url, reqOpts);
30
+ // Error handling
31
+ if (resp.statusCode > 399) {
32
+ if (resp.headers['content-type'] === 'application/problem+json') {
33
+ const problem = await resp.body.json();
34
+ throw new HttpError({
35
+ statusCode: resp.statusCode,
36
+ problem,
37
+ });
38
+ }
39
+ // Requests can fail before API, in this case we only have a status code
40
+ throw new HttpError({
41
+ statusCode: resp.statusCode,
42
+ });
43
+ }
44
+ // Response parsing
45
+ if (resp.statusCode === 204) {
46
+ return undefined;
47
+ }
48
+ if (resp.headers['content-type'] === 'application/json') {
49
+ return await resp.body.json();
50
+ }
51
+ if (!resp.headers['content-type']) {
52
+ throw new Error('Missing content type');
53
+ }
54
+ throw new Error(`Unknown content type: ${resp.headers['content-type']}`);
55
+ }
56
+ getUrl(path, searchParams) {
57
+ let qs = searchParams ? searchParams.toString() : '';
58
+ qs = qs.length > 0 ? `?${qs}` : '';
59
+ const url = new URL(`${path}${qs}`, this.config.baseUrl);
60
+ return url;
61
+ }
62
+ getAuthHeaders() {
63
+ if (this.config.token) {
64
+ return {
65
+ authorization: `Bearer ${this.config.token} `,
66
+ };
67
+ }
68
+ if (this.config.username && this.config.password) {
69
+ const encoded = Buffer.from(`${this.config.username}:${this.config.password} `).toString('base64');
70
+ return {
71
+ authorization: `Basic ${encoded} `,
72
+ };
73
+ }
74
+ return {};
75
+ }
76
+ static toURLSearchParams(params) {
77
+ const searchParams = new URLSearchParams();
78
+ for (const [key, value] of Object.entries(params)) {
79
+ if (Array.isArray(value)) {
80
+ searchParams.append(key, value.join(','));
81
+ }
82
+ else if (value instanceof Date) {
83
+ searchParams.append(key, value.toISOString());
84
+ }
85
+ else {
86
+ searchParams.append(key, value.toString());
87
+ }
88
+ }
89
+ return searchParams;
90
+ }
91
+ }
92
+ export class HttpError extends Error {
93
+ statusCode;
94
+ problem;
95
+ constructor({ statusCode, problem }) {
96
+ super(problem?.type || 'unexpected status code');
97
+ this.name = 'HttpError';
98
+ this.statusCode = statusCode;
99
+ this.problem = problem;
100
+ }
101
+ }
@@ -0,0 +1,65 @@
1
+ import { RequestOptions, BaseClient, OpenMeterConfig } from './client.js';
2
+ /**
3
+ * Usage Event
4
+ */
5
+ export type Event = {
6
+ /**
7
+ * @description The version of the CloudEvents specification which the event uses.
8
+ * @example 1.0
9
+ */
10
+ specversion?: string;
11
+ /**
12
+ * @description Unique identifier for the event, defaults to uuid v4.
13
+ * @example "5c10fade-1c9e-4d6c-8275-c52c36731d3c"
14
+ */
15
+ id?: string;
16
+ /**
17
+ * Format: uri-reference
18
+ * @description Identifies the context in which an event happened, defaults to: @openmeter/sdk
19
+ * @example services/service-0
20
+ */
21
+ source?: string;
22
+ /**
23
+ * @description Describes the type of event related to the originating occurrence.
24
+ * @example "api_request"
25
+ */
26
+ type: string;
27
+ /**
28
+ * @description Describes the subject of the event in the context of the event producer (identified by source).
29
+ * @example "customer_id"
30
+ */
31
+ subject: string;
32
+ /**
33
+ * Format: date-time
34
+ * @description Date of when the occurrence happened.
35
+ * @example new Date('2023-01-01T01:01:01.001Z')
36
+ */
37
+ time?: Date;
38
+ /**
39
+ * Format: uri
40
+ * @description Identifies the schema that data adheres to.
41
+ */
42
+ dataschema?: string;
43
+ /**
44
+ * @description Content type of the data value. Must adhere to RFC 2046 format.
45
+ * @example application/json
46
+ * @enum {string|null}
47
+ */
48
+ datacontenttype?: 'application/json';
49
+ /**
50
+ * @description The event payload.
51
+ * @example {
52
+ * "duration_ms": "12",
53
+ * "path": "/hello"
54
+ * }
55
+ */
56
+ data: Record<string, string | number | Record<string, string | number>>;
57
+ };
58
+ export declare class EventsClient extends BaseClient {
59
+ constructor(config: OpenMeterConfig);
60
+ /**
61
+ * Ingest usage event in a CloudEvents format
62
+ * @see https://cloudevents.io
63
+ */
64
+ ingest(usageEvent: Event, options?: RequestOptions): Promise<void>;
65
+ }