@allisall/sdk 0.0.0-stage → 0.2.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 (70) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +227 -2
  4. package/dist/client.d.ts +121 -0
  5. package/dist/client.js +193 -0
  6. package/dist/decimal.d.ts +20 -0
  7. package/dist/decimal.js +23 -0
  8. package/dist/errors.d.ts +79 -0
  9. package/dist/errors.js +98 -0
  10. package/dist/generated/openapi.d.ts +7724 -0
  11. package/dist/generated/openapi.js +4 -0
  12. package/dist/generated/operations.d.ts +37 -0
  13. package/dist/generated/operations.js +976 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.js +5 -0
  16. package/dist/ingest.d.ts +28 -0
  17. package/dist/ingest.js +247 -0
  18. package/dist/money.d.ts +57 -0
  19. package/dist/money.js +284 -0
  20. package/dist/multipart.d.ts +27 -0
  21. package/dist/multipart.js +48 -0
  22. package/dist/options.d.ts +50 -0
  23. package/dist/options.js +73 -0
  24. package/dist/pagination.d.ts +55 -0
  25. package/dist/pagination.js +85 -0
  26. package/dist/request.d.ts +117 -0
  27. package/dist/request.js +363 -0
  28. package/dist/resources/agents.d.ts +51 -0
  29. package/dist/resources/agents.js +65 -0
  30. package/dist/resources/analytics.d.ts +42 -0
  31. package/dist/resources/analytics.js +68 -0
  32. package/dist/resources/api-keys.d.ts +54 -0
  33. package/dist/resources/api-keys.js +63 -0
  34. package/dist/resources/calculations.d.ts +78 -0
  35. package/dist/resources/calculations.js +111 -0
  36. package/dist/resources/clients.d.ts +65 -0
  37. package/dist/resources/clients.js +85 -0
  38. package/dist/resources/cost.d.ts +69 -0
  39. package/dist/resources/cost.js +89 -0
  40. package/dist/resources/dashboard.d.ts +24 -0
  41. package/dist/resources/dashboard.js +28 -0
  42. package/dist/resources/events.d.ts +47 -0
  43. package/dist/resources/events.js +69 -0
  44. package/dist/resources/health.d.ts +14 -0
  45. package/dist/resources/health.js +17 -0
  46. package/dist/resources/imports.d.ts +33 -0
  47. package/dist/resources/imports.js +37 -0
  48. package/dist/resources/outcome-types.d.ts +25 -0
  49. package/dist/resources/outcome-types.js +33 -0
  50. package/dist/resources/reports.d.ts +77 -0
  51. package/dist/resources/reports.js +103 -0
  52. package/dist/resources/value-models.d.ts +92 -0
  53. package/dist/resources/value-models.js +126 -0
  54. package/dist/retry.d.ts +18 -0
  55. package/dist/retry.js +105 -0
  56. package/dist/runtime.d.ts +19 -0
  57. package/dist/runtime.js +61 -0
  58. package/dist/share.d.ts +52 -0
  59. package/dist/share.js +60 -0
  60. package/dist/transport.d.ts +102 -0
  61. package/dist/transport.js +269 -0
  62. package/dist/types.d.ts +268 -0
  63. package/dist/types.js +89 -0
  64. package/dist/unicode.d.ts +2 -0
  65. package/dist/unicode.js +11 -0
  66. package/dist/uuid.d.ts +6 -0
  67. package/dist/uuid.js +20 -0
  68. package/dist/version.d.ts +2 -0
  69. package/dist/version.js +2 -0
  70. package/package.json +40 -4
package/dist/client.js ADDED
@@ -0,0 +1,193 @@
1
+ // The client: its options, and how the credential and the base URL are
2
+ // resolved at construction.
3
+ import { AllisallError } from "./errors.js";
4
+ import { DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, resolveHeaders, resolveMaxRetries, resolveTimeout, } from "./options.js";
5
+ import { Agents } from "./resources/agents.js";
6
+ import { Analytics } from "./resources/analytics.js";
7
+ import { ApiKeys } from "./resources/api-keys.js";
8
+ import { Calculations } from "./resources/calculations.js";
9
+ import { Clients } from "./resources/clients.js";
10
+ import { Cost } from "./resources/cost.js";
11
+ import { DashboardOperations } from "./resources/dashboard.js";
12
+ import { Events } from "./resources/events.js";
13
+ import { HealthOperations } from "./resources/health.js";
14
+ import { Imports } from "./resources/imports.js";
15
+ import { OutcomeTypes } from "./resources/outcome-types.js";
16
+ import { Reports } from "./resources/reports.js";
17
+ import { ValueModels } from "./resources/value-models.js";
18
+ import { globalValue, isBrowser, readEnv, userAgent } from "./runtime.js";
19
+ import { BROWSER_KEY_MESSAGE, isPrintableCredential, } from "./transport.js";
20
+ const DEFAULT_BASE_URL = "https://api.allisai.ai";
21
+ function resolveCredential(options) {
22
+ const { apiKey, tokenProvider } = options;
23
+ if (apiKey !== undefined && tokenProvider !== undefined) {
24
+ throw new AllisallError("allisall: pass either apiKey or tokenProvider, not both");
25
+ }
26
+ if (tokenProvider !== undefined) {
27
+ if (typeof tokenProvider !== "function") {
28
+ throw new AllisallError("allisall: tokenProvider must be a function");
29
+ }
30
+ return { kind: "provider", provider: tokenProvider };
31
+ }
32
+ if (apiKey !== undefined) {
33
+ const key = typeof apiKey === "string" ? apiKey.trim() : "";
34
+ if (key === "") {
35
+ throw new AllisallError("allisall: apiKey is empty");
36
+ }
37
+ if (!isPrintableCredential(key)) {
38
+ throw new AllisallError("allisall: apiKey holds a character other than printable ASCII, which cannot be sent");
39
+ }
40
+ return { kind: "apiKey", apiKey: key };
41
+ }
42
+ // A variable of white space alone counts as unset.
43
+ const fromEnv = readEnv("AIA_API_KEY")?.trim();
44
+ if (fromEnv !== undefined && fromEnv !== "") {
45
+ if (!isPrintableCredential(fromEnv)) {
46
+ throw new AllisallError("allisall: AIA_API_KEY holds a character other than printable ASCII, which cannot be sent");
47
+ }
48
+ return { kind: "apiKey", apiKey: fromEnv };
49
+ }
50
+ throw new AllisallError("allisall: no credential; pass apiKey or tokenProvider, or set AIA_API_KEY");
51
+ }
52
+ /** Reads `globalThis.location.href`, where it exists. */
53
+ function pageAddress() {
54
+ const location = globalValue("location");
55
+ if (typeof location !== "object" || location === null)
56
+ return undefined;
57
+ const href = Reflect.get(location, "href");
58
+ return typeof href === "string" ? href : undefined;
59
+ }
60
+ /**
61
+ * Resolves the base URL: the option, else `AIA_BASE_URL`, else the API's
62
+ * own host. In a browser, a path starting with a single `/` is resolved
63
+ * against the page's address and must keep its origin.
64
+ *
65
+ * @throws {AllisallError} for a base URL the client refuses.
66
+ */
67
+ function resolveBaseURL(option, browser) {
68
+ const raw = option ?? readEnv("AIA_BASE_URL") ?? DEFAULT_BASE_URL;
69
+ // A path on the page's own origin, such as "/api". A leading "//" names
70
+ // another host, so it is not a path.
71
+ const pathOnly = typeof raw === "string" && raw.startsWith("/") && !raw.startsWith("//");
72
+ const page = browser && pathOnly ? pageAddress() : undefined;
73
+ let url;
74
+ try {
75
+ url = page === undefined ? new URL(raw) : new URL(raw, page);
76
+ }
77
+ catch {
78
+ throw new AllisallError(pathOnly && browser
79
+ ? `allisall: the base URL ${JSON.stringify(raw)} is a path, and the page's address could not be read to resolve it`
80
+ : `allisall: the base URL ${JSON.stringify(raw)} is not an absolute URL`);
81
+ }
82
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
83
+ throw new AllisallError(`allisall: the base URL ${JSON.stringify(raw)} is not http or https`);
84
+ }
85
+ // URL parsing reads a backslash as "/" and drops tabs and newlines, so a
86
+ // value such as "/\other.example/api" passes the "//" check above and
87
+ // still names another host. Only the resolved origin settles it.
88
+ if (page !== undefined && url.origin !== new URL(page).origin) {
89
+ throw new AllisallError(`allisall: the base URL ${JSON.stringify(raw)} is a path, but resolves to another origin, ${url.origin}`);
90
+ }
91
+ return url;
92
+ }
93
+ /**
94
+ * Resolves what every request needs apart from its credential and default
95
+ * headers: the base URL, timeout, retries, `fetch` and `User-Agent`, which
96
+ * a browser does not send.
97
+ *
98
+ * @throws {AllisallError} for a base URL, timeout or `maxRetries` the
99
+ * client refuses.
100
+ */
101
+ export function transportSettings(options, browser) {
102
+ return {
103
+ baseURL: resolveBaseURL(options.baseURL, browser),
104
+ timeout: resolveTimeout(options.timeout, DEFAULT_TIMEOUT_MS),
105
+ maxRetries: resolveMaxRetries(options.maxRetries, DEFAULT_MAX_RETRIES),
106
+ fetch: options.fetch,
107
+ userAgent: userAgent(),
108
+ };
109
+ }
110
+ /**
111
+ * Checks the client's options and fixes everything a request needs.
112
+ *
113
+ * @throws {AllisallError} for an option the client refuses.
114
+ * @throws {TypeError} when `defaultHeaders` is not a plain object of
115
+ * header names and string values, names a header the SDK sets, or holds a
116
+ * name or value the platform cannot send.
117
+ */
118
+ export function transportConfig(options) {
119
+ const credential = resolveCredential(options);
120
+ const browser = isBrowser();
121
+ const refuseApiKeys = browser && options.dangerouslyAllowBrowser !== true;
122
+ if (refuseApiKeys && credential.kind === "apiKey") {
123
+ throw new AllisallError(BROWSER_KEY_MESSAGE);
124
+ }
125
+ return {
126
+ ...transportSettings(options, browser),
127
+ credential,
128
+ refuseApiKeys,
129
+ defaultHeaders: resolveHeaders(options.defaultHeaders, "defaultHeaders"),
130
+ };
131
+ }
132
+ /**
133
+ * The allisall API client. Construct one and reuse it.
134
+ *
135
+ * Its operations are grouped by what they act on, one property per group,
136
+ * such as `client.clients.list()`. A failed call rejects with an `ApiError`
137
+ * for an error response, or a `ConnectionError` when no response arrived.
138
+ */
139
+ export class Allisall {
140
+ /** Events: ingest, and the events behind a client's totals. */
141
+ events;
142
+ /** CSV imports of events. */
143
+ imports;
144
+ /** Clients, and the agents assigned to each. */
145
+ clients;
146
+ /** Agents. */
147
+ agents;
148
+ /** Value models, by their own id or by their client's, and value templates. */
149
+ valueModels;
150
+ /** Outcome types, for the workspace or for one client. */
151
+ outcomeTypes;
152
+ /** Cost line items and the price table. */
153
+ cost;
154
+ /** A client's value, cost, ROI and anomalies over a period. */
155
+ analytics;
156
+ /** Calculations, and the human steps that validate, publish and reopen them. */
157
+ calculations;
158
+ /** Reports on calculations, and the human steps that share and revoke them. */
159
+ reports;
160
+ /** The workspace's month, and renewal readiness. */
161
+ dashboard;
162
+ /** API keys, managed with a session token. */
163
+ apiKeys;
164
+ /** The API's liveness. */
165
+ health;
166
+ /**
167
+ * @throws {AllisallError} when there is no credential, or both an API key
168
+ * and a token provider; when the base URL is neither an absolute `http`
169
+ * or `https` URL nor, in a browser, a path starting with a single `/`
170
+ * that resolves to the page's own origin; when `maxRetries` is negative
171
+ * or `timeout` is not positive; or when an API key is used in a browser
172
+ * without `dangerouslyAllowBrowser`.
173
+ * @throws {TypeError} when `defaultHeaders` is not a plain object of
174
+ * header names and string values, names a header the SDK sets, or holds a
175
+ * name or value the platform cannot send.
176
+ */
177
+ constructor(options = {}) {
178
+ const config = transportConfig(options);
179
+ this.events = new Events(config);
180
+ this.imports = new Imports(config);
181
+ this.clients = new Clients(config);
182
+ this.agents = new Agents(config);
183
+ this.valueModels = new ValueModels(config);
184
+ this.outcomeTypes = new OutcomeTypes(config);
185
+ this.cost = new Cost(config);
186
+ this.analytics = new Analytics(config);
187
+ this.calculations = new Calculations(config);
188
+ this.reports = new Reports(config);
189
+ this.dashboard = new DashboardOperations(config);
190
+ this.apiKeys = new ApiKeys(config);
191
+ this.health = new HealthOperations(config);
192
+ }
193
+ }
@@ -0,0 +1,20 @@
1
+ declare const decimalBrand: unique symbol;
2
+ /**
3
+ * An exact decimal amount, such as a `cost`. It is a string at run time,
4
+ * branded so that only `decimal()` produces one. The SDK writes its text
5
+ * into the request unchanged, as a JSON number, so no binary float ever
6
+ * rounds it.
7
+ */
8
+ export type Decimal = string & {
9
+ readonly [decimalBrand]: true;
10
+ };
11
+ /** Reports whether `text` has the syntax of a `Decimal`. */
12
+ export declare function isDecimalText(text: string): boolean;
13
+ /**
14
+ * Makes a `Decimal` from its text, such as `decimal("0.3683")`.
15
+ *
16
+ * @throws {TypeError} when `text` is not an optional `-`, digits without a
17
+ * leading zero (or a single `0`), and optional decimal places after a `.`.
18
+ */
19
+ export declare function decimal(text: string): Decimal;
20
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The syntax a `Decimal` accepts: an optional `-`, digits without a leading
3
+ * zero (or a single `0`), and optional decimal places after a `.`. Every
4
+ * string it accepts is a valid JSON number.
5
+ */
6
+ const DECIMAL_SYNTAX = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/;
7
+ /** Reports whether `text` has the syntax of a `Decimal`. */
8
+ export function isDecimalText(text) {
9
+ return DECIMAL_SYNTAX.test(text);
10
+ }
11
+ /**
12
+ * Makes a `Decimal` from its text, such as `decimal("0.3683")`.
13
+ *
14
+ * @throws {TypeError} when `text` is not an optional `-`, digits without a
15
+ * leading zero (or a single `0`), and optional decimal places after a `.`.
16
+ */
17
+ export function decimal(text) {
18
+ if (typeof text !== "string" || !isDecimalText(text)) {
19
+ throw new TypeError(`allisall: ${JSON.stringify(text)} is not a decimal; write it as digits with an optional "-" and decimal places, such as "12.5"`);
20
+ }
21
+ // The syntax check above is the invariant the brand stands for.
22
+ return text;
23
+ }
@@ -0,0 +1,79 @@
1
+ import type { ErrorCode, EventInput, IngestSummary, Problem } from "./types.js";
2
+ /**
3
+ * The base of every error the SDK throws. Catch it to handle any SDK
4
+ * failure; catch `ApiError`, `ConnectionError` or `IngestError` to handle
5
+ * one condition.
6
+ */
7
+ export declare class AllisallError extends Error {
8
+ name: string;
9
+ }
10
+ /** The members of an `ApiError`. */
11
+ export interface ApiErrorInit {
12
+ status: number;
13
+ problem?: Problem | undefined;
14
+ requestId?: string | undefined;
15
+ retryAfter?: number | undefined;
16
+ headers: Headers;
17
+ message?: string | undefined;
18
+ }
19
+ /**
20
+ * An error response from the API, or a successful response whose body was
21
+ * not the expected shape. Branch on `code`; for a code you do not
22
+ * recognise, fall back on `status`.
23
+ */
24
+ export declare class ApiError extends AllisallError {
25
+ name: string;
26
+ /** The HTTP status. */
27
+ readonly status: number;
28
+ /** The problem's `code`. Absent when the body was not a problem document. */
29
+ readonly code: ErrorCode | undefined;
30
+ /** The whole problem document. Absent when the body was not one. */
31
+ readonly problem: Problem | undefined;
32
+ /** The problem's `request_id`, or else the response's `X-Request-Id`. */
33
+ readonly requestId: string | undefined;
34
+ /**
35
+ * The response's `Retry-After`, in milliseconds from when the response
36
+ * arrived. Absent when the response had none, or one that did not parse
37
+ * or named a time in the past.
38
+ */
39
+ readonly retryAfter: number | undefined;
40
+ /** The response headers. */
41
+ readonly headers: Headers;
42
+ constructor(init: ApiErrorInit);
43
+ }
44
+ /**
45
+ * A request that got no response: the connection failed, was reset, or the
46
+ * attempt timed out. `cause` holds the underlying error.
47
+ */
48
+ export declare class ConnectionError extends AllisallError {
49
+ name: string;
50
+ /** The `X-Request-Id` the SDK sent on the last attempt, to quote when reporting it. */
51
+ readonly requestId: string;
52
+ constructor(requestId: string, cause: unknown);
53
+ }
54
+ /**
55
+ * A batch of an `ingest` call failed, so the call stopped and sent nothing
56
+ * after it. `cause` holds what failed the batch: an `ApiError`, a
57
+ * `ConnectionError`, the caller's cancellation, or the error from getting
58
+ * the token: the token provider's own error, unchanged, or the
59
+ * `AllisallError` refusing a provider's API key in a browser. To recover,
60
+ * send `remaining` again: every event in it keeps the `event_id` it was
61
+ * sent with, so any the failed batch stored come back as duplicates.
62
+ */
63
+ export declare class IngestError extends AllisallError {
64
+ name: string;
65
+ /** The combined outcome of the batches that succeeded before the failed one. */
66
+ readonly summary: IngestSummary;
67
+ /**
68
+ * Copies of every event from the failed batch onward, each with its
69
+ * `event_id`, less any event too large to send: those are rows of
70
+ * `summary.errors` and never in `remaining`. To map `remaining` back to
71
+ * your list, walk it from `remainingFrom` and skip the `index` of every
72
+ * `summary.errors` row at or past `remainingFrom`. With no such row,
73
+ * `remaining[i]` is your event `remainingFrom + i`.
74
+ */
75
+ readonly remaining: EventInput[];
76
+ /** The index, in the list passed to `ingest`, of the first event in `remaining`. */
77
+ readonly remainingFrom: number;
78
+ constructor(cause: unknown, summary: IngestSummary, remaining: EventInput[], remainingFrom: number);
79
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The base of every error the SDK throws. Catch it to handle any SDK
3
+ * failure; catch `ApiError`, `ConnectionError` or `IngestError` to handle
4
+ * one condition.
5
+ */
6
+ export class AllisallError extends Error {
7
+ name = "AllisallError";
8
+ }
9
+ /**
10
+ * An error response from the API, or a successful response whose body was
11
+ * not the expected shape. Branch on `code`; for a code you do not
12
+ * recognise, fall back on `status`.
13
+ */
14
+ export class ApiError extends AllisallError {
15
+ name = "ApiError";
16
+ /** The HTTP status. */
17
+ status;
18
+ /** The problem's `code`. Absent when the body was not a problem document. */
19
+ code;
20
+ /** The whole problem document. Absent when the body was not one. */
21
+ problem;
22
+ /** The problem's `request_id`, or else the response's `X-Request-Id`. */
23
+ requestId;
24
+ /**
25
+ * The response's `Retry-After`, in milliseconds from when the response
26
+ * arrived. Absent when the response had none, or one that did not parse
27
+ * or named a time in the past.
28
+ */
29
+ retryAfter;
30
+ /** The response headers. */
31
+ headers;
32
+ constructor(init) {
33
+ super(init.message ?? apiErrorMessage(init.status, init.problem));
34
+ this.status = init.status;
35
+ this.problem = init.problem;
36
+ this.code = init.problem?.code;
37
+ const problemId = init.problem?.request_id;
38
+ this.requestId =
39
+ problemId !== undefined && problemId !== "" ? problemId : init.requestId;
40
+ this.retryAfter = init.retryAfter;
41
+ this.headers = init.headers;
42
+ }
43
+ }
44
+ function apiErrorMessage(status, problem) {
45
+ const parts = [`allisall: the API answered ${status}`];
46
+ if (problem?.code !== undefined)
47
+ parts.push(problem.code);
48
+ if (problem?.detail !== undefined)
49
+ parts.push(problem.detail);
50
+ return parts.join(": ");
51
+ }
52
+ /**
53
+ * A request that got no response: the connection failed, was reset, or the
54
+ * attempt timed out. `cause` holds the underlying error.
55
+ */
56
+ export class ConnectionError extends AllisallError {
57
+ name = "ConnectionError";
58
+ /** The `X-Request-Id` the SDK sent on the last attempt, to quote when reporting it. */
59
+ requestId;
60
+ constructor(requestId, cause) {
61
+ super(`allisall: no response from the API (request ${requestId}): ${causeText(cause)}`, { cause });
62
+ this.requestId = requestId;
63
+ }
64
+ }
65
+ function causeText(cause) {
66
+ return cause instanceof Error ? cause.message : String(cause);
67
+ }
68
+ /**
69
+ * A batch of an `ingest` call failed, so the call stopped and sent nothing
70
+ * after it. `cause` holds what failed the batch: an `ApiError`, a
71
+ * `ConnectionError`, the caller's cancellation, or the error from getting
72
+ * the token: the token provider's own error, unchanged, or the
73
+ * `AllisallError` refusing a provider's API key in a browser. To recover,
74
+ * send `remaining` again: every event in it keeps the `event_id` it was
75
+ * sent with, so any the failed batch stored come back as duplicates.
76
+ */
77
+ export class IngestError extends AllisallError {
78
+ name = "IngestError";
79
+ /** The combined outcome of the batches that succeeded before the failed one. */
80
+ summary;
81
+ /**
82
+ * Copies of every event from the failed batch onward, each with its
83
+ * `event_id`, less any event too large to send: those are rows of
84
+ * `summary.errors` and never in `remaining`. To map `remaining` back to
85
+ * your list, walk it from `remainingFrom` and skip the `index` of every
86
+ * `summary.errors` row at or past `remainingFrom`. With no such row,
87
+ * `remaining[i]` is your event `remainingFrom + i`.
88
+ */
89
+ remaining;
90
+ /** The index, in the list passed to `ingest`, of the first event in `remaining`. */
91
+ remainingFrom;
92
+ constructor(cause, summary, remaining, remainingFrom) {
93
+ super(`allisall: an ingest batch failed and the call stopped; send the ${remaining.length} event(s) in remaining, from index ${remainingFrom}, again: ${causeText(cause)}`, { cause });
94
+ this.summary = summary;
95
+ this.remaining = remaining;
96
+ this.remainingFrom = remainingFrom;
97
+ }
98
+ }