@reopt-ai/data-contract 0.2.0 → 0.5.0

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 (56) hide show
  1. package/README.md +11 -4
  2. package/dist/{chunk-IKOEYZVT.js → chunk-2LZBZXBF.js} +9 -3
  3. package/dist/chunk-2LZBZXBF.js.map +1 -0
  4. package/dist/{chunk-LBSBZO7O.cjs → chunk-4B4AY44F.cjs} +33 -7
  5. package/dist/chunk-4B4AY44F.cjs.map +1 -0
  6. package/dist/{chunk-2SJPZP2D.js → chunk-AHXPQQKO.js} +30 -4
  7. package/dist/chunk-AHXPQQKO.js.map +1 -0
  8. package/dist/{chunk-SC7TFGTO.js → chunk-BCGR27G2.js} +130 -14
  9. package/dist/chunk-BCGR27G2.js.map +1 -0
  10. package/dist/{chunk-FRJK7OCQ.js → chunk-MFZEIH5Z.js} +7 -4
  11. package/dist/chunk-MFZEIH5Z.js.map +1 -0
  12. package/dist/{chunk-CSTC3ADG.cjs → chunk-OLSMH4RY.cjs} +9 -3
  13. package/dist/chunk-OLSMH4RY.cjs.map +1 -0
  14. package/dist/{chunk-GCLUMVPA.cjs → chunk-VQ2CD5MG.cjs} +138 -22
  15. package/dist/chunk-VQ2CD5MG.cjs.map +1 -0
  16. package/dist/{chunk-HRKELGGH.cjs → chunk-XI3VP5DN.cjs} +7 -4
  17. package/dist/chunk-XI3VP5DN.cjs.map +1 -0
  18. package/dist/client.cjs +94 -44
  19. package/dist/client.cjs.map +1 -1
  20. package/dist/client.d.cts +8 -4
  21. package/dist/client.d.ts +8 -4
  22. package/dist/client.js +58 -8
  23. package/dist/client.js.map +1 -1
  24. package/dist/control.cjs +14 -4
  25. package/dist/control.cjs.map +1 -1
  26. package/dist/control.d.cts +49 -7
  27. package/dist/control.d.ts +49 -7
  28. package/dist/control.js +15 -5
  29. package/dist/index.cjs +2 -2
  30. package/dist/index.d.cts +15 -4
  31. package/dist/index.d.ts +15 -4
  32. package/dist/index.js +1 -1
  33. package/dist/ingest.cjs +2 -2
  34. package/dist/ingest.d.cts +9 -7
  35. package/dist/ingest.d.ts +9 -7
  36. package/dist/ingest.js +1 -1
  37. package/dist/query.cjs +29 -3
  38. package/dist/query.cjs.map +1 -1
  39. package/dist/query.d.cts +317 -21
  40. package/dist/query.d.ts +317 -21
  41. package/dist/query.js +30 -4
  42. package/dist/report.cjs +85 -0
  43. package/dist/report.cjs.map +1 -0
  44. package/dist/report.d.cts +167 -0
  45. package/dist/report.d.ts +167 -0
  46. package/dist/report.js +85 -0
  47. package/dist/report.js.map +1 -0
  48. package/package.json +7 -2
  49. package/dist/chunk-2SJPZP2D.js.map +0 -1
  50. package/dist/chunk-CSTC3ADG.cjs.map +0 -1
  51. package/dist/chunk-FRJK7OCQ.js.map +0 -1
  52. package/dist/chunk-GCLUMVPA.cjs.map +0 -1
  53. package/dist/chunk-HRKELGGH.cjs.map +0 -1
  54. package/dist/chunk-IKOEYZVT.js.map +0 -1
  55. package/dist/chunk-LBSBZO7O.cjs.map +0 -1
  56. package/dist/chunk-SC7TFGTO.js.map +0 -1
package/dist/client.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkHRKELGGHcjs = require('./chunk-HRKELGGH.cjs');
3
+ var _chunkXI3VP5DNcjs = require('./chunk-XI3VP5DN.cjs');
4
4
 
5
5
 
6
6
 
@@ -11,20 +11,24 @@ var _chunkHRKELGGHcjs = require('./chunk-HRKELGGH.cjs');
11
11
 
12
12
 
13
13
 
14
- var _chunkLBSBZO7Ocjs = require('./chunk-LBSBZO7O.cjs');
15
14
 
15
+ var _chunk4B4AY44Fcjs = require('./chunk-4B4AY44F.cjs');
16
16
 
17
17
 
18
18
 
19
19
 
20
- var _chunkGCLUMVPAcjs = require('./chunk-GCLUMVPA.cjs');
21
20
 
22
21
 
23
22
 
23
+ var _chunkVQ2CD5MGcjs = require('./chunk-VQ2CD5MG.cjs');
24
24
 
25
25
 
26
26
 
27
- var _chunkCSTC3ADGcjs = require('./chunk-CSTC3ADG.cjs');
27
+
28
+
29
+
30
+
31
+ var _chunkOLSMH4RYcjs = require('./chunk-OLSMH4RY.cjs');
28
32
 
29
33
  // src/client.ts
30
34
  var TRACK_PATH = "/api/track";
@@ -72,7 +76,7 @@ function toApiError(status, rawBody, retryAfterMs, requestId) {
72
76
  try {
73
77
  parsed = JSON.parse(rawBody);
74
78
  } catch (e) {
75
- return new (0, _chunkCSTC3ADGcjs.DataApiError)({
79
+ return new (0, _chunkOLSMH4RYcjs.DataApiError)({
76
80
  message: `reopt-data responded ${status} with a non-JSON body`,
77
81
  status,
78
82
  code: status >= 500 ? "internal_error" : "unknown_error",
@@ -84,7 +88,7 @@ function toApiError(status, rawBody, retryAfterMs, requestId) {
84
88
  const body = _nullishCoalesce(parsed, () => ( {}));
85
89
  const code = typeof body.code === "string" ? body.code : status >= 500 ? "internal_error" : "unknown_error";
86
90
  const message = typeof body.message === "string" && body.message || typeof body.error === "string" && body.error || `reopt-data responded ${status}`;
87
- return new (0, _chunkCSTC3ADGcjs.DataApiError)({
91
+ return new (0, _chunkOLSMH4RYcjs.DataApiError)({
88
92
  message,
89
93
  status,
90
94
  code,
@@ -106,7 +110,17 @@ function createDataClient(options) {
106
110
  maxDelayMs: _nullishCoalesce(_optionalChain([options, 'access', _11 => _11.retry, 'optionalAccess', _12 => _12.maxDelayMs]), () => ( DEFAULTS.maxDelayMs)),
107
111
  respectRetryAfter: _nullishCoalesce(_optionalChain([options, 'access', _13 => _13.retry, 'optionalAccess', _14 => _14.respectRetryAfter]), () => ( true))
108
112
  };
109
- const userAgent = options.userAgent ? `reopt-data-contract/${_chunkCSTC3ADGcjs.CONTRACT_VERSION} ${options.userAgent}` : `reopt-data-contract/${_chunkCSTC3ADGcjs.CONTRACT_VERSION}`;
113
+ const userAgent = options.userAgent ? `reopt-data-contract/${_chunkOLSMH4RYcjs.CONTRACT_VERSION} ${options.userAgent}` : `reopt-data-contract/${_chunkOLSMH4RYcjs.CONTRACT_VERSION}`;
114
+ function requireClientCredentials() {
115
+ if (!options.clientId || !options.clientSecret) {
116
+ throw new (0, _chunkOLSMH4RYcjs.DataApiError)({
117
+ message: "createDataClient was given no client credentials \u2014 pass clientId and clientSecret to use the ingest or query planes",
118
+ status: 0,
119
+ code: "credentials_missing"
120
+ });
121
+ }
122
+ return { clientId: options.clientId, clientSecret: options.clientSecret };
123
+ }
110
124
  async function requestOnce(method, path, body, extraHeaders, signal) {
111
125
  const controller = new AbortController();
112
126
  const timer = setTimeout(
@@ -121,8 +135,7 @@ function createDataClient(options) {
121
135
  headers: {
122
136
  "content-type": "application/json",
123
137
  "user-agent": userAgent,
124
- [_chunkCSTC3ADGcjs.CLIENT_ID_HEADER]: options.clientId,
125
- [_chunkCSTC3ADGcjs.CLIENT_SECRET_HEADER]: options.clientSecret,
138
+ ...options.clientId && options.clientSecret ? { [_chunkOLSMH4RYcjs.CLIENT_ID_HEADER]: options.clientId, [_chunkOLSMH4RYcjs.CLIENT_SECRET_HEADER]: options.clientSecret } : {},
126
139
  ...extraHeaders
127
140
  },
128
141
  body: JSON.stringify(body),
@@ -141,7 +154,7 @@ function createDataClient(options) {
141
154
  response = await requestOnce(method, path, body, extraHeaders, signal);
142
155
  } catch (cause) {
143
156
  if (_optionalChain([signal, 'optionalAccess', _20 => _20.aborted])) throw cause;
144
- lastError = new (0, _chunkCSTC3ADGcjs.DataApiError)({
157
+ lastError = new (0, _chunkOLSMH4RYcjs.DataApiError)({
145
158
  message: cause instanceof Error ? cause.message : String(cause),
146
159
  status: 0,
147
160
  code: "network_error",
@@ -153,14 +166,24 @@ function createDataClient(options) {
153
166
  }
154
167
  throw lastError;
155
168
  }
156
- const requestId = _nullishCoalesce(response.headers.get(_chunkCSTC3ADGcjs.REQUEST_ID_HEADER), () => ( void 0));
169
+ const requestId = _nullishCoalesce(response.headers.get(_chunkOLSMH4RYcjs.REQUEST_ID_HEADER), () => ( void 0));
170
+ const serverContractVersion = response.headers.get(_chunkOLSMH4RYcjs.CONTRACT_VERSION_HEADER);
157
171
  const rawBody = await response.text();
172
+ if (serverContractVersion !== _chunkOLSMH4RYcjs.CONTRACT_VERSION) {
173
+ throw new (0, _chunkOLSMH4RYcjs.DataApiError)({
174
+ message: serverContractVersion ? `reopt-data contract ${serverContractVersion} does not match client contract ${_chunkOLSMH4RYcjs.CONTRACT_VERSION}` : "reopt-data response did not declare its contract version",
175
+ status: response.status,
176
+ code: "contract_mismatch",
177
+ requestId,
178
+ details: { expected: _chunkOLSMH4RYcjs.CONTRACT_VERSION, received: serverContractVersion }
179
+ });
180
+ }
158
181
  if (response.ok) {
159
182
  let json;
160
183
  try {
161
184
  json = JSON.parse(rawBody);
162
185
  } catch (cause) {
163
- throw new (0, _chunkCSTC3ADGcjs.DataApiError)({
186
+ throw new (0, _chunkOLSMH4RYcjs.DataApiError)({
164
187
  message: "reopt-data returned a 2xx with an unparseable body",
165
188
  status: response.status,
166
189
  code: "contract_mismatch",
@@ -171,7 +194,7 @@ function createDataClient(options) {
171
194
  try {
172
195
  return parse(json);
173
196
  } catch (cause) {
174
- throw new (0, _chunkCSTC3ADGcjs.DataApiError)({
197
+ throw new (0, _chunkOLSMH4RYcjs.DataApiError)({
175
198
  message: "reopt-data returned a 2xx that does not match the contract",
176
199
  status: response.status,
177
200
  code: "contract_mismatch",
@@ -185,92 +208,119 @@ function createDataClient(options) {
185
208
  if (!lastError.retryable || attempt === retry.maxRetries) throw lastError;
186
209
  await sleep(_nullishCoalesce(retryAfterMs, () => ( backoffDelay(attempt, retry))), signal);
187
210
  }
188
- throw _nullishCoalesce(lastError, () => ( new (0, _chunkCSTC3ADGcjs.DataApiError)({ message: "reopt-data request failed", status: 0, code: "network_error" })));
211
+ throw _nullishCoalesce(lastError, () => ( new (0, _chunkOLSMH4RYcjs.DataApiError)({ message: "reopt-data request failed", status: 0, code: "network_error" })));
189
212
  }
190
- function post(path, body, parse, signal) {
213
+ async function post(path, body, parse, signal) {
214
+ requireClientCredentials();
191
215
  return send("POST", path, body, parse, {}, signal);
192
216
  }
193
217
  return {
194
218
  ingest: {
195
219
  track(events, init) {
196
- return post(TRACK_PATH, events, (value) => _chunkHRKELGGHcjs.zIngestResponse.parse(value), _optionalChain([init, 'optionalAccess', _21 => _21.signal]));
220
+ return post(TRACK_PATH, events, (value) => _chunkXI3VP5DNcjs.zIngestResponse.parse(value), _optionalChain([init, 'optionalAccess', _21 => _21.signal]));
197
221
  }
198
222
  },
199
223
  query: {
200
224
  eventsTimeseries(input, init) {
201
225
  return post(
202
- _chunkGCLUMVPAcjs.QUERY_API_PATHS.eventsTimeseries,
226
+ _chunkVQ2CD5MGcjs.QUERY_API_PATHS.eventsTimeseries,
203
227
  input,
204
- (value) => _chunkGCLUMVPAcjs.zEventsTimeseriesResponse.parse(value),
228
+ (value) => _chunkVQ2CD5MGcjs.zEventsTimeseriesResponse.parse(value),
205
229
  _optionalChain([init, 'optionalAccess', _22 => _22.signal])
206
230
  );
207
231
  },
208
232
  funnel(input, init) {
209
- return post(_chunkGCLUMVPAcjs.QUERY_API_PATHS.funnel, input, (value) => _chunkGCLUMVPAcjs.zFunnelResponse.parse(value), _optionalChain([init, 'optionalAccess', _23 => _23.signal]));
233
+ return post(_chunkVQ2CD5MGcjs.QUERY_API_PATHS.funnel, input, (value) => _chunkVQ2CD5MGcjs.zFunnelResponse.parse(value), _optionalChain([init, 'optionalAccess', _23 => _23.signal]));
210
234
  },
211
235
  retention(input, init) {
212
- return post(_chunkGCLUMVPAcjs.QUERY_API_PATHS.retention, input, (value) => _chunkGCLUMVPAcjs.zRetentionResponse.parse(value), _optionalChain([init, 'optionalAccess', _24 => _24.signal]));
236
+ return post(_chunkVQ2CD5MGcjs.QUERY_API_PATHS.retention, input, (value) => _chunkVQ2CD5MGcjs.zRetentionResponse.parse(value), _optionalChain([init, 'optionalAccess', _24 => _24.signal]));
237
+ },
238
+ trafficOverview(input, init) {
239
+ return post(
240
+ _chunkVQ2CD5MGcjs.QUERY_API_PATHS.trafficOverview,
241
+ input,
242
+ (value) => _chunkVQ2CD5MGcjs.zTrafficOverviewResponse.parse(value),
243
+ _optionalChain([init, 'optionalAccess', _25 => _25.signal])
244
+ );
245
+ },
246
+ trafficSources(input, init) {
247
+ return post(
248
+ _chunkVQ2CD5MGcjs.QUERY_API_PATHS.trafficSources,
249
+ input,
250
+ (value) => _chunkVQ2CD5MGcjs.zTrafficSourcesResponse.parse(value),
251
+ _optionalChain([init, 'optionalAccess', _26 => _26.signal])
252
+ );
213
253
  }
214
254
  },
215
255
  control: {
216
256
  createOrganization(input, auth, init) {
217
257
  return send(
218
258
  "POST",
219
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.organizations,
259
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.organizations,
220
260
  input,
221
- (value) => _chunkLBSBZO7Ocjs.zCreateOrganizationResponse.parse(value),
222
- { [_chunkLBSBZO7Ocjs.PLATFORM_KEY_HEADER]: auth.platformKey },
223
- _optionalChain([init, 'optionalAccess', _25 => _25.signal])
261
+ (value) => _chunk4B4AY44Fcjs.zCreateOrganizationResponse.parse(value),
262
+ { [_chunk4B4AY44Fcjs.PLATFORM_KEY_HEADER]: auth.platformKey },
263
+ _optionalChain([init, 'optionalAccess', _27 => _27.signal])
224
264
  );
225
265
  },
226
266
  rotateOrgKey(organizationId, auth, init) {
227
267
  return send(
228
268
  "POST",
229
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.organizationKeys(organizationId),
269
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.organizationKeys(organizationId),
230
270
  {},
231
- (value) => _chunkLBSBZO7Ocjs.zRotateOrganizationKeyResponse.parse(value),
232
- { [_chunkLBSBZO7Ocjs.PLATFORM_KEY_HEADER]: auth.platformKey },
233
- _optionalChain([init, 'optionalAccess', _26 => _26.signal])
271
+ (value) => _chunk4B4AY44Fcjs.zRotateOrganizationKeyResponse.parse(value),
272
+ { [_chunk4B4AY44Fcjs.PLATFORM_KEY_HEADER]: auth.platformKey },
273
+ _optionalChain([init, 'optionalAccess', _28 => _28.signal])
234
274
  );
235
275
  },
236
276
  updateOrganizationQuota(organizationId, input, auth, init) {
237
277
  return send(
238
278
  "PATCH",
239
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.organization(organizationId),
279
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.organization(organizationId),
240
280
  input,
241
- (value) => _chunkLBSBZO7Ocjs.zUpdateOrganizationQuotaResponse.parse(value),
242
- { [_chunkLBSBZO7Ocjs.PLATFORM_KEY_HEADER]: auth.platformKey },
243
- _optionalChain([init, 'optionalAccess', _27 => _27.signal])
281
+ (value) => _chunk4B4AY44Fcjs.zUpdateOrganizationQuotaResponse.parse(value),
282
+ { [_chunk4B4AY44Fcjs.PLATFORM_KEY_HEADER]: auth.platformKey },
283
+ _optionalChain([init, 'optionalAccess', _29 => _29.signal])
244
284
  );
245
285
  },
246
286
  createProject(input, auth, init) {
247
287
  return send(
248
288
  "POST",
249
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.projects,
289
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.projects,
250
290
  input,
251
- (value) => _chunkLBSBZO7Ocjs.zCreateProjectResponse.parse(value),
252
- { [_chunkLBSBZO7Ocjs.ORG_KEY_HEADER]: auth.orgKey },
253
- _optionalChain([init, 'optionalAccess', _28 => _28.signal])
291
+ (value) => _chunk4B4AY44Fcjs.zCreateProjectResponse.parse(value),
292
+ { [_chunk4B4AY44Fcjs.ORG_KEY_HEADER]: auth.orgKey },
293
+ _optionalChain([init, 'optionalAccess', _30 => _30.signal])
254
294
  );
255
295
  },
256
296
  rotateServerClientSecret(projectId, clientId, auth, init) {
257
297
  return send(
258
298
  "POST",
259
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.clientRotate(projectId, clientId),
299
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.clientRotate(projectId, clientId),
260
300
  {},
261
- (value) => _chunkLBSBZO7Ocjs.zRotateClientSecretResponse.parse(value),
262
- { [_chunkLBSBZO7Ocjs.ORG_KEY_HEADER]: auth.orgKey },
263
- _optionalChain([init, 'optionalAccess', _29 => _29.signal])
301
+ (value) => _chunk4B4AY44Fcjs.zRotateClientSecretResponse.parse(value),
302
+ { [_chunk4B4AY44Fcjs.ORG_KEY_HEADER]: auth.orgKey },
303
+ _optionalChain([init, 'optionalAccess', _31 => _31.signal])
264
304
  );
265
305
  },
266
306
  deleteProject(projectId, input, auth, init) {
267
307
  return send(
268
308
  "DELETE",
269
- _chunkLBSBZO7Ocjs.CONTROL_API_PATHS.project(projectId),
309
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.project(projectId),
270
310
  input,
271
- (value) => _chunkLBSBZO7Ocjs.zDeleteProjectResponse.parse(value),
272
- { [_chunkLBSBZO7Ocjs.ORG_KEY_HEADER]: auth.orgKey },
273
- _optionalChain([init, 'optionalAccess', _30 => _30.signal])
311
+ (value) => _chunk4B4AY44Fcjs.zDeleteProjectResponse.parse(value),
312
+ { [_chunk4B4AY44Fcjs.ORG_KEY_HEADER]: auth.orgKey },
313
+ _optionalChain([init, 'optionalAccess', _32 => _32.signal])
314
+ );
315
+ },
316
+ upsertEventMeta(projectId, name, input, auth, init) {
317
+ return send(
318
+ "PUT",
319
+ _chunk4B4AY44Fcjs.CONTROL_API_PATHS.projectEvent(projectId, name),
320
+ input,
321
+ (value) => _chunk4B4AY44Fcjs.zEventMetaResponse.parse(value),
322
+ { [_chunk4B4AY44Fcjs.ORG_KEY_HEADER]: auth.orgKey },
323
+ _optionalChain([init, 'optionalAccess', _33 => _33.signal])
274
324
  );
275
325
  }
276
326
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/client.cjs","../src/client.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;AC0BA,IAAM,WAAA,EAAa,YAAA;AAmGnB,IAAM,SAAA,EAAW;AAAA,EACf,SAAA,EAAW,GAAA;AAAA,EACX,UAAA,EAAY,CAAA;AAAA,EACZ,WAAA,EAAa,GAAA;AAAA,EACb,UAAA,EAAY;AACd,CAAA;AAEA,SAAS,gBAAA,CAAiB,OAAA,EAAyB;AACjD,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACnC;AAGA,SAAS,eAAA,CAAgB,MAAA,EAA2C;AAClE,EAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,OAAO,KAAA,CAAA;AAEpB,EAAA,MAAM,QAAA,EAAU,MAAA,CAAO,MAAM,CAAA;AAC7B,EAAA,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,OAAO,EAAA,GAAK,QAAA,GAAW,CAAA,EAAG,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,GAAI,CAAA;AAE9E,EAAA,MAAM,GAAA,EAAK,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAC5B,EAAA,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,EAAG,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AAE3D,EAAA,OAAO,KAAA,CAAA;AACT;AAEA,SAAS,KAAA,CAAM,EAAA,EAAY,MAAA,EAAqC;AAC9D,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,EAAA,GAAW;AACtC,IAAA,GAAA,iBAAI,MAAA,2BAAQ,SAAA,EAAS;AACnB,MAAA,MAAA,kBAAO,MAAA,CAAO,MAAA,UAAU,IAAI,KAAA,CAAM,SAAS,GAAC,CAAA;AAC5C,MAAA,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,EAAQ,UAAA,CAAW,CAAA,EAAA,GAAM;AAC7B,sBAAA,MAAA,6BAAQ,mBAAA,mBAAoB,OAAA,EAAS,OAAO,GAAA;AAC5C,MAAA,OAAA,CAAQ,CAAA;AAAA,IACV,CAAA,EAAG,EAAE,CAAA;AACL,IAAA,MAAM,QAAA,EAAU,CAAA,EAAA,GAAM;AACpB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,MAAA,kCAAO,MAAA,6BAAQ,QAAA,UAAU,IAAI,KAAA,CAAM,SAAS,GAAC,CAAA;AAAA,IAC/C,CAAA;AACA,oBAAA,MAAA,6BAAQ,gBAAA,mBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,KAAK,CAAC,GAAA;AAAA,EAC3D,CAAC,CAAA;AACH;AAEA,SAAS,YAAA,CAAa,OAAA,EAAiB,KAAA,EAAiD;AACtF,EAAA,MAAM,YAAA,EAAc,KAAA,CAAM,YAAA,EAAc,EAAA,GAAK,OAAA;AAC7C,EAAA,MAAM,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,KAAA,CAAM,UAAU,CAAA;AAGrD,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,OAAA,EAAA,CAAU,IAAA,EAAM,IAAA,CAAK,MAAA,CAAO,EAAA,EAAI,GAAA,CAAI,CAAA;AACxD;AAOA,SAAS,UAAA,CACP,MAAA,EACA,OAAA,EACA,YAAA,EACA,SAAA,EACc;AACd,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAAA,EAC7B,EAAA,UAAQ;AACN,IAAA,OAAO,IAAI,mCAAA,CAAa;AAAA,MACtB,OAAA,EAAS,CAAA,qBAAA,EAAwB,MAAM,CAAA,qBAAA,CAAA;AAAA,MACvC,MAAA;AAAA,MACA,IAAA,EAAM,OAAA,GAAU,IAAA,EAAM,iBAAA,EAAmB,eAAA;AAAA,MACzC,SAAA;AAAA,MACA,YAAA;AAAA,MACA,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAI;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,KAAA,mBAAQ,MAAA,UAAU,CAAC,GAAA;AACzB,EAAA,MAAM,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA,IAAS,SAAA,EAAW,IAAA,CAAK,KAAA,EAAO,OAAA,GAAU,IAAA,EAAM,iBAAA,EAAmB,eAAA;AAC5F,EAAA,MAAM,QAAA,EACH,OAAO,IAAA,CAAK,QAAA,IAAY,SAAA,GAAY,IAAA,CAAK,QAAA,GACzC,OAAO,IAAA,CAAK,MAAA,IAAU,SAAA,GAAY,IAAA,CAAK,MAAA,GACxC,CAAA,qBAAA,EAAwB,MAAM,CAAA,CAAA;AAER,EAAA;AACtB,IAAA;AACA,IAAA;AACA,IAAA;AACgF,IAAA;AAChF,IAAA;AACwB,IAAA;AACzB,EAAA;AACH;AAEyE;AACvB,EAAA;AACJ,EAAA;AACT,EAAA;AAC6D,IAAA;AAChG,EAAA;AAEgD,EAAA;AACA,EAAA;AACI,IAAA;AACE,IAAA;AACF,IAAA;AACK,IAAA;AACzD,EAAA;AAI2C,EAAA;AAQtB,EAAA;AACoB,IAAA;AACzB,IAAA;AACyE,MAAA;AACrF,MAAA;AACF,IAAA;AACmF,IAAA;AACpB,oBAAA;AAE3D,IAAA;AACwC,MAAA;AACxC,QAAA;AACS,QAAA;AACS,UAAA;AACF,UAAA;AACc,UAAA;AACI,UAAA;AAC7B,UAAA;AACL,QAAA;AACyB,QAAA;AACN,QAAA;AACpB,MAAA;AACD,IAAA;AACkB,MAAA;AACgC,sBAAA;AACpD,IAAA;AACF,EAAA;AASc,EAAA;AACR,IAAA;AAE0D,IAAA;AACxD,MAAA;AACA,MAAA;AACmE,QAAA;AACvD,MAAA;AAEa,QAAA;AACE,QAAA;AACmC,UAAA;AACtD,UAAA;AACF,UAAA;AACG,UAAA;AACV,QAAA;AAC+B,QAAA;AACkB,UAAA;AAChD,UAAA;AACF,QAAA;AACM,QAAA;AACR,MAAA;AAE6D,MAAA;AACzB,MAAA;AAEnB,MAAA;AACX,QAAA;AACA,QAAA;AACuB,UAAA;AACX,QAAA;AACS,UAAA;AACZ,YAAA;AACQ,YAAA;AACX,YAAA;AACN,YAAA;AACS,YAAA;AACV,UAAA;AACH,QAAA;AAEI,QAAA;AACe,UAAA;AACH,QAAA;AACS,UAAA;AACZ,YAAA;AACQ,YAAA;AACX,YAAA;AACN,YAAA;AACS,YAAA;AACV,UAAA;AACH,QAAA;AACF,MAAA;AAEsG,MAAA;AAC9B,MAAA;AAER,MAAA;AACA,MAAA;AAClE,IAAA;AAG8G,IAAA;AAChH,EAAA;AAE8G,EAAA;AAC3D,IAAA;AACnD,EAAA;AAEO,EAAA;AACG,IAAA;AACc,MAAA;AACmE,QAAA;AACvF,MAAA;AACF,IAAA;AACO,IAAA;AACyB,MAAA;AACrB,QAAA;AACW,UAAA;AAChB,UAAA;AACgD,UAAA;AAC1C,0BAAA;AACR,QAAA;AACF,MAAA;AACoB,MAAA;AAC8E,QAAA;AAClG,MAAA;AACuB,MAAA;AACiF,QAAA;AACxG,MAAA;AACF,IAAA;AACS,IAAA;AAC+B,MAAA;AAC7B,QAAA;AACL,UAAA;AACkB,UAAA;AAClB,UAAA;AACkD,UAAA;AACR,UAAA;AACpC,0BAAA;AACR,QAAA;AACF,MAAA;AACyC,MAAA;AAChC,QAAA;AACL,UAAA;AACiD,UAAA;AAChD,UAAA;AACoD,UAAA;AACX,UAAA;AACpC,0BAAA;AACR,QAAA;AACF,MAAA;AAC2D,MAAA;AAClD,QAAA;AACL,UAAA;AAC6C,UAAA;AAC7C,UAAA;AACuD,UAAA;AACb,UAAA;AACpC,0BAAA;AACR,QAAA;AACF,MAAA;AACiC,MAAA;AACxB,QAAA;AACL,UAAA;AACkB,UAAA;AAClB,UAAA;AAC6C,UAAA;AACb,UAAA;AAC1B,0BAAA;AACR,QAAA;AACF,MAAA;AAC0D,MAAA;AACjD,QAAA;AACL,UAAA;AACkD,UAAA;AACjD,UAAA;AACiD,UAAA;AAClB,UAAA;AAC1B,0BAAA;AACR,QAAA;AACF,MAAA;AAC4C,MAAA;AACnC,QAAA;AACL,UAAA;AACmC,UAAA;AACnC,UAAA;AAC6C,UAAA;AACb,UAAA;AAC1B,0BAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACF;ADpLqJ;AACA;AACA","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/client.cjs","sourcesContent":[null,"/**\n * `createDataClient` — typed fetch over the ingest and query planes.\n *\n * Deliberately stateless: no queue, no buffer, no background flush. Batch\n * assembly and cursor management belong to the caller (a forwarder already has\n * a cursor and byte-batching; duplicating that here would give it two).\n *\n * Every response is parsed with the contract schema, so a server that drifts\n * from the contract surfaces as `contract_mismatch` instead of quietly\n * returning something the caller then mis-reads.\n */\nimport {\n CLIENT_ID_HEADER,\n CLIENT_SECRET_HEADER,\n CONTRACT_VERSION,\n DataApiError,\n REQUEST_ID_HEADER,\n type ClientCredentials,\n} from \"./index.js\";\nimport { zIngestResponse, type IngestResponse, type ITrackHandlerPayload } from \"./ingest.js\";\nimport {\n CONTROL_API_PATHS,\n ORG_KEY_HEADER,\n PLATFORM_KEY_HEADER,\n zCreateOrganizationResponse,\n zCreateProjectResponse,\n zDeleteProjectResponse,\n zRotateClientSecretResponse,\n zRotateOrganizationKeyResponse,\n zUpdateOrganizationQuotaResponse,\n type CreateOrganizationInput,\n type CreateOrganizationResponse,\n type CreateProjectInput,\n type CreateProjectResponse,\n type DeleteProjectInput,\n type DeleteProjectResponse,\n type RotateClientSecretResponse,\n type RotateOrganizationKeyResponse,\n type UpdateOrganizationQuotaInput,\n type UpdateOrganizationQuotaResponse,\n} from \"./control.js\";\nimport {\n QUERY_API_PATHS,\n zEventsTimeseriesResponse,\n zFunnelResponse,\n zRetentionResponse,\n type EventsTimeseriesInput,\n type EventsTimeseriesResponse,\n type FunnelInput,\n type FunnelResponse,\n type RetentionInput,\n type RetentionResponse,\n} from \"./query.js\";\n\nconst TRACK_PATH = \"/api/track\";\n\nexport interface DataClientRetryOptions {\n /** Attempts after the first. Only 429 and 5xx are retried. Default 2. */\n maxRetries?: number;\n /** First backoff step; doubles each attempt, with jitter. Default 500ms. */\n baseDelayMs?: number;\n /** Cap on a single backoff step. Default 20s. */\n maxDelayMs?: number;\n /** Prefer the server's `Retry-After` over the computed backoff. Default true. */\n respectRetryAfter?: boolean;\n}\n\nexport interface DataClientOptions extends ClientCredentials {\n /** Origin of the reopt-data deployment, e.g. `https://data.reopt.app`. Trailing slash ignored. */\n baseUrl: string;\n /** Injected so callers can pass a framework-instrumented fetch. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /** Per-attempt timeout. Default 10s. */\n timeoutMs?: number;\n retry?: DataClientRetryOptions;\n /** Appended to the default `reopt-data-contract/<version>`. */\n userAgent?: string;\n}\n\nexport interface DataRequestInit {\n signal?: AbortSignal;\n}\n\n/**\n * Provisioning credentials are passed per call rather than held on the client,\n * because they are a different authority from the client credential the rest of\n * the surface uses — and because a long-lived object holding the platform key\n * is a wider blast radius than a value the caller reads at the moment it needs\n * it.\n */\nexport interface PlatformAuth {\n platformKey: string;\n}\n\nexport interface OrganizationAuth {\n orgKey: string;\n}\n\nexport interface DataClient {\n ingest: {\n /**\n * `POST /api/track`. Send events as one batch; splitting to stay under\n * the 512KB body cap is the caller's job, since only the caller knows how\n * to resume a partially-sent range.\n */\n track(events: ITrackHandlerPayload[], init?: DataRequestInit): Promise<IngestResponse>;\n };\n query: {\n eventsTimeseries(input: EventsTimeseriesInput, init?: DataRequestInit): Promise<EventsTimeseriesResponse>;\n funnel(input: FunnelInput, init?: DataRequestInit): Promise<FunnelResponse>;\n retention(input: RetentionInput, init?: DataRequestInit): Promise<RetentionResponse>;\n };\n control: {\n /** Idempotent on `externalId`. `orgKey` comes back only on first creation. */\n createOrganization(\n input: CreateOrganizationInput,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<CreateOrganizationResponse>;\n /** Issues a new key and revokes every previous one. The recovery path for a lost key. */\n rotateOrgKey(\n organizationId: string,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<RotateOrganizationKeyResponse>;\n updateOrganizationQuota(\n organizationId: string,\n input: UpdateOrganizationQuotaInput,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<UpdateOrganizationQuotaResponse>;\n /** Idempotent on `externalId`. `clientSecret` comes back only on first creation. */\n createProject(\n input: CreateProjectInput,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<CreateProjectResponse>;\n rotateServerClientSecret(\n projectId: string,\n clientId: string,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<RotateClientSecretResponse>;\n /** Irreversible. Idempotent: a project that is already gone still answers 202. */\n deleteProject(\n projectId: string,\n input: DeleteProjectInput,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<DeleteProjectResponse>;\n };\n}\n\nconst DEFAULTS = {\n timeoutMs: 10_000,\n maxRetries: 2,\n baseDelayMs: 500,\n maxDelayMs: 20_000,\n} as const;\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\n/** `Retry-After` is either delta-seconds or an HTTP date; both are legal. */\nfunction parseRetryAfter(header: string | null): number | undefined {\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1000);\n\n const at = Date.parse(header);\n if (Number.isFinite(at)) return Math.max(0, at - Date.now());\n\n return undefined;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new Error(\"Aborted\"));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction backoffDelay(attempt: number, retry: Required<DataClientRetryOptions>): number {\n const exponential = retry.baseDelayMs * 2 ** attempt;\n const capped = Math.min(exponential, retry.maxDelayMs);\n // Full jitter — a fleet of forwarders that all got 429 should not all come\n // back at the same instant.\n return Math.round(capped * (0.5 + Math.random() * 0.5));\n}\n\n/**\n * Body of a non-2xx response. Both planes share `{ status, code, error, ... }`,\n * so one reader covers them; anything else is reported as `internal_error`\n * with the raw text kept in `details`.\n */\nfunction toApiError(\n status: number,\n rawBody: string,\n retryAfterMs: number | undefined,\n requestId?: string\n): DataApiError {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n return new DataApiError({\n message: `reopt-data responded ${status} with a non-JSON body`,\n status,\n code: status >= 500 ? \"internal_error\" : \"unknown_error\",\n requestId,\n retryAfterMs,\n details: rawBody.slice(0, 2000),\n });\n }\n\n const body = (parsed ?? {}) as Record<string, unknown>;\n const code = typeof body.code === \"string\" ? body.code : status >= 500 ? \"internal_error\" : \"unknown_error\";\n const message =\n (typeof body.message === \"string\" && body.message) ||\n (typeof body.error === \"string\" && body.error) ||\n `reopt-data responded ${status}`;\n\n return new DataApiError({\n message,\n status,\n code,\n requestId: (typeof body.requestId === \"string\" ? body.requestId : undefined) ?? requestId,\n retryAfterMs,\n details: body.errors ?? undefined,\n });\n}\n\nexport function createDataClient(options: DataClientOptions): DataClient {\n const baseUrl = normalizeBaseUrl(options.baseUrl);\n const doFetch = options.fetch ?? globalThis.fetch;\n if (typeof doFetch !== \"function\") {\n throw new TypeError(\"createDataClient: no fetch implementation available — pass options.fetch\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULTS.timeoutMs;\n const retry: Required<DataClientRetryOptions> = {\n maxRetries: options.retry?.maxRetries ?? DEFAULTS.maxRetries,\n baseDelayMs: options.retry?.baseDelayMs ?? DEFAULTS.baseDelayMs,\n maxDelayMs: options.retry?.maxDelayMs ?? DEFAULTS.maxDelayMs,\n respectRetryAfter: options.retry?.respectRetryAfter ?? true,\n };\n\n const userAgent = options.userAgent\n ? `reopt-data-contract/${CONTRACT_VERSION} ${options.userAgent}`\n : `reopt-data-contract/${CONTRACT_VERSION}`;\n\n async function requestOnce(\n method: \"POST\" | \"PATCH\" | \"DELETE\",\n path: string,\n body: unknown,\n extraHeaders: Record<string, string>,\n signal?: AbortSignal\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(new Error(`reopt-data request timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n const onCallerAbort = () => controller.abort(signal?.reason ?? new Error(\"Aborted\"));\n signal?.addEventListener(\"abort\", onCallerAbort, { once: true });\n\n try {\n return await doFetch(`${baseUrl}${path}`, {\n method,\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": userAgent,\n [CLIENT_ID_HEADER]: options.clientId,\n [CLIENT_SECRET_HEADER]: options.clientSecret,\n ...extraHeaders,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onCallerAbort);\n }\n }\n\n async function send<T>(\n method: \"POST\" | \"PATCH\" | \"DELETE\",\n path: string,\n body: unknown,\n parse: (value: unknown) => T,\n extraHeaders: Record<string, string> = {},\n signal?: AbortSignal\n ): Promise<T> {\n let lastError: DataApiError | undefined;\n\n for (let attempt = 0; attempt <= retry.maxRetries; attempt++) {\n let response: Response;\n try {\n response = await requestOnce(method, path, body, extraHeaders, signal);\n } catch (cause) {\n // A caller-initiated abort is not a transport failure — propagate it.\n if (signal?.aborted) throw cause;\n lastError = new DataApiError({\n message: cause instanceof Error ? cause.message : String(cause),\n status: 0,\n code: \"network_error\",\n details: cause,\n });\n if (attempt < retry.maxRetries) {\n await sleep(backoffDelay(attempt, retry), signal);\n continue;\n }\n throw lastError;\n }\n\n const requestId = response.headers.get(REQUEST_ID_HEADER) ?? undefined;\n const rawBody = await response.text();\n\n if (response.ok) {\n let json: unknown;\n try {\n json = JSON.parse(rawBody);\n } catch (cause) {\n throw new DataApiError({\n message: \"reopt-data returned a 2xx with an unparseable body\",\n status: response.status,\n code: \"contract_mismatch\",\n requestId,\n details: cause,\n });\n }\n\n try {\n return parse(json);\n } catch (cause) {\n throw new DataApiError({\n message: \"reopt-data returned a 2xx that does not match the contract\",\n status: response.status,\n code: \"contract_mismatch\",\n requestId,\n details: cause,\n });\n }\n }\n\n const retryAfterMs = retry.respectRetryAfter ? parseRetryAfter(response.headers.get(\"retry-after\")) : undefined;\n lastError = toApiError(response.status, rawBody, retryAfterMs, requestId);\n\n if (!lastError.retryable || attempt === retry.maxRetries) throw lastError;\n await sleep(retryAfterMs ?? backoffDelay(attempt, retry), signal);\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new DataApiError({ message: \"reopt-data request failed\", status: 0, code: \"network_error\" });\n }\n\n function post<T>(path: string, body: unknown, parse: (value: unknown) => T, signal?: AbortSignal): Promise<T> {\n return send(\"POST\", path, body, parse, {}, signal);\n }\n\n return {\n ingest: {\n track(events, init) {\n return post(TRACK_PATH, events, (value) => zIngestResponse.parse(value), init?.signal);\n },\n },\n query: {\n eventsTimeseries(input, init) {\n return post(\n QUERY_API_PATHS.eventsTimeseries,\n input,\n (value) => zEventsTimeseriesResponse.parse(value),\n init?.signal\n );\n },\n funnel(input, init) {\n return post(QUERY_API_PATHS.funnel, input, (value) => zFunnelResponse.parse(value), init?.signal);\n },\n retention(input, init) {\n return post(QUERY_API_PATHS.retention, input, (value) => zRetentionResponse.parse(value), init?.signal);\n },\n },\n control: {\n createOrganization(input, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.organizations,\n input,\n (value) => zCreateOrganizationResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n rotateOrgKey(organizationId, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.organizationKeys(organizationId),\n {},\n (value) => zRotateOrganizationKeyResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n updateOrganizationQuota(organizationId, input, auth, init) {\n return send(\n \"PATCH\",\n CONTROL_API_PATHS.organization(organizationId),\n input,\n (value) => zUpdateOrganizationQuotaResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n createProject(input, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.projects,\n input,\n (value) => zCreateProjectResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n rotateServerClientSecret(projectId, clientId, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.clientRotate(projectId, clientId),\n {},\n (value) => zRotateClientSecretResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n deleteProject(projectId, input, auth, init) {\n return send(\n \"DELETE\",\n CONTROL_API_PATHS.project(projectId),\n input,\n (value) => zDeleteProjectResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/client.cjs","../src/client.ts"],"names":[],"mappings":"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","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/client.cjs","sourcesContent":[null,"/**\n * `createDataClient` — typed fetch over the ingest and query planes.\n *\n * Deliberately stateless: no queue, no buffer, no background flush. Batch\n * assembly and cursor management belong to the caller (a forwarder already has\n * a cursor and byte-batching; duplicating that here would give it two).\n *\n * Every response is parsed with the contract schema, so a server that drifts\n * from the contract surfaces as `contract_mismatch` instead of quietly\n * returning something the caller then mis-reads.\n */\nimport {\n CLIENT_ID_HEADER,\n CLIENT_SECRET_HEADER,\n CONTRACT_VERSION,\n CONTRACT_VERSION_HEADER,\n DataApiError,\n REQUEST_ID_HEADER,\n type OptionalClientCredentials,\n} from \"./index.js\";\nimport { zIngestResponse, type IngestResponse, type ITrackHandlerPayload } from \"./ingest.js\";\nimport {\n CONTROL_API_PATHS,\n ORG_KEY_HEADER,\n PLATFORM_KEY_HEADER,\n zCreateOrganizationResponse,\n zCreateProjectResponse,\n zDeleteProjectResponse,\n zRotateClientSecretResponse,\n zRotateOrganizationKeyResponse,\n zUpdateOrganizationQuotaResponse,\n zEventMetaResponse,\n type CreateOrganizationInput,\n type EventMetaResponse,\n type UpsertEventMetaInput,\n type CreateOrganizationResponse,\n type CreateProjectInput,\n type CreateProjectResponse,\n type DeleteProjectInput,\n type DeleteProjectResponse,\n type RotateClientSecretResponse,\n type RotateOrganizationKeyResponse,\n type UpdateOrganizationQuotaInput,\n type UpdateOrganizationQuotaResponse,\n} from \"./control.js\";\nimport {\n QUERY_API_PATHS,\n zEventsTimeseriesResponse,\n zFunnelResponse,\n zRetentionResponse,\n zTrafficOverviewResponse,\n zTrafficSourcesResponse,\n type EventsTimeseriesInput,\n type EventsTimeseriesResponse,\n type FunnelInput,\n type FunnelResponse,\n type RetentionInput,\n type RetentionResponse,\n type TrafficOverviewInput,\n type TrafficOverviewResponse,\n type TrafficSourcesInput,\n type TrafficSourcesResponse,\n} from \"./query.js\";\n\nconst TRACK_PATH = \"/api/track\";\n\nexport interface DataClientRetryOptions {\n /** Attempts after the first. Only 429 and 5xx are retried. Default 2. */\n maxRetries?: number;\n /** First backoff step; doubles each attempt, with jitter. Default 500ms. */\n baseDelayMs?: number;\n /** Cap on a single backoff step. Default 20s. */\n maxDelayMs?: number;\n /** Prefer the server's `Retry-After` over the computed backoff. Default true. */\n respectRetryAfter?: boolean;\n}\n\nexport interface DataClientOptions extends OptionalClientCredentials {\n /** Origin of the reopt-data deployment, e.g. `https://data.reopt.app`. Trailing slash ignored. */\n baseUrl: string;\n /** Injected so callers can pass a framework-instrumented fetch. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /** Per-attempt timeout. Default 10s. */\n timeoutMs?: number;\n retry?: DataClientRetryOptions;\n /** Appended to the default `reopt-data-contract/<version>`. */\n userAgent?: string;\n}\n\nexport interface DataRequestInit {\n signal?: AbortSignal;\n}\n\n/**\n * Provisioning credentials are passed per call rather than held on the client,\n * because they are a different authority from the client credential the rest of\n * the surface uses — and because a long-lived object holding the platform key\n * is a wider blast radius than a value the caller reads at the moment it needs\n * it.\n */\nexport interface PlatformAuth {\n platformKey: string;\n}\n\nexport interface OrganizationAuth {\n orgKey: string;\n}\n\nexport interface DataClient {\n ingest: {\n /**\n * `POST /api/track`. Send events as one batch; splitting to stay under\n * the 512KB body cap is the caller's job, since only the caller knows how\n * to resume a partially-sent range.\n */\n track(events: ITrackHandlerPayload[], init?: DataRequestInit): Promise<IngestResponse>;\n };\n query: {\n eventsTimeseries(input: EventsTimeseriesInput, init?: DataRequestInit): Promise<EventsTimeseriesResponse>;\n funnel(input: FunnelInput, init?: DataRequestInit): Promise<FunnelResponse>;\n retention(input: RetentionInput, init?: DataRequestInit): Promise<RetentionResponse>;\n trafficOverview(input: TrafficOverviewInput, init?: DataRequestInit): Promise<TrafficOverviewResponse>;\n trafficSources(input: TrafficSourcesInput, init?: DataRequestInit): Promise<TrafficSourcesResponse>;\n };\n control: {\n /** Idempotent on `externalId`. `orgKey` comes back only on first creation. */\n createOrganization(\n input: CreateOrganizationInput,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<CreateOrganizationResponse>;\n /** Issues a new key and revokes every previous one. The recovery path for a lost key. */\n rotateOrgKey(\n organizationId: string,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<RotateOrganizationKeyResponse>;\n updateOrganizationQuota(\n organizationId: string,\n input: UpdateOrganizationQuotaInput,\n auth: PlatformAuth,\n init?: DataRequestInit\n ): Promise<UpdateOrganizationQuotaResponse>;\n /** Idempotent on `externalId`. `clientSecret` comes back only on first creation. */\n createProject(\n input: CreateProjectInput,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<CreateProjectResponse>;\n rotateServerClientSecret(\n projectId: string,\n clientId: string,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<RotateClientSecretResponse>;\n /** Irreversible. Idempotent: a project that is already gone still answers 202. */\n deleteProject(\n projectId: string,\n input: DeleteProjectInput,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<DeleteProjectResponse>;\n /** Idempotent: a repeat with the same body changes nothing and answers 200. */\n upsertEventMeta(\n projectId: string,\n name: string,\n input: UpsertEventMetaInput,\n auth: OrganizationAuth,\n init?: DataRequestInit\n ): Promise<EventMetaResponse>;\n };\n}\n\nconst DEFAULTS = {\n timeoutMs: 10_000,\n maxRetries: 2,\n baseDelayMs: 500,\n maxDelayMs: 20_000,\n} as const;\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\n/** `Retry-After` is either delta-seconds or an HTTP date; both are legal. */\nfunction parseRetryAfter(header: string | null): number | undefined {\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1000);\n\n const at = Date.parse(header);\n if (Number.isFinite(at)) return Math.max(0, at - Date.now());\n\n return undefined;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new Error(\"Aborted\"));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction backoffDelay(attempt: number, retry: Required<DataClientRetryOptions>): number {\n const exponential = retry.baseDelayMs * 2 ** attempt;\n const capped = Math.min(exponential, retry.maxDelayMs);\n // Full jitter — a fleet of forwarders that all got 429 should not all come\n // back at the same instant.\n return Math.round(capped * (0.5 + Math.random() * 0.5));\n}\n\n/**\n * Body of a non-2xx response. Both planes share `{ status, code, error, ... }`,\n * so one reader covers them; anything else is reported as `internal_error`\n * with the raw text kept in `details`.\n */\nfunction toApiError(\n status: number,\n rawBody: string,\n retryAfterMs: number | undefined,\n requestId?: string\n): DataApiError {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n return new DataApiError({\n message: `reopt-data responded ${status} with a non-JSON body`,\n status,\n code: status >= 500 ? \"internal_error\" : \"unknown_error\",\n requestId,\n retryAfterMs,\n details: rawBody.slice(0, 2000),\n });\n }\n\n const body = (parsed ?? {}) as Record<string, unknown>;\n const code = typeof body.code === \"string\" ? body.code : status >= 500 ? \"internal_error\" : \"unknown_error\";\n const message =\n (typeof body.message === \"string\" && body.message) ||\n (typeof body.error === \"string\" && body.error) ||\n `reopt-data responded ${status}`;\n\n return new DataApiError({\n message,\n status,\n code,\n requestId: (typeof body.requestId === \"string\" ? body.requestId : undefined) ?? requestId,\n retryAfterMs,\n details: body.errors ?? undefined,\n });\n}\n\nexport function createDataClient(options: DataClientOptions): DataClient {\n const baseUrl = normalizeBaseUrl(options.baseUrl);\n const doFetch = options.fetch ?? globalThis.fetch;\n if (typeof doFetch !== \"function\") {\n throw new TypeError(\"createDataClient: no fetch implementation available — pass options.fetch\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULTS.timeoutMs;\n const retry: Required<DataClientRetryOptions> = {\n maxRetries: options.retry?.maxRetries ?? DEFAULTS.maxRetries,\n baseDelayMs: options.retry?.baseDelayMs ?? DEFAULTS.baseDelayMs,\n maxDelayMs: options.retry?.maxDelayMs ?? DEFAULTS.maxDelayMs,\n respectRetryAfter: options.retry?.respectRetryAfter ?? true,\n };\n\n const userAgent = options.userAgent\n ? `reopt-data-contract/${CONTRACT_VERSION} ${options.userAgent}`\n : `reopt-data-contract/${CONTRACT_VERSION}`;\n\n /**\n * Client credentials, or a clear failure. The ingest and query planes cannot\n * run without them; the provisioning plane never uses them.\n */\n function requireClientCredentials(): { clientId: string; clientSecret: string } {\n if (!options.clientId || !options.clientSecret) {\n throw new DataApiError({\n message:\n \"createDataClient was given no client credentials — pass clientId and clientSecret to use the ingest or query planes\",\n status: 0,\n code: \"credentials_missing\",\n });\n }\n return { clientId: options.clientId, clientSecret: options.clientSecret };\n }\n\n async function requestOnce(\n method: \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\",\n path: string,\n body: unknown,\n extraHeaders: Record<string, string>,\n signal?: AbortSignal\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(new Error(`reopt-data request timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n const onCallerAbort = () => controller.abort(signal?.reason ?? new Error(\"Aborted\"));\n signal?.addEventListener(\"abort\", onCallerAbort, { once: true });\n\n try {\n return await doFetch(`${baseUrl}${path}`, {\n method,\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": userAgent,\n ...(options.clientId && options.clientSecret\n ? { [CLIENT_ID_HEADER]: options.clientId, [CLIENT_SECRET_HEADER]: options.clientSecret }\n : {}),\n ...extraHeaders,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onCallerAbort);\n }\n }\n\n async function send<T>(\n method: \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\",\n path: string,\n body: unknown,\n parse: (value: unknown) => T,\n extraHeaders: Record<string, string> = {},\n signal?: AbortSignal\n ): Promise<T> {\n let lastError: DataApiError | undefined;\n\n for (let attempt = 0; attempt <= retry.maxRetries; attempt++) {\n let response: Response;\n try {\n response = await requestOnce(method, path, body, extraHeaders, signal);\n } catch (cause) {\n // A caller-initiated abort is not a transport failure — propagate it.\n if (signal?.aborted) throw cause;\n lastError = new DataApiError({\n message: cause instanceof Error ? cause.message : String(cause),\n status: 0,\n code: \"network_error\",\n details: cause,\n });\n if (attempt < retry.maxRetries) {\n await sleep(backoffDelay(attempt, retry), signal);\n continue;\n }\n throw lastError;\n }\n\n const requestId = response.headers.get(REQUEST_ID_HEADER) ?? undefined;\n const serverContractVersion = response.headers.get(CONTRACT_VERSION_HEADER);\n const rawBody = await response.text();\n\n if (serverContractVersion !== CONTRACT_VERSION) {\n throw new DataApiError({\n message: serverContractVersion\n ? `reopt-data contract ${serverContractVersion} does not match client contract ${CONTRACT_VERSION}`\n : \"reopt-data response did not declare its contract version\",\n status: response.status,\n code: \"contract_mismatch\",\n requestId,\n details: { expected: CONTRACT_VERSION, received: serverContractVersion },\n });\n }\n\n if (response.ok) {\n let json: unknown;\n try {\n json = JSON.parse(rawBody);\n } catch (cause) {\n throw new DataApiError({\n message: \"reopt-data returned a 2xx with an unparseable body\",\n status: response.status,\n code: \"contract_mismatch\",\n requestId,\n details: cause,\n });\n }\n\n try {\n return parse(json);\n } catch (cause) {\n throw new DataApiError({\n message: \"reopt-data returned a 2xx that does not match the contract\",\n status: response.status,\n code: \"contract_mismatch\",\n requestId,\n details: cause,\n });\n }\n }\n\n const retryAfterMs = retry.respectRetryAfter ? parseRetryAfter(response.headers.get(\"retry-after\")) : undefined;\n lastError = toApiError(response.status, rawBody, retryAfterMs, requestId);\n\n if (!lastError.retryable || attempt === retry.maxRetries) throw lastError;\n await sleep(retryAfterMs ?? backoffDelay(attempt, retry), signal);\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new DataApiError({ message: \"reopt-data request failed\", status: 0, code: \"network_error\" });\n }\n\n /**\n * The ingest and query planes; both require client credentials.\n *\n * `async` so a missing credential rejects rather than throwing\n * synchronously — an async method that sometimes throws before returning a\n * promise slips past every `.catch()` a caller writes.\n */\n async function post<T>(path: string, body: unknown, parse: (value: unknown) => T, signal?: AbortSignal): Promise<T> {\n requireClientCredentials();\n return send(\"POST\", path, body, parse, {}, signal);\n }\n\n return {\n ingest: {\n track(events, init) {\n return post(TRACK_PATH, events, (value) => zIngestResponse.parse(value), init?.signal);\n },\n },\n query: {\n eventsTimeseries(input, init) {\n return post(\n QUERY_API_PATHS.eventsTimeseries,\n input,\n (value) => zEventsTimeseriesResponse.parse(value),\n init?.signal\n );\n },\n funnel(input, init) {\n return post(QUERY_API_PATHS.funnel, input, (value) => zFunnelResponse.parse(value), init?.signal);\n },\n retention(input, init) {\n return post(QUERY_API_PATHS.retention, input, (value) => zRetentionResponse.parse(value), init?.signal);\n },\n trafficOverview(input, init) {\n return post(\n QUERY_API_PATHS.trafficOverview,\n input,\n (value) => zTrafficOverviewResponse.parse(value),\n init?.signal\n );\n },\n trafficSources(input, init) {\n return post(\n QUERY_API_PATHS.trafficSources,\n input,\n (value) => zTrafficSourcesResponse.parse(value),\n init?.signal\n );\n },\n },\n control: {\n createOrganization(input, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.organizations,\n input,\n (value) => zCreateOrganizationResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n rotateOrgKey(organizationId, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.organizationKeys(organizationId),\n {},\n (value) => zRotateOrganizationKeyResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n updateOrganizationQuota(organizationId, input, auth, init) {\n return send(\n \"PATCH\",\n CONTROL_API_PATHS.organization(organizationId),\n input,\n (value) => zUpdateOrganizationQuotaResponse.parse(value),\n { [PLATFORM_KEY_HEADER]: auth.platformKey },\n init?.signal\n );\n },\n createProject(input, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.projects,\n input,\n (value) => zCreateProjectResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n rotateServerClientSecret(projectId, clientId, auth, init) {\n return send(\n \"POST\",\n CONTROL_API_PATHS.clientRotate(projectId, clientId),\n {},\n (value) => zRotateClientSecretResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n deleteProject(projectId, input, auth, init) {\n return send(\n \"DELETE\",\n CONTROL_API_PATHS.project(projectId),\n input,\n (value) => zDeleteProjectResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n upsertEventMeta(projectId, name, input, auth, init) {\n return send(\n \"PUT\",\n CONTROL_API_PATHS.projectEvent(projectId, name),\n input,\n (value) => zEventMetaResponse.parse(value),\n { [ORG_KEY_HEADER]: auth.orgKey },\n init?.signal\n );\n },\n },\n };\n}\n"]}
package/dist/client.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { ClientCredentials } from './index.cjs';
1
+ import { OptionalClientCredentials } from './index.cjs';
2
2
  import { ITrackHandlerPayload, IngestResponse } from './ingest.cjs';
3
- import { CreateOrganizationInput, CreateOrganizationResponse, RotateOrganizationKeyResponse, UpdateOrganizationQuotaInput, UpdateOrganizationQuotaResponse, CreateProjectInput, CreateProjectResponse, RotateClientSecretResponse, DeleteProjectInput, DeleteProjectResponse } from './control.cjs';
4
- import { EventsTimeseriesInput, EventsTimeseriesResponse, FunnelInput, FunnelResponse, RetentionInput, RetentionResponse } from './query.cjs';
3
+ import { CreateOrganizationInput, CreateOrganizationResponse, RotateOrganizationKeyResponse, UpdateOrganizationQuotaInput, UpdateOrganizationQuotaResponse, CreateProjectInput, CreateProjectResponse, RotateClientSecretResponse, DeleteProjectInput, DeleteProjectResponse, UpsertEventMetaInput, EventMetaResponse } from './control.cjs';
4
+ import { EventsTimeseriesInput, EventsTimeseriesResponse, FunnelInput, FunnelResponse, RetentionInput, RetentionResponse, TrafficOverviewInput, TrafficOverviewResponse, TrafficSourcesInput, TrafficSourcesResponse } from './query.cjs';
5
5
  import 'zod';
6
6
 
7
7
  /**
@@ -26,7 +26,7 @@ interface DataClientRetryOptions {
26
26
  /** Prefer the server's `Retry-After` over the computed backoff. Default true. */
27
27
  respectRetryAfter?: boolean;
28
28
  }
29
- interface DataClientOptions extends ClientCredentials {
29
+ interface DataClientOptions extends OptionalClientCredentials {
30
30
  /** Origin of the reopt-data deployment, e.g. `https://data.reopt.app`. Trailing slash ignored. */
31
31
  baseUrl: string;
32
32
  /** Injected so callers can pass a framework-instrumented fetch. Defaults to `globalThis.fetch`. */
@@ -66,6 +66,8 @@ interface DataClient {
66
66
  eventsTimeseries(input: EventsTimeseriesInput, init?: DataRequestInit): Promise<EventsTimeseriesResponse>;
67
67
  funnel(input: FunnelInput, init?: DataRequestInit): Promise<FunnelResponse>;
68
68
  retention(input: RetentionInput, init?: DataRequestInit): Promise<RetentionResponse>;
69
+ trafficOverview(input: TrafficOverviewInput, init?: DataRequestInit): Promise<TrafficOverviewResponse>;
70
+ trafficSources(input: TrafficSourcesInput, init?: DataRequestInit): Promise<TrafficSourcesResponse>;
69
71
  };
70
72
  control: {
71
73
  /** Idempotent on `externalId`. `orgKey` comes back only on first creation. */
@@ -78,6 +80,8 @@ interface DataClient {
78
80
  rotateServerClientSecret(projectId: string, clientId: string, auth: OrganizationAuth, init?: DataRequestInit): Promise<RotateClientSecretResponse>;
79
81
  /** Irreversible. Idempotent: a project that is already gone still answers 202. */
80
82
  deleteProject(projectId: string, input: DeleteProjectInput, auth: OrganizationAuth, init?: DataRequestInit): Promise<DeleteProjectResponse>;
83
+ /** Idempotent: a repeat with the same body changes nothing and answers 200. */
84
+ upsertEventMeta(projectId: string, name: string, input: UpsertEventMetaInput, auth: OrganizationAuth, init?: DataRequestInit): Promise<EventMetaResponse>;
81
85
  };
82
86
  }
83
87
  declare function createDataClient(options: DataClientOptions): DataClient;
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { ClientCredentials } from './index.js';
1
+ import { OptionalClientCredentials } from './index.js';
2
2
  import { ITrackHandlerPayload, IngestResponse } from './ingest.js';
3
- import { CreateOrganizationInput, CreateOrganizationResponse, RotateOrganizationKeyResponse, UpdateOrganizationQuotaInput, UpdateOrganizationQuotaResponse, CreateProjectInput, CreateProjectResponse, RotateClientSecretResponse, DeleteProjectInput, DeleteProjectResponse } from './control.js';
4
- import { EventsTimeseriesInput, EventsTimeseriesResponse, FunnelInput, FunnelResponse, RetentionInput, RetentionResponse } from './query.js';
3
+ import { CreateOrganizationInput, CreateOrganizationResponse, RotateOrganizationKeyResponse, UpdateOrganizationQuotaInput, UpdateOrganizationQuotaResponse, CreateProjectInput, CreateProjectResponse, RotateClientSecretResponse, DeleteProjectInput, DeleteProjectResponse, UpsertEventMetaInput, EventMetaResponse } from './control.js';
4
+ import { EventsTimeseriesInput, EventsTimeseriesResponse, FunnelInput, FunnelResponse, RetentionInput, RetentionResponse, TrafficOverviewInput, TrafficOverviewResponse, TrafficSourcesInput, TrafficSourcesResponse } from './query.js';
5
5
  import 'zod';
6
6
 
7
7
  /**
@@ -26,7 +26,7 @@ interface DataClientRetryOptions {
26
26
  /** Prefer the server's `Retry-After` over the computed backoff. Default true. */
27
27
  respectRetryAfter?: boolean;
28
28
  }
29
- interface DataClientOptions extends ClientCredentials {
29
+ interface DataClientOptions extends OptionalClientCredentials {
30
30
  /** Origin of the reopt-data deployment, e.g. `https://data.reopt.app`. Trailing slash ignored. */
31
31
  baseUrl: string;
32
32
  /** Injected so callers can pass a framework-instrumented fetch. Defaults to `globalThis.fetch`. */
@@ -66,6 +66,8 @@ interface DataClient {
66
66
  eventsTimeseries(input: EventsTimeseriesInput, init?: DataRequestInit): Promise<EventsTimeseriesResponse>;
67
67
  funnel(input: FunnelInput, init?: DataRequestInit): Promise<FunnelResponse>;
68
68
  retention(input: RetentionInput, init?: DataRequestInit): Promise<RetentionResponse>;
69
+ trafficOverview(input: TrafficOverviewInput, init?: DataRequestInit): Promise<TrafficOverviewResponse>;
70
+ trafficSources(input: TrafficSourcesInput, init?: DataRequestInit): Promise<TrafficSourcesResponse>;
69
71
  };
70
72
  control: {
71
73
  /** Idempotent on `externalId`. `orgKey` comes back only on first creation. */
@@ -78,6 +80,8 @@ interface DataClient {
78
80
  rotateServerClientSecret(projectId: string, clientId: string, auth: OrganizationAuth, init?: DataRequestInit): Promise<RotateClientSecretResponse>;
79
81
  /** Irreversible. Idempotent: a project that is already gone still answers 202. */
80
82
  deleteProject(projectId: string, input: DeleteProjectInput, auth: OrganizationAuth, init?: DataRequestInit): Promise<DeleteProjectResponse>;
83
+ /** Idempotent: a repeat with the same body changes nothing and answers 200. */
84
+ upsertEventMeta(projectId: string, name: string, input: UpsertEventMetaInput, auth: OrganizationAuth, init?: DataRequestInit): Promise<EventMetaResponse>;
81
85
  };
82
86
  }
83
87
  declare function createDataClient(options: DataClientOptions): DataClient;
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  zIngestResponse
3
- } from "./chunk-FRJK7OCQ.js";
3
+ } from "./chunk-MFZEIH5Z.js";
4
4
  import {
5
5
  CONTROL_API_PATHS,
6
6
  ORG_KEY_HEADER,
@@ -8,23 +8,27 @@ import {
8
8
  zCreateOrganizationResponse,
9
9
  zCreateProjectResponse,
10
10
  zDeleteProjectResponse,
11
+ zEventMetaResponse,
11
12
  zRotateClientSecretResponse,
12
13
  zRotateOrganizationKeyResponse,
13
14
  zUpdateOrganizationQuotaResponse
14
- } from "./chunk-2SJPZP2D.js";
15
+ } from "./chunk-AHXPQQKO.js";
15
16
  import {
16
17
  QUERY_API_PATHS,
17
18
  zEventsTimeseriesResponse,
18
19
  zFunnelResponse,
19
- zRetentionResponse
20
- } from "./chunk-SC7TFGTO.js";
20
+ zRetentionResponse,
21
+ zTrafficOverviewResponse,
22
+ zTrafficSourcesResponse
23
+ } from "./chunk-BCGR27G2.js";
21
24
  import {
22
25
  CLIENT_ID_HEADER,
23
26
  CLIENT_SECRET_HEADER,
24
27
  CONTRACT_VERSION,
28
+ CONTRACT_VERSION_HEADER,
25
29
  DataApiError,
26
30
  REQUEST_ID_HEADER
27
- } from "./chunk-IKOEYZVT.js";
31
+ } from "./chunk-2LZBZXBF.js";
28
32
 
29
33
  // src/client.ts
30
34
  var TRACK_PATH = "/api/track";
@@ -107,6 +111,16 @@ function createDataClient(options) {
107
111
  respectRetryAfter: options.retry?.respectRetryAfter ?? true
108
112
  };
109
113
  const userAgent = options.userAgent ? `reopt-data-contract/${CONTRACT_VERSION} ${options.userAgent}` : `reopt-data-contract/${CONTRACT_VERSION}`;
114
+ function requireClientCredentials() {
115
+ if (!options.clientId || !options.clientSecret) {
116
+ throw new DataApiError({
117
+ message: "createDataClient was given no client credentials \u2014 pass clientId and clientSecret to use the ingest or query planes",
118
+ status: 0,
119
+ code: "credentials_missing"
120
+ });
121
+ }
122
+ return { clientId: options.clientId, clientSecret: options.clientSecret };
123
+ }
110
124
  async function requestOnce(method, path, body, extraHeaders, signal) {
111
125
  const controller = new AbortController();
112
126
  const timer = setTimeout(
@@ -121,8 +135,7 @@ function createDataClient(options) {
121
135
  headers: {
122
136
  "content-type": "application/json",
123
137
  "user-agent": userAgent,
124
- [CLIENT_ID_HEADER]: options.clientId,
125
- [CLIENT_SECRET_HEADER]: options.clientSecret,
138
+ ...options.clientId && options.clientSecret ? { [CLIENT_ID_HEADER]: options.clientId, [CLIENT_SECRET_HEADER]: options.clientSecret } : {},
126
139
  ...extraHeaders
127
140
  },
128
141
  body: JSON.stringify(body),
@@ -154,7 +167,17 @@ function createDataClient(options) {
154
167
  throw lastError;
155
168
  }
156
169
  const requestId = response.headers.get(REQUEST_ID_HEADER) ?? void 0;
170
+ const serverContractVersion = response.headers.get(CONTRACT_VERSION_HEADER);
157
171
  const rawBody = await response.text();
172
+ if (serverContractVersion !== CONTRACT_VERSION) {
173
+ throw new DataApiError({
174
+ message: serverContractVersion ? `reopt-data contract ${serverContractVersion} does not match client contract ${CONTRACT_VERSION}` : "reopt-data response did not declare its contract version",
175
+ status: response.status,
176
+ code: "contract_mismatch",
177
+ requestId,
178
+ details: { expected: CONTRACT_VERSION, received: serverContractVersion }
179
+ });
180
+ }
158
181
  if (response.ok) {
159
182
  let json;
160
183
  try {
@@ -187,7 +210,8 @@ function createDataClient(options) {
187
210
  }
188
211
  throw lastError ?? new DataApiError({ message: "reopt-data request failed", status: 0, code: "network_error" });
189
212
  }
190
- function post(path, body, parse, signal) {
213
+ async function post(path, body, parse, signal) {
214
+ requireClientCredentials();
191
215
  return send("POST", path, body, parse, {}, signal);
192
216
  }
193
217
  return {
@@ -210,6 +234,22 @@ function createDataClient(options) {
210
234
  },
211
235
  retention(input, init) {
212
236
  return post(QUERY_API_PATHS.retention, input, (value) => zRetentionResponse.parse(value), init?.signal);
237
+ },
238
+ trafficOverview(input, init) {
239
+ return post(
240
+ QUERY_API_PATHS.trafficOverview,
241
+ input,
242
+ (value) => zTrafficOverviewResponse.parse(value),
243
+ init?.signal
244
+ );
245
+ },
246
+ trafficSources(input, init) {
247
+ return post(
248
+ QUERY_API_PATHS.trafficSources,
249
+ input,
250
+ (value) => zTrafficSourcesResponse.parse(value),
251
+ init?.signal
252
+ );
213
253
  }
214
254
  },
215
255
  control: {
@@ -272,6 +312,16 @@ function createDataClient(options) {
272
312
  { [ORG_KEY_HEADER]: auth.orgKey },
273
313
  init?.signal
274
314
  );
315
+ },
316
+ upsertEventMeta(projectId, name, input, auth, init) {
317
+ return send(
318
+ "PUT",
319
+ CONTROL_API_PATHS.projectEvent(projectId, name),
320
+ input,
321
+ (value) => zEventMetaResponse.parse(value),
322
+ { [ORG_KEY_HEADER]: auth.orgKey },
323
+ init?.signal
324
+ );
275
325
  }
276
326
  }
277
327
  };