@sinio/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.
- package/LICENSE +21 -0
- package/README.md +136 -2
- package/dist/cjs/client.d.ts +75 -0
- package/dist/cjs/client.js +196 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/errors.d.ts +35 -0
- package/dist/cjs/errors.js +60 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/http.d.ts +22 -0
- package/dist/cjs/http.js +139 -0
- package/dist/cjs/http.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/serialize.d.ts +49 -0
- package/dist/cjs/serialize.js +191 -0
- package/dist/cjs/serialize.js.map +1 -0
- package/dist/cjs/types.d.ts +126 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/vocabularies.d.ts +23 -0
- package/dist/cjs/vocabularies.js +39 -0
- package/dist/cjs/vocabularies.js.map +1 -0
- package/dist/esm/client.d.ts +75 -0
- package/dist/esm/client.js +192 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/errors.d.ts +35 -0
- package/dist/esm/errors.js +49 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/http.d.ts +22 -0
- package/dist/esm/http.js +134 -0
- package/dist/esm/http.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/serialize.d.ts +49 -0
- package/dist/esm/serialize.js +179 -0
- package/dist/esm/serialize.js.map +1 -0
- package/dist/esm/types.d.ts +126 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/vocabularies.d.ts +23 -0
- package/dist/esm/vocabularies.js +35 -0
- package/dist/esm/vocabularies.js.map +1 -0
- package/package.json +43 -4
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type FetchLike } from "./http.ts";
|
|
2
|
+
import type { AssignmentInput, AssignmentsResult, EntityInput, LocationInput, SendEventsOptions, SendEventsResult, SinioEvent, ProjectOptions, UpsertResult } from "./types.ts";
|
|
3
|
+
export declare const SDK_VERSION = "0.2.0";
|
|
4
|
+
export interface SinioClientOptions {
|
|
5
|
+
/** Machine credential, sent as `Authorization: Bearer`. Mutually exclusive with `webhookKey`. */
|
|
6
|
+
readonly apiKey?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Key carried in the URL instead of a header, for systems that can only be
|
|
9
|
+
* pointed at a webhook URL. Only `sendEvents` is available in this mode.
|
|
10
|
+
*/
|
|
11
|
+
readonly webhookKey?: string;
|
|
12
|
+
/** Defaults to `https://api.sinio.ai`. */
|
|
13
|
+
readonly baseUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Sent as `X-Source-Code`. Only for keys not bound to a single source (organization-wide keys,
|
|
16
|
+
* issued on request); a source key already carries its source.
|
|
17
|
+
*/
|
|
18
|
+
readonly sourceCode?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Default project `external_id`. Only for organization-wide keys (issued on request) covering
|
|
21
|
+
* several projects; a project key already carries its project.
|
|
22
|
+
*/
|
|
23
|
+
readonly project?: string;
|
|
24
|
+
/** Per-request timeout, default 30 s. */
|
|
25
|
+
readonly timeoutMs?: number;
|
|
26
|
+
/** Retries on 429, 5xx and dropped connections. Default 3. */
|
|
27
|
+
readonly maxRetries?: number;
|
|
28
|
+
/** First backoff ceiling, doubled per attempt with full jitter. Default 500 ms. */
|
|
29
|
+
readonly retryBaseDelayMs?: number;
|
|
30
|
+
/** Injectable transport, for tests or for Node < 18. */
|
|
31
|
+
readonly fetch?: FetchLike;
|
|
32
|
+
/** Injectable sleep, so tests do not wait on retries. */
|
|
33
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Client for the sinio.ai ingestion API.
|
|
37
|
+
*
|
|
38
|
+
* Every method accepts an arbitrarily long list and splits it into calls of
|
|
39
|
+
* 500 items, the API limit, merging the counters and rejections back into a
|
|
40
|
+
* single result. Sending the same events twice is safe: idempotency is keyed
|
|
41
|
+
* on `(project, source, source_event_id, revision)`, so a replay is counted as
|
|
42
|
+
* a duplicate rather than stored again.
|
|
43
|
+
*/
|
|
44
|
+
export declare class SinioClient {
|
|
45
|
+
private readonly transport;
|
|
46
|
+
private readonly authHeaders;
|
|
47
|
+
private readonly eventsPath;
|
|
48
|
+
private readonly webhookMode;
|
|
49
|
+
private readonly sourceCode;
|
|
50
|
+
private readonly project;
|
|
51
|
+
constructor(options: SinioClientOptions);
|
|
52
|
+
/** `POST /v1/events` — live ingestion. Derivation is asynchronous: a 200 confirms receipt only. */
|
|
53
|
+
sendEvents(events: readonly SinioEvent[], options?: SendEventsOptions): Promise<SendEventsResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Same endpoint with `mode: "backfill"`: history is persisted but nothing is
|
|
56
|
+
* alerted, and derivation is replayed later in a canonical order.
|
|
57
|
+
*/
|
|
58
|
+
backfillEvents(events: readonly SinioEvent[], options?: Omit<SendEventsOptions, "mode">): Promise<SendEventsResult>;
|
|
59
|
+
/**
|
|
60
|
+
* `POST /v1/entities` — bulk upsert keyed on `(kind, external_id)`. An unknown
|
|
61
|
+
* `subkind` never fails the call: it is created undeclared, for someone
|
|
62
|
+
* to qualify in the console.
|
|
63
|
+
*/
|
|
64
|
+
upsertEntities(entities: readonly EntityInput[], options?: ProjectOptions): Promise<UpsertResult>;
|
|
65
|
+
/** `POST /v1/locations` — bulk upsert of the location tree, keyed on `external_id`. */
|
|
66
|
+
upsertLocations(locations: readonly LocationInput[], options?: ProjectOptions): Promise<UpsertResult>;
|
|
67
|
+
/**
|
|
68
|
+
* `POST /v1/assignments` — `credential -> subject` with a validity range. An
|
|
69
|
+
* overlap is rejected with `overlapping_assignment`, never merged silently.
|
|
70
|
+
*/
|
|
71
|
+
createAssignments(assignments: readonly AssignmentInput[], options?: ProjectOptions): Promise<AssignmentsResult>;
|
|
72
|
+
private upsertReference;
|
|
73
|
+
private withProject;
|
|
74
|
+
private assertBearerMode;
|
|
75
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { SinioValidationError } from "./errors.js";
|
|
2
|
+
import { Transport } from "./http.js";
|
|
3
|
+
import { MAX_ITEMS_PER_CALL, chunk, requireList, toWireAssignment, toWireEntity, toWireEvent, toWireLocation, toWireMode, } from "./serialize.js";
|
|
4
|
+
export const SDK_VERSION = "0.2.0";
|
|
5
|
+
/**
|
|
6
|
+
* Client for the sinio.ai ingestion API.
|
|
7
|
+
*
|
|
8
|
+
* Every method accepts an arbitrarily long list and splits it into calls of
|
|
9
|
+
* 500 items, the API limit, merging the counters and rejections back into a
|
|
10
|
+
* single result. Sending the same events twice is safe: idempotency is keyed
|
|
11
|
+
* on `(project, source, source_event_id, revision)`, so a replay is counted as
|
|
12
|
+
* a duplicate rather than stored again.
|
|
13
|
+
*/
|
|
14
|
+
export class SinioClient {
|
|
15
|
+
transport;
|
|
16
|
+
authHeaders;
|
|
17
|
+
eventsPath;
|
|
18
|
+
webhookMode;
|
|
19
|
+
sourceCode;
|
|
20
|
+
project;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
const hasApiKey = typeof options.apiKey === "string" && options.apiKey.length > 0;
|
|
23
|
+
const hasWebhookKey = typeof options.webhookKey === "string" && options.webhookKey.length > 0;
|
|
24
|
+
if (hasApiKey === hasWebhookKey) {
|
|
25
|
+
throw new SinioValidationError("Provide exactly one of `apiKey` or `webhookKey`.");
|
|
26
|
+
}
|
|
27
|
+
this.webhookMode = hasWebhookKey;
|
|
28
|
+
this.authHeaders = hasApiKey ? { authorization: `Bearer ${options.apiKey}` } : {};
|
|
29
|
+
this.eventsPath = hasWebhookKey ? `/v1/webhooks/${encodeURIComponent(options.webhookKey)}` : "/v1/events";
|
|
30
|
+
this.sourceCode = options.sourceCode;
|
|
31
|
+
this.project = options.project;
|
|
32
|
+
this.transport = new Transport({
|
|
33
|
+
baseUrl: options.baseUrl ?? "https://api.sinio.ai",
|
|
34
|
+
timeoutMs: options.timeoutMs ?? 30_000,
|
|
35
|
+
maxRetries: options.maxRetries ?? 3,
|
|
36
|
+
retryBaseDelayMs: options.retryBaseDelayMs ?? 500,
|
|
37
|
+
userAgent: `sinio-node/${SDK_VERSION}`,
|
|
38
|
+
fetch: options.fetch,
|
|
39
|
+
sleep: options.sleep,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** `POST /v1/events` — live ingestion. Derivation is asynchronous: a 200 confirms receipt only. */
|
|
43
|
+
async sendEvents(events, options = {}) {
|
|
44
|
+
const mode = toWireMode(options.mode ?? "webhook");
|
|
45
|
+
const wire = requireList(events, "events").map((event, index) => toWireEvent(withDefaultProject(event, this.project), index));
|
|
46
|
+
const sourceCode = options.sourceCode ?? this.sourceCode;
|
|
47
|
+
const headers = sourceCode === undefined ? {} : { "x-source-code": sourceCode };
|
|
48
|
+
const merged = {
|
|
49
|
+
batchIds: [],
|
|
50
|
+
accepted: 0,
|
|
51
|
+
pendingClassification: 0,
|
|
52
|
+
duplicates: 0,
|
|
53
|
+
revisions: 0,
|
|
54
|
+
rejected: 0,
|
|
55
|
+
rejections: [],
|
|
56
|
+
testMode: false,
|
|
57
|
+
};
|
|
58
|
+
for (const part of chunk(wire, MAX_ITEMS_PER_CALL)) {
|
|
59
|
+
const response = asRecord(await this.transport.post(this.eventsPath, { events: part, mode }, { ...this.authHeaders, ...headers }));
|
|
60
|
+
const batchId = asString(response["batch_id"]);
|
|
61
|
+
if (batchId !== null) {
|
|
62
|
+
merged.batchIds.push(batchId);
|
|
63
|
+
}
|
|
64
|
+
merged.accepted += asNumber(response["accepted"]);
|
|
65
|
+
merged.pendingClassification += asNumber(response["pending_classification"]);
|
|
66
|
+
merged.duplicates += asNumber(response["duplicates"]);
|
|
67
|
+
merged.revisions += asNumber(response["revisions"]);
|
|
68
|
+
merged.rejected += asNumber(response["rejected"]);
|
|
69
|
+
merged.testMode = merged.testMode || response["test_mode"] === true;
|
|
70
|
+
for (const rejection of asArray(response["rejections"])) {
|
|
71
|
+
const item = asRecord(rejection);
|
|
72
|
+
merged.rejections.push({
|
|
73
|
+
sourceEventId: asString(item["source_event_id"]) ?? "",
|
|
74
|
+
code: asCode(item["code"]),
|
|
75
|
+
message: asString(item["message"]) ?? "",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return merged;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Same endpoint with `mode: "backfill"`: history is persisted but nothing is
|
|
83
|
+
* alerted, and derivation is replayed later in a canonical order.
|
|
84
|
+
*/
|
|
85
|
+
async backfillEvents(events, options = {}) {
|
|
86
|
+
return this.sendEvents(events, { ...options, mode: "backfill" });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* `POST /v1/entities` — bulk upsert keyed on `(kind, external_id)`. An unknown
|
|
90
|
+
* `subkind` never fails the call: it is created undeclared, for someone
|
|
91
|
+
* to qualify in the console.
|
|
92
|
+
*/
|
|
93
|
+
async upsertEntities(entities, options = {}) {
|
|
94
|
+
this.assertBearerMode("upsertEntities");
|
|
95
|
+
return this.upsertReference("/v1/entities", "entities", requireList(entities, "entities").map((entity, index) => toWireEntity(entity, index)), options);
|
|
96
|
+
}
|
|
97
|
+
/** `POST /v1/locations` — bulk upsert of the location tree, keyed on `external_id`. */
|
|
98
|
+
async upsertLocations(locations, options = {}) {
|
|
99
|
+
this.assertBearerMode("upsertLocations");
|
|
100
|
+
return this.upsertReference("/v1/locations", "locations", requireList(locations, "locations").map((location, index) => toWireLocation(location, index)), options);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* `POST /v1/assignments` — `credential -> subject` with a validity range. An
|
|
104
|
+
* overlap is rejected with `overlapping_assignment`, never merged silently.
|
|
105
|
+
*/
|
|
106
|
+
async createAssignments(assignments, options = {}) {
|
|
107
|
+
this.assertBearerMode("createAssignments");
|
|
108
|
+
const wire = requireList(assignments, "assignments").map((assignment, index) => toWireAssignment(assignment, index));
|
|
109
|
+
const merged = {
|
|
110
|
+
accepted: 0,
|
|
111
|
+
rejected: 0,
|
|
112
|
+
rejections: [],
|
|
113
|
+
};
|
|
114
|
+
for (const part of chunk(wire, MAX_ITEMS_PER_CALL)) {
|
|
115
|
+
const response = asRecord(await this.transport.post("/v1/assignments", this.withProject({ assignments: part }, options), this.authHeaders));
|
|
116
|
+
merged.accepted += asNumber(response["accepted"]);
|
|
117
|
+
merged.rejected += asNumber(response["rejected"]);
|
|
118
|
+
for (const rejection of asArray(response["rejections"])) {
|
|
119
|
+
const item = asRecord(rejection);
|
|
120
|
+
merged.rejections.push({
|
|
121
|
+
credential: asString(item["credential"]) ?? "",
|
|
122
|
+
subject: asString(item["subject"]) ?? "",
|
|
123
|
+
code: asCode(item["code"]),
|
|
124
|
+
message: asString(item["message"]) ?? "",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return merged;
|
|
129
|
+
}
|
|
130
|
+
async upsertReference(path, key, wire, options) {
|
|
131
|
+
const merged = {
|
|
132
|
+
accepted: 0,
|
|
133
|
+
rejected: 0,
|
|
134
|
+
rejections: [],
|
|
135
|
+
};
|
|
136
|
+
for (const part of chunk(wire, MAX_ITEMS_PER_CALL)) {
|
|
137
|
+
const response = asRecord(await this.transport.post(path, this.withProject({ [key]: part }, options), this.authHeaders));
|
|
138
|
+
merged.accepted += asNumber(response["accepted"]);
|
|
139
|
+
merged.rejected += asNumber(response["rejected"]);
|
|
140
|
+
for (const rejection of asArray(response["rejections"])) {
|
|
141
|
+
const item = asRecord(rejection);
|
|
142
|
+
merged.rejections.push({
|
|
143
|
+
externalId: asString(item["external_id"]) ?? "",
|
|
144
|
+
code: asCode(item["code"]),
|
|
145
|
+
message: asString(item["message"]) ?? "",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return merged;
|
|
150
|
+
}
|
|
151
|
+
withProject(body, options) {
|
|
152
|
+
const project = optionalProject(options.project) ?? this.project;
|
|
153
|
+
return project === undefined ? body : { project, ...body };
|
|
154
|
+
}
|
|
155
|
+
assertBearerMode(operation) {
|
|
156
|
+
if (this.webhookMode) {
|
|
157
|
+
throw new SinioValidationError(`${operation} requires an API key: a webhook key only authenticates event ingestion.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** The client-level project applies only to an event that does not name its own. */
|
|
162
|
+
function withDefaultProject(event, project) {
|
|
163
|
+
if (project === undefined || typeof event !== "object" || event === null || event.project !== undefined) {
|
|
164
|
+
return event;
|
|
165
|
+
}
|
|
166
|
+
return { ...event, project };
|
|
167
|
+
}
|
|
168
|
+
function optionalProject(value) {
|
|
169
|
+
if (value !== undefined && typeof value !== "string") {
|
|
170
|
+
throw new SinioValidationError(`project must be a string (got ${JSON.stringify(value)})`);
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
/** A known code or any code the API adds later, kept as is; never a reason to fail. */
|
|
175
|
+
function asCode(value) {
|
|
176
|
+
return asString(value) ?? "unknown";
|
|
177
|
+
}
|
|
178
|
+
function asRecord(value) {
|
|
179
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
180
|
+
? value
|
|
181
|
+
: {};
|
|
182
|
+
}
|
|
183
|
+
function asArray(value) {
|
|
184
|
+
return Array.isArray(value) ? value : [];
|
|
185
|
+
}
|
|
186
|
+
function asNumber(value) {
|
|
187
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
188
|
+
}
|
|
189
|
+
function asString(value) {
|
|
190
|
+
return typeof value === "string" ? value : null;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RejectionCodeValue } from "./types.ts";
|
|
2
|
+
/** Base class for every error the SDK throws. */
|
|
3
|
+
export declare class SinioError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** The request never got a usable HTTP response (DNS, TCP, TLS, timeout). */
|
|
7
|
+
export declare class SinioConnectionError extends SinioError {
|
|
8
|
+
readonly reason: unknown;
|
|
9
|
+
constructor(message: string, reason?: unknown);
|
|
10
|
+
}
|
|
11
|
+
/** A 4xx or 5xx response. */
|
|
12
|
+
export declare class SinioApiError extends SinioError {
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly body: unknown;
|
|
15
|
+
/** Rejection code returned by the API, when the body carries one (a known `RejectionCode` or a newer one). */
|
|
16
|
+
readonly code: RejectionCodeValue | null;
|
|
17
|
+
constructor(message: string, status: number, body: unknown, code?: RejectionCodeValue | null);
|
|
18
|
+
}
|
|
19
|
+
/** 401 / 403 — invalid, revoked, or insufficiently scoped key. Never replay. */
|
|
20
|
+
export declare class SinioAuthError extends SinioApiError {
|
|
21
|
+
}
|
|
22
|
+
/** 400 / 404 / 413 — the request itself is wrong. Fix it before replaying. */
|
|
23
|
+
export declare class SinioRequestError extends SinioApiError {
|
|
24
|
+
}
|
|
25
|
+
/** 429 — quota exceeded. `retryAfterSeconds` mirrors the `Retry-After` header. */
|
|
26
|
+
export declare class SinioRateLimitError extends SinioApiError {
|
|
27
|
+
readonly retryAfterSeconds: number | null;
|
|
28
|
+
constructor(message: string, status: number, body: unknown, retryAfterSeconds: number | null);
|
|
29
|
+
}
|
|
30
|
+
/** 5xx — temporary. Safe to replay verbatim: ingestion is idempotent. */
|
|
31
|
+
export declare class SinioServerError extends SinioApiError {
|
|
32
|
+
}
|
|
33
|
+
/** Raised before any network call, when the input cannot be a valid request. */
|
|
34
|
+
export declare class SinioValidationError extends SinioError {
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Base class for every error the SDK throws. */
|
|
2
|
+
export class SinioError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** The request never got a usable HTTP response (DNS, TCP, TLS, timeout). */
|
|
9
|
+
export class SinioConnectionError extends SinioError {
|
|
10
|
+
reason;
|
|
11
|
+
constructor(message, reason) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.reason = reason;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** A 4xx or 5xx response. */
|
|
17
|
+
export class SinioApiError extends SinioError {
|
|
18
|
+
status;
|
|
19
|
+
body;
|
|
20
|
+
/** Rejection code returned by the API, when the body carries one (a known `RejectionCode` or a newer one). */
|
|
21
|
+
code;
|
|
22
|
+
constructor(message, status, body, code = null) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.body = body;
|
|
26
|
+
this.code = code;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** 401 / 403 — invalid, revoked, or insufficiently scoped key. Never replay. */
|
|
30
|
+
export class SinioAuthError extends SinioApiError {
|
|
31
|
+
}
|
|
32
|
+
/** 400 / 404 / 413 — the request itself is wrong. Fix it before replaying. */
|
|
33
|
+
export class SinioRequestError extends SinioApiError {
|
|
34
|
+
}
|
|
35
|
+
/** 429 — quota exceeded. `retryAfterSeconds` mirrors the `Retry-After` header. */
|
|
36
|
+
export class SinioRateLimitError extends SinioApiError {
|
|
37
|
+
retryAfterSeconds;
|
|
38
|
+
constructor(message, status, body, retryAfterSeconds) {
|
|
39
|
+
super(message, status, body);
|
|
40
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** 5xx — temporary. Safe to replay verbatim: ingestion is idempotent. */
|
|
44
|
+
export class SinioServerError extends SinioApiError {
|
|
45
|
+
}
|
|
46
|
+
/** Raised before any network call, when the input cannot be a valid request. */
|
|
47
|
+
export class SinioValidationError extends SinioError {
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAEA,iDAAiD;AACjD,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,OAAO,oBAAqB,SAAQ,UAAU;IACzC,MAAM,CAAU;IAEzB,YAAY,OAAe,EAAE,MAAgB;QAC3C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,6BAA6B;AAC7B,MAAM,OAAO,aAAc,SAAQ,UAAU;IAClC,MAAM,CAAS;IACf,IAAI,CAAU;IACvB,8GAA8G;IACrG,IAAI,CAA4B;IAEzC,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,OAAkC,IAAI;QAChG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,gFAAgF;AAChF,MAAM,OAAO,cAAe,SAAQ,aAAa;CAAG;AAEpD,8EAA8E;AAC9E,MAAM,OAAO,iBAAkB,SAAQ,aAAa;CAAG;AAEvD,kFAAkF;AAClF,MAAM,OAAO,mBAAoB,SAAQ,aAAa;IAC3C,iBAAiB,CAAgB;IAE1C,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,iBAAgC;QAC1F,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;IAC7C,CAAC;CACF;AAED,yEAAyE;AACzE,MAAM,OAAO,gBAAiB,SAAQ,aAAa;CAAG;AAEtD,gFAAgF;AAChF,MAAM,OAAO,oBAAqB,SAAQ,UAAU;CAAG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
|
|
2
|
+
export interface TransportOptions {
|
|
3
|
+
readonly baseUrl: string;
|
|
4
|
+
readonly timeoutMs: number;
|
|
5
|
+
readonly maxRetries: number;
|
|
6
|
+
readonly retryBaseDelayMs: number;
|
|
7
|
+
readonly userAgent: string;
|
|
8
|
+
readonly fetch?: FetchLike | undefined;
|
|
9
|
+
/** Injected in tests so retries do not actually wait. */
|
|
10
|
+
readonly sleep?: ((ms: number) => Promise<void>) | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** `Retry-After` is either a delay in seconds or an HTTP date. Both are accepted. */
|
|
13
|
+
export declare function parseRetryAfter(header: string | null, now?: number): number | null;
|
|
14
|
+
export declare class Transport {
|
|
15
|
+
private readonly options;
|
|
16
|
+
private readonly fetchImpl;
|
|
17
|
+
private readonly sleep;
|
|
18
|
+
constructor(options: TransportOptions);
|
|
19
|
+
post(path: string, body: unknown, headers: Record<string, string>): Promise<unknown>;
|
|
20
|
+
/** Exponential backoff with full jitter, unless the server told us how long to wait. */
|
|
21
|
+
private backoffMs;
|
|
22
|
+
}
|
package/dist/esm/http.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { SinioAuthError, SinioConnectionError, SinioRateLimitError, SinioRequestError, SinioServerError, } from "./errors.js";
|
|
2
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
/** `Retry-After` is either a delay in seconds or an HTTP date. Both are accepted. */
|
|
4
|
+
export function parseRetryAfter(header, now = Date.now()) {
|
|
5
|
+
if (header === null) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const seconds = Number(header);
|
|
9
|
+
if (Number.isFinite(seconds)) {
|
|
10
|
+
return Math.max(0, seconds);
|
|
11
|
+
}
|
|
12
|
+
const date = Date.parse(header);
|
|
13
|
+
if (Number.isNaN(date)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return Math.max(0, (date - now) / 1000);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A 429 or a 5xx is retried, and so is a dropped connection: ingestion is
|
|
20
|
+
* idempotent, so a replay can only ever be counted as a duplicate. A 4xx that
|
|
21
|
+
* is not a 429 is the caller's fault and is raised immediately.
|
|
22
|
+
*/
|
|
23
|
+
function isRetriableStatus(status) {
|
|
24
|
+
return status === 429 || status >= 500;
|
|
25
|
+
}
|
|
26
|
+
export class Transport {
|
|
27
|
+
options;
|
|
28
|
+
fetchImpl;
|
|
29
|
+
sleep;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
const provided = options.fetch ?? globalThis.fetch;
|
|
33
|
+
if (provided === undefined) {
|
|
34
|
+
throw new Error("No global fetch available; pass `fetch` in the client options (Node < 18).");
|
|
35
|
+
}
|
|
36
|
+
this.fetchImpl = provided;
|
|
37
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
38
|
+
}
|
|
39
|
+
async post(path, body, headers) {
|
|
40
|
+
const url = `${this.options.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
41
|
+
const payload = JSON.stringify(body);
|
|
42
|
+
let attempt = 0;
|
|
43
|
+
for (;;) {
|
|
44
|
+
let response;
|
|
45
|
+
try {
|
|
46
|
+
response = await this.fetchImpl(url, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
"content-type": "application/json",
|
|
50
|
+
accept: "application/json",
|
|
51
|
+
"user-agent": this.options.userAgent,
|
|
52
|
+
...headers,
|
|
53
|
+
},
|
|
54
|
+
body: payload,
|
|
55
|
+
signal: AbortSignal.timeout(this.options.timeoutMs),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (cause) {
|
|
59
|
+
if (attempt >= this.options.maxRetries) {
|
|
60
|
+
throw new SinioConnectionError(`Request to ${url} failed: ${String(cause)}`, cause);
|
|
61
|
+
}
|
|
62
|
+
await this.sleep(this.backoffMs(attempt, null));
|
|
63
|
+
attempt += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (response.ok) {
|
|
67
|
+
return await readJson(response);
|
|
68
|
+
}
|
|
69
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
70
|
+
if (isRetriableStatus(response.status) && attempt < this.options.maxRetries) {
|
|
71
|
+
await this.sleep(this.backoffMs(attempt, retryAfter));
|
|
72
|
+
attempt += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
throw await toApiError(response, retryAfter);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Exponential backoff with full jitter, unless the server told us how long to wait. */
|
|
79
|
+
backoffMs(attempt, retryAfterSeconds) {
|
|
80
|
+
if (retryAfterSeconds !== null) {
|
|
81
|
+
return retryAfterSeconds * 1000;
|
|
82
|
+
}
|
|
83
|
+
const ceiling = Math.min(this.options.retryBaseDelayMs * 2 ** attempt, 30_000);
|
|
84
|
+
return Math.random() * ceiling;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function readJson(response) {
|
|
88
|
+
const text = await response.text();
|
|
89
|
+
if (text.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(text);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return text;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function toApiError(response, retryAfterSeconds) {
|
|
100
|
+
const body = await readJson(response);
|
|
101
|
+
const code = extractCode(body);
|
|
102
|
+
const message = `${response.status} ${response.statusText}: ${extractMessage(body) ?? "no message"}`;
|
|
103
|
+
if (response.status === 401 || response.status === 403) {
|
|
104
|
+
return new SinioAuthError(message, response.status, body, code);
|
|
105
|
+
}
|
|
106
|
+
if (response.status === 429) {
|
|
107
|
+
return new SinioRateLimitError(message, response.status, body, retryAfterSeconds);
|
|
108
|
+
}
|
|
109
|
+
if (response.status >= 500) {
|
|
110
|
+
return new SinioServerError(message, response.status, body, code);
|
|
111
|
+
}
|
|
112
|
+
return new SinioRequestError(message, response.status, body, code);
|
|
113
|
+
}
|
|
114
|
+
function extractCode(body) {
|
|
115
|
+
if (typeof body === "object" && body !== null && "code" in body && typeof body.code === "string") {
|
|
116
|
+
return body.code;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function extractMessage(body) {
|
|
121
|
+
if (typeof body === "string") {
|
|
122
|
+
return body;
|
|
123
|
+
}
|
|
124
|
+
if (typeof body !== "object" || body === null || !("message" in body)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const { message } = body;
|
|
128
|
+
// NestJS's ValidationPipe returns an array of messages, one per failed rule.
|
|
129
|
+
if (Array.isArray(message)) {
|
|
130
|
+
return message.join("; ");
|
|
131
|
+
}
|
|
132
|
+
return typeof message === "string" ? message : null;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SinioClient, SDK_VERSION, type SinioClientOptions } from "./client.ts";
|
|
2
|
+
export { SinioApiError, SinioAuthError, SinioConnectionError, SinioError, SinioRateLimitError, SinioRequestError, SinioServerError, SinioValidationError, } from "./errors.ts";
|
|
3
|
+
export { MAX_EVENT_COUNT, MAX_ITEMS_PER_CALL } from "./serialize.ts";
|
|
4
|
+
export { ASSIGNMENT_RELATIONS, ENTITY_KINDS, EVENT_ROLES, INGESTION_MODES, LOCATION_LEVELS, REJECTION_CODES, isKnownRejectionCode, } from "./vocabularies.ts";
|
|
5
|
+
export type * from "./types.ts";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SinioClient, SDK_VERSION } from "./client.js";
|
|
2
|
+
export { SinioApiError, SinioAuthError, SinioConnectionError, SinioError, SinioRateLimitError, SinioRequestError, SinioServerError, SinioValidationError, } from "./errors.js";
|
|
3
|
+
export { MAX_EVENT_COUNT, MAX_ITEMS_PER_CALL } from "./serialize.js";
|
|
4
|
+
export { ASSIGNMENT_RELATIONS, ENTITY_KINDS, EVENT_ROLES, INGESTION_MODES, LOCATION_LEVELS, REJECTION_CODES, isKnownRejectionCode, } from "./vocabularies.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAA2B,MAAM,aAAa,CAAC;AAChF,OAAO,EACL,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,eAAe,EACf,eAAe,EACf,oBAAoB,GACrB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AssignmentInput, AssignmentRelation, EntityInput, EntityKind, EventRole, IngestionMode, JsonObject, LocationInput, LocationLevel, SinioEvent, Timestamp } from "./types.ts";
|
|
2
|
+
/** The API caps every collection at 500 items per call. */
|
|
3
|
+
export declare const MAX_ITEMS_PER_CALL = 500;
|
|
4
|
+
/** The most units one event may stand for. */
|
|
5
|
+
export declare const MAX_EVENT_COUNT = 1000000;
|
|
6
|
+
export type WireRoles = {
|
|
7
|
+
readonly [role in EventRole]?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
export interface WireEvent {
|
|
10
|
+
readonly source_event_id: string;
|
|
11
|
+
readonly source_version?: string | undefined;
|
|
12
|
+
readonly project?: string | undefined;
|
|
13
|
+
readonly type: string;
|
|
14
|
+
readonly label?: string | undefined;
|
|
15
|
+
readonly occurred_at: string;
|
|
16
|
+
readonly roles?: WireRoles | undefined;
|
|
17
|
+
readonly payload?: JsonObject | undefined;
|
|
18
|
+
readonly count?: number | undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface WireEntity {
|
|
21
|
+
readonly external_id: string;
|
|
22
|
+
readonly kind: EntityKind;
|
|
23
|
+
readonly subkind?: string | undefined;
|
|
24
|
+
readonly label?: string | undefined;
|
|
25
|
+
readonly location?: string | undefined;
|
|
26
|
+
readonly attributes?: JsonObject | undefined;
|
|
27
|
+
}
|
|
28
|
+
export interface WireLocation {
|
|
29
|
+
readonly external_id: string;
|
|
30
|
+
readonly kind: LocationLevel;
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly parent?: string | undefined;
|
|
33
|
+
}
|
|
34
|
+
export interface WireAssignment {
|
|
35
|
+
readonly credential: string;
|
|
36
|
+
readonly subject: string;
|
|
37
|
+
readonly relation?: AssignmentRelation | undefined;
|
|
38
|
+
readonly valid_from: string;
|
|
39
|
+
readonly valid_to?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
export declare function toIso(value: Timestamp, field: string): string;
|
|
42
|
+
export declare function chunk<T>(items: readonly T[], size: number): T[][];
|
|
43
|
+
export declare function requireOneOf<const T extends string>(value: unknown, values: readonly T[], field: string): T;
|
|
44
|
+
export declare function requireList(value: unknown, field: string): readonly unknown[];
|
|
45
|
+
export declare function toWireMode(mode: unknown): IngestionMode;
|
|
46
|
+
export declare function toWireEvent(event: SinioEvent, index: number): WireEvent;
|
|
47
|
+
export declare function toWireEntity(entity: EntityInput, index: number): WireEntity;
|
|
48
|
+
export declare function toWireLocation(location: LocationInput, index: number): WireLocation;
|
|
49
|
+
export declare function toWireAssignment(assignment: AssignmentInput, index: number): WireAssignment;
|