totalum-sdk 0.1.0-dev.4 → 0.1.0-dev.5
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/README.md +7 -3
- package/dist/_types/billing.d-Cj9gNbAW.d.ts +38 -0
- package/dist/_types/coerce.d-C1KWvgPp.d.ts +6 -0
- package/dist/_types/errors.d-ZrZKjL3U.d.ts +2991 -0
- package/dist/_types/integrations.d-Cii20Vl4.d.ts +793 -0
- package/dist/_types/ops.d-BIgDkhpR.d.ts +117 -0
- package/dist/ai/index.d.ts +424 -0
- package/dist/ai/index.js +185 -0
- package/dist/analytics/index.d.ts +35 -0
- package/dist/analytics/index.js +15 -0
- package/dist/browser/index.d.ts +1442 -0
- package/dist/browser/index.js +168 -0
- package/dist/cron/index.d.ts +180 -0
- package/dist/cron/index.js +61 -0
- package/dist/d1/errors.js +5 -13
- package/dist/d1/index.d.ts +90 -5
- package/dist/d1/index.js +6 -10
- package/dist/email/index.d.ts +31 -0
- package/dist/email/index.js +30 -0
- package/dist/errors.js +35 -0
- package/dist/files/index.d.ts +119 -0
- package/dist/files/index.js +63 -0
- package/dist/http.js +85 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +68 -0
- package/dist/logs/index.d.ts +51 -0
- package/dist/logs/index.js +18 -0
- package/dist/payments/index.d.ts +47 -0
- package/dist/payments/index.js +20 -0
- package/dist/pdf/index.d.ts +42 -0
- package/dist/pdf/index.js +17 -0
- package/dist/realtime/index.d.ts +68 -0
- package/dist/realtime/index.js +20 -0
- package/dist/scan/index.d.ts +46 -0
- package/dist/scan/index.js +12 -0
- package/dist/seo/index.d.ts +23 -0
- package/dist/seo/index.js +12 -0
- package/dist/speech/index.d.ts +41 -0
- package/dist/speech/index.js +22 -0
- package/dist/web/index.d.ts +170 -0
- package/dist/web/index.js +31 -0
- package/dist/webhooks/index.d.ts +48 -0
- package/dist/webhooks/index.js +61 -0
- package/package.json +77 -5
- package/totalum-sdk.md +1040 -0
- package/dist/d1/errors.d.ts +0 -27
- package/dist/d1/https.d.ts +0 -10
- package/dist/d1/lazy.d.ts +0 -7
- package/dist/d1/libsql.d.ts +0 -23
- package/dist/d1/sql.d.ts +0 -9
- package/dist/d1/types.d.ts +0 -72
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Z as ZodObject, d as ZodString, c as ZodOptional, h as ZodRecord, g as ZodUnknown, m as $strip, j as ZodBoolean, l as ZodInt, o as output, t as ZodISODateTime, T as TotalumClientOptions } from '../_types/errors.d-ZrZKjL3U.js';
|
|
2
|
+
export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
3
|
+
|
|
4
|
+
/** `POST /v1/realtime/tickets` (scope `realtime`). `ttl` is the join window in seconds, not the session length. */
|
|
5
|
+
declare const RealtimeTicketInput: ZodObject<{
|
|
6
|
+
room: ZodString;
|
|
7
|
+
identity: ZodObject<{
|
|
8
|
+
id: ZodString;
|
|
9
|
+
name: ZodOptional<ZodString>;
|
|
10
|
+
metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;
|
|
11
|
+
}, $strip>;
|
|
12
|
+
permissions: ZodOptional<ZodObject<{
|
|
13
|
+
subscribe: ZodOptional<ZodBoolean>;
|
|
14
|
+
publish: ZodOptional<ZodBoolean>;
|
|
15
|
+
}, $strip>>;
|
|
16
|
+
ttl: ZodOptional<ZodInt>;
|
|
17
|
+
}, $strip>;
|
|
18
|
+
type RealtimeTicketInput = output<typeof RealtimeTicketInput>;
|
|
19
|
+
declare const RealtimeTicketOutput: ZodObject<{
|
|
20
|
+
ticket: ZodString;
|
|
21
|
+
url: ZodString;
|
|
22
|
+
room: ZodString;
|
|
23
|
+
identity: ZodObject<{
|
|
24
|
+
id: ZodString;
|
|
25
|
+
name: ZodOptional<ZodString>;
|
|
26
|
+
metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;
|
|
27
|
+
}, $strip>;
|
|
28
|
+
permissions: ZodObject<{
|
|
29
|
+
subscribe: ZodBoolean;
|
|
30
|
+
publish: ZodBoolean;
|
|
31
|
+
}, $strip>;
|
|
32
|
+
expiresAt: ZodISODateTime;
|
|
33
|
+
}, $strip>;
|
|
34
|
+
type RealtimeTicketOutput = output<typeof RealtimeTicketOutput>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* `totalum.realtime` server side (plan 05 §6.14.1) over SDK-API `/v1/realtime/*` (scope `realtime`). The app's own
|
|
38
|
+
* server mints a short-lived ticket after its own auth check and hands `{ticket, url}` to the page; the page opens the
|
|
39
|
+
* `wss://` `url` and never holds a key. A room costs awake room-minutes × 4, never connections or messages.
|
|
40
|
+
*/
|
|
41
|
+
declare function totalumRealtime(options?: TotalumClientOptions): {
|
|
42
|
+
/**
|
|
43
|
+
* A ticket for one room, one identity and one permission set (default subscribe only), valid to join for `ttl`
|
|
44
|
+
* seconds (5–300, default 60).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const { url } = await realtime.ticket({ room: `chat:${orgId}`, identity: { id: user.id }, permissions: { publish: true } });
|
|
48
|
+
*/
|
|
49
|
+
ticket: (input: RealtimeTicketInput) => Promise<{
|
|
50
|
+
ticket: string;
|
|
51
|
+
url: string;
|
|
52
|
+
room: string;
|
|
53
|
+
identity: {
|
|
54
|
+
id: string;
|
|
55
|
+
name?: string | undefined;
|
|
56
|
+
metadata?: Record<string, unknown> | undefined;
|
|
57
|
+
};
|
|
58
|
+
permissions: {
|
|
59
|
+
subscribe: boolean;
|
|
60
|
+
publish: boolean;
|
|
61
|
+
};
|
|
62
|
+
expiresAt: string;
|
|
63
|
+
}>;
|
|
64
|
+
};
|
|
65
|
+
type TotalumRealtime = ReturnType<typeof totalumRealtime>;
|
|
66
|
+
|
|
67
|
+
export { RealtimeTicketInput, RealtimeTicketOutput, TotalumClientOptions, totalumRealtime };
|
|
68
|
+
export type { TotalumRealtime };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsonClient } from '../http.js';
|
|
2
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* `totalum.realtime` server side (plan 05 §6.14.1) over SDK-API `/v1/realtime/*` (scope `realtime`). The app's own
|
|
5
|
+
* server mints a short-lived ticket after its own auth check and hands `{ticket, url}` to the page; the page opens the
|
|
6
|
+
* `wss://` `url` and never holds a key. A room costs awake room-minutes × 4, never connections or messages.
|
|
7
|
+
*/
|
|
8
|
+
export function totalumRealtime(options = {}) {
|
|
9
|
+
const call = jsonClient(options);
|
|
10
|
+
return {
|
|
11
|
+
/**
|
|
12
|
+
* A ticket for one room, one identity and one permission set (default subscribe only), valid to join for `ttl`
|
|
13
|
+
* seconds (5–300, default 60).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const { url } = await realtime.ticket({ room: `chat:${orgId}`, identity: { id: user.id }, permissions: { publish: true } });
|
|
17
|
+
*/
|
|
18
|
+
ticket: (input) => call('POST', '/v1/realtime/tickets', input),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { S as ScanDocumentInput, s as ScanOcrInput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
2
|
+
export { t as ScanOcrOutput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
3
|
+
import { T as TotalumClientOptions } from '../_types/errors.d-ZrZKjL3U.js';
|
|
4
|
+
export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
5
|
+
import '../_types/coerce.d-C1KWvgPp.js';
|
|
6
|
+
|
|
7
|
+
/** The v1 scan result: the extracted fields (one set per page with `processEveryPdfPageAsDifferentScan`) + `metadata`. */
|
|
8
|
+
interface ScanDocumentResult {
|
|
9
|
+
data: Record<string, unknown> | Record<string, unknown>[];
|
|
10
|
+
metadata: {
|
|
11
|
+
usageUnits: number;
|
|
12
|
+
exactUsageUnits: number;
|
|
13
|
+
pages: number;
|
|
14
|
+
provider: string;
|
|
15
|
+
credits: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** `totalum.scan` (plan 05 §6.7) over `/v1/scan/*` (scope `scan`): a vision model through Totalum's AI gateway. */
|
|
19
|
+
declare function totalumScan(options?: TotalumClientOptions): {
|
|
20
|
+
/** Extracts the fields described by `properties` from a document or image (one set per page on request). */
|
|
21
|
+
document: (input: ScanDocumentInput) => Promise<ScanDocumentResult>;
|
|
22
|
+
/** The text of a document or image, per page as Markdown. */
|
|
23
|
+
ocr: (input: ScanOcrInput) => Promise<{
|
|
24
|
+
text: string;
|
|
25
|
+
pages: {
|
|
26
|
+
markdown: string;
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
words?: {
|
|
30
|
+
text: string;
|
|
31
|
+
bbox: number[];
|
|
32
|
+
}[] | undefined;
|
|
33
|
+
}[];
|
|
34
|
+
fullDetails?: unknown;
|
|
35
|
+
} & {
|
|
36
|
+
usage: {
|
|
37
|
+
vendor?: string | undefined;
|
|
38
|
+
units?: number | undefined;
|
|
39
|
+
credits: number;
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
type TotalumScan = ReturnType<typeof totalumScan>;
|
|
44
|
+
|
|
45
|
+
export { ScanDocumentInput, ScanOcrInput, TotalumClientOptions, totalumScan };
|
|
46
|
+
export type { ScanDocumentResult, TotalumScan };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { envelopeClient, withUsage } from '../http.js';
|
|
2
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
3
|
+
/** `totalum.scan` (plan 05 §6.7) over `/v1/scan/*` (scope `scan`): a vision model through Totalum's AI gateway. */
|
|
4
|
+
export function totalumScan(options = {}) {
|
|
5
|
+
const call = envelopeClient(options);
|
|
6
|
+
return {
|
|
7
|
+
/** Extracts the fields described by `properties` from a document or image (one set per page on request). */
|
|
8
|
+
document: async (input) => (await call('POST', '/v1/scan/document', input)),
|
|
9
|
+
/** The text of a document or image, per page as Markdown. */
|
|
10
|
+
ocr: async (input) => withUsage(await call('POST', '/v1/scan/ocr', input)),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { w as SeoIndexNowKey, x as SeoNotifyOutput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
2
|
+
import { T as TotalumClientOptions } from '../_types/errors.d-ZrZKjL3U.js';
|
|
3
|
+
export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
4
|
+
import '../_types/coerce.d-C1KWvgPp.js';
|
|
5
|
+
|
|
6
|
+
/** `totalum.seo` (plan 05 §6.10) over SDK-API `/v1/seo/*` (scope `seo`): IndexNow for verified custom domains, free. */
|
|
7
|
+
declare function totalumSeo(options?: TotalumClientOptions): {
|
|
8
|
+
/** Tells search engines (IndexNow) that these URLs of the project's custom domains changed. */
|
|
9
|
+
notifyChanged: (urls: string[]) => Promise<{
|
|
10
|
+
accepted: number;
|
|
11
|
+
indexNow: "error" | "sent" | "skipped_no_custom_domain";
|
|
12
|
+
sitemapsPinged: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
/** The key the template serves at `/<key>.txt`. */
|
|
15
|
+
indexNowKey: () => Promise<{
|
|
16
|
+
key: string;
|
|
17
|
+
keyLocation: string;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
type TotalumSeo = ReturnType<typeof totalumSeo>;
|
|
21
|
+
|
|
22
|
+
export { TotalumClientOptions, totalumSeo };
|
|
23
|
+
export type { TotalumSeo };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsonClient } from '../http.js';
|
|
2
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
3
|
+
/** `totalum.seo` (plan 05 §6.10) over SDK-API `/v1/seo/*` (scope `seo`): IndexNow for verified custom domains, free. */
|
|
4
|
+
export function totalumSeo(options = {}) {
|
|
5
|
+
const call = jsonClient(options);
|
|
6
|
+
return {
|
|
7
|
+
/** Tells search engines (IndexNow) that these URLs of the project's custom domains changed. */
|
|
8
|
+
notifyChanged: (urls) => call('POST', '/v1/seo/notify-changed', { urls }),
|
|
9
|
+
/** The key the template serves at `/<key>.txt`. */
|
|
10
|
+
indexNowKey: () => call('GET', '/v1/seo/indexnow-key'),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { T as TranscribeInput, y as SpeakInput, z as StoredMedia } from '../_types/integrations.d-Cii20Vl4.js';
|
|
2
|
+
export { A as TranscribeOutput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
3
|
+
import { T as TotalumClientOptions } from '../_types/errors.d-ZrZKjL3U.js';
|
|
4
|
+
export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
5
|
+
import '../_types/coerce.d-C1KWvgPp.js';
|
|
6
|
+
|
|
7
|
+
/** `totalum.speech` (plan 05 §6.8) over `/v1/speech/*` (scope `speech`): OpenRouter speech through Totalum. */
|
|
8
|
+
declare function totalumSpeech(options?: TotalumClientOptions): {
|
|
9
|
+
/** Speech to text from `audioBase64` or a `url`; optional language, word timestamps and speakers. */
|
|
10
|
+
transcribe: (input: TranscribeInput) => Promise<{
|
|
11
|
+
text: string;
|
|
12
|
+
durationSec: number;
|
|
13
|
+
usage: {
|
|
14
|
+
credits: number;
|
|
15
|
+
vendor?: string | undefined;
|
|
16
|
+
units?: number | undefined;
|
|
17
|
+
vendorCostUsd?: number | undefined;
|
|
18
|
+
};
|
|
19
|
+
language?: string | undefined;
|
|
20
|
+
segments?: {
|
|
21
|
+
start: number;
|
|
22
|
+
end: number;
|
|
23
|
+
text: string;
|
|
24
|
+
}[] | undefined;
|
|
25
|
+
words?: {
|
|
26
|
+
word: string;
|
|
27
|
+
start: number;
|
|
28
|
+
end: number;
|
|
29
|
+
}[] | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
/** The audio bytes, or with `store: true` the stored file `{key, url, usage}`; `credits` is the call's hold. */
|
|
32
|
+
synthesize: (input: SpeakInput) => Promise<StoredMedia | {
|
|
33
|
+
audio: ArrayBuffer;
|
|
34
|
+
contentType: string;
|
|
35
|
+
credits: number;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
type TotalumSpeech = ReturnType<typeof totalumSpeech>;
|
|
39
|
+
|
|
40
|
+
export { SpeakInput, StoredMedia, TotalumClientOptions, TranscribeInput, totalumSpeech };
|
|
41
|
+
export type { TotalumSpeech };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsonClient, rawClient } from '../http.js';
|
|
2
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
3
|
+
/** `totalum.speech` (plan 05 §6.8) over `/v1/speech/*` (scope `speech`): OpenRouter speech through Totalum. */
|
|
4
|
+
export function totalumSpeech(options = {}) {
|
|
5
|
+
const call = jsonClient(options);
|
|
6
|
+
const raw = rawClient(options);
|
|
7
|
+
return {
|
|
8
|
+
/** Speech to text from `audioBase64` or a `url`; optional language, word timestamps and speakers. */
|
|
9
|
+
transcribe: (input) => call('POST', '/v1/speech/transcribe', input),
|
|
10
|
+
/** The audio bytes, or with `store: true` the stored file `{key, url, usage}`; `credits` is the call's hold. */
|
|
11
|
+
synthesize: async (input) => {
|
|
12
|
+
if (input.store)
|
|
13
|
+
return call('POST', '/v1/speech/speak', input);
|
|
14
|
+
const res = await raw('/v1/speech/speak', input);
|
|
15
|
+
return {
|
|
16
|
+
audio: await res.arrayBuffer(),
|
|
17
|
+
contentType: res.headers.get('content-type') ?? 'audio/mpeg',
|
|
18
|
+
credits: Number(res.headers.get('x-totalum-credits') ?? 0),
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { G as WebScrapeInput, H as WebSearchInput, I as WebMapInput, K as WebCrawlInput, u as ScreenshotInput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
2
|
+
export { J as JobCreated, v as ScreenshotOutput, W as WebJob, B as WebScrapeOutput, D as WebSearchOutput } from '../_types/integrations.d-Cii20Vl4.js';
|
|
3
|
+
import { T as TotalumClientOptions } from '../_types/errors.d-ZrZKjL3U.js';
|
|
4
|
+
export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
5
|
+
import '../_types/coerce.d-C1KWvgPp.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `totalum.web` (plan 05 §6.2) over SDK-API `/v1/web/*` (scope `web`): Firecrawl scrape/search/map/crawl and Browser
|
|
9
|
+
* Run screenshots, charged provider cost × 28 — every result carries `usage.credits`.
|
|
10
|
+
*/
|
|
11
|
+
declare function totalumWeb(options?: TotalumClientOptions): {
|
|
12
|
+
/**
|
|
13
|
+
* One page as `markdown`, `html`, `links`, … (Firecrawl).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const { markdown } = await web.scrape('https://example.com', { formats: ['markdown'] });
|
|
17
|
+
*/
|
|
18
|
+
scrape: (url: string, opts?: Omit<WebScrapeInput, "url">) => Promise<{
|
|
19
|
+
url: string;
|
|
20
|
+
statusCode: number;
|
|
21
|
+
metadata: {
|
|
22
|
+
sourceURL: string;
|
|
23
|
+
title?: string | undefined;
|
|
24
|
+
description?: string | undefined;
|
|
25
|
+
language?: string | undefined;
|
|
26
|
+
};
|
|
27
|
+
usage: {
|
|
28
|
+
credits: number;
|
|
29
|
+
vendor?: string | undefined;
|
|
30
|
+
units?: number | undefined;
|
|
31
|
+
vendorCostUsd?: number | undefined;
|
|
32
|
+
};
|
|
33
|
+
markdown?: string | undefined;
|
|
34
|
+
html?: string | undefined;
|
|
35
|
+
rawHtml?: string | undefined;
|
|
36
|
+
links?: string[] | undefined;
|
|
37
|
+
screenshotUrl?: string | undefined;
|
|
38
|
+
json?: unknown;
|
|
39
|
+
summary?: string | undefined;
|
|
40
|
+
contentUrl?: string | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
/** A web search; with `scrapeOptions` each result also carries its page content. */
|
|
43
|
+
search: (query: string, opts?: Omit<WebSearchInput, "query">) => Promise<{
|
|
44
|
+
web: {
|
|
45
|
+
title: string;
|
|
46
|
+
url: string;
|
|
47
|
+
position: number;
|
|
48
|
+
snippet?: string | undefined;
|
|
49
|
+
}[];
|
|
50
|
+
news?: {
|
|
51
|
+
title: string;
|
|
52
|
+
url: string;
|
|
53
|
+
position: number;
|
|
54
|
+
snippet?: string | undefined;
|
|
55
|
+
}[] | undefined;
|
|
56
|
+
images?: Record<string, unknown>[] | undefined;
|
|
57
|
+
scraped?: {
|
|
58
|
+
url: string;
|
|
59
|
+
statusCode: number;
|
|
60
|
+
metadata: {
|
|
61
|
+
sourceURL: string;
|
|
62
|
+
title?: string | undefined;
|
|
63
|
+
description?: string | undefined;
|
|
64
|
+
language?: string | undefined;
|
|
65
|
+
};
|
|
66
|
+
markdown?: string | undefined;
|
|
67
|
+
html?: string | undefined;
|
|
68
|
+
rawHtml?: string | undefined;
|
|
69
|
+
links?: string[] | undefined;
|
|
70
|
+
screenshotUrl?: string | undefined;
|
|
71
|
+
json?: unknown;
|
|
72
|
+
summary?: string | undefined;
|
|
73
|
+
contentUrl?: string | undefined;
|
|
74
|
+
}[] | undefined;
|
|
75
|
+
} & {
|
|
76
|
+
usage: {
|
|
77
|
+
vendor?: string | undefined;
|
|
78
|
+
units?: number | undefined;
|
|
79
|
+
credits: number;
|
|
80
|
+
};
|
|
81
|
+
}>;
|
|
82
|
+
/** The URLs of a site (sitemap and links), without fetching the pages. */
|
|
83
|
+
map: (url: string, opts?: Omit<WebMapInput, "url">) => Promise<{
|
|
84
|
+
links: string[];
|
|
85
|
+
} & {
|
|
86
|
+
usage: {
|
|
87
|
+
vendor?: string | undefined;
|
|
88
|
+
units?: number | undefined;
|
|
89
|
+
credits: number;
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
/** `202 {jobId}`: poll `crawlStatus` until the job leaves `running`; that read settles its charge. */
|
|
93
|
+
crawl: (url: string, opts?: Omit<WebCrawlInput, "url">) => Promise<{
|
|
94
|
+
jobId: string;
|
|
95
|
+
estimatedCredits?: number | undefined;
|
|
96
|
+
}>;
|
|
97
|
+
/** The crawl job: `status`, pages so far and their content. */
|
|
98
|
+
crawlStatus: (jobId: string) => Promise<{
|
|
99
|
+
jobId: string;
|
|
100
|
+
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "expired";
|
|
101
|
+
total: number;
|
|
102
|
+
completed: number;
|
|
103
|
+
creditsUsed: number;
|
|
104
|
+
data: {
|
|
105
|
+
url: string;
|
|
106
|
+
statusCode: number;
|
|
107
|
+
metadata: {
|
|
108
|
+
sourceURL: string;
|
|
109
|
+
title?: string | undefined;
|
|
110
|
+
description?: string | undefined;
|
|
111
|
+
language?: string | undefined;
|
|
112
|
+
};
|
|
113
|
+
markdown?: string | undefined;
|
|
114
|
+
html?: string | undefined;
|
|
115
|
+
rawHtml?: string | undefined;
|
|
116
|
+
links?: string[] | undefined;
|
|
117
|
+
screenshotUrl?: string | undefined;
|
|
118
|
+
json?: unknown;
|
|
119
|
+
summary?: string | undefined;
|
|
120
|
+
contentUrl?: string | undefined;
|
|
121
|
+
}[];
|
|
122
|
+
next?: string | undefined;
|
|
123
|
+
resultUrl?: string | undefined;
|
|
124
|
+
}>;
|
|
125
|
+
/** Stops a running crawl; pages already crawled are charged. */
|
|
126
|
+
cancelCrawl: (jobId: string) => Promise<{
|
|
127
|
+
jobId: string;
|
|
128
|
+
status: "queued" | "running" | "completed" | "failed" | "cancelled" | "expired";
|
|
129
|
+
total: number;
|
|
130
|
+
completed: number;
|
|
131
|
+
creditsUsed: number;
|
|
132
|
+
data: {
|
|
133
|
+
url: string;
|
|
134
|
+
statusCode: number;
|
|
135
|
+
metadata: {
|
|
136
|
+
sourceURL: string;
|
|
137
|
+
title?: string | undefined;
|
|
138
|
+
description?: string | undefined;
|
|
139
|
+
language?: string | undefined;
|
|
140
|
+
};
|
|
141
|
+
markdown?: string | undefined;
|
|
142
|
+
html?: string | undefined;
|
|
143
|
+
rawHtml?: string | undefined;
|
|
144
|
+
links?: string[] | undefined;
|
|
145
|
+
screenshotUrl?: string | undefined;
|
|
146
|
+
json?: unknown;
|
|
147
|
+
summary?: string | undefined;
|
|
148
|
+
contentUrl?: string | undefined;
|
|
149
|
+
}[];
|
|
150
|
+
next?: string | undefined;
|
|
151
|
+
resultUrl?: string | undefined;
|
|
152
|
+
}>;
|
|
153
|
+
/** A screenshot of a `url` or of inline `html`, stored as a project file (`key`, `url`). */
|
|
154
|
+
screenshot: (input: ScreenshotInput) => Promise<{
|
|
155
|
+
key: string;
|
|
156
|
+
url: string;
|
|
157
|
+
format: "png" | "jpeg" | "webp";
|
|
158
|
+
base64?: string | undefined;
|
|
159
|
+
} & {
|
|
160
|
+
usage: {
|
|
161
|
+
vendor?: string | undefined;
|
|
162
|
+
units?: number | undefined;
|
|
163
|
+
credits: number;
|
|
164
|
+
};
|
|
165
|
+
}>;
|
|
166
|
+
};
|
|
167
|
+
type TotalumWeb = ReturnType<typeof totalumWeb>;
|
|
168
|
+
|
|
169
|
+
export { ScreenshotInput, TotalumClientOptions, totalumWeb };
|
|
170
|
+
export type { TotalumWeb };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { envelopeClient, withUsage } from '../http.js';
|
|
2
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* `totalum.web` (plan 05 §6.2) over SDK-API `/v1/web/*` (scope `web`): Firecrawl scrape/search/map/crawl and Browser
|
|
5
|
+
* Run screenshots, charged provider cost × 28 — every result carries `usage.credits`.
|
|
6
|
+
*/
|
|
7
|
+
export function totalumWeb(options = {}) {
|
|
8
|
+
const call = envelopeClient(options);
|
|
9
|
+
const job = (jobId) => `/v1/web/jobs/${encodeURIComponent(jobId)}`;
|
|
10
|
+
return {
|
|
11
|
+
/**
|
|
12
|
+
* One page as `markdown`, `html`, `links`, … (Firecrawl).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const { markdown } = await web.scrape('https://example.com', { formats: ['markdown'] });
|
|
16
|
+
*/
|
|
17
|
+
scrape: async (url, opts = {}) => (await call('POST', '/v1/web/scrape', { ...opts, url })).data,
|
|
18
|
+
/** A web search; with `scrapeOptions` each result also carries its page content. */
|
|
19
|
+
search: async (query, opts = {}) => withUsage(await call('POST', '/v1/web/search', { ...opts, query })),
|
|
20
|
+
/** The URLs of a site (sitemap and links), without fetching the pages. */
|
|
21
|
+
map: async (url, opts = {}) => withUsage(await call('POST', '/v1/web/map', { ...opts, url })),
|
|
22
|
+
/** `202 {jobId}`: poll `crawlStatus` until the job leaves `running`; that read settles its charge. */
|
|
23
|
+
crawl: async (url, opts = {}) => (await call('POST', '/v1/web/crawl', { ...opts, url })).data,
|
|
24
|
+
/** The crawl job: `status`, pages so far and their content. */
|
|
25
|
+
crawlStatus: async (jobId) => (await call('GET', job(jobId))).data,
|
|
26
|
+
/** Stops a running crawl; pages already crawled are charged. */
|
|
27
|
+
cancelCrawl: async (jobId) => (await call('DELETE', job(jobId))).data,
|
|
28
|
+
/** A screenshot of a `url` or of inline `html`, stored as a project file (`key`, `url`). */
|
|
29
|
+
screenshot: async (input) => withUsage(await call('POST', '/v1/web/screenshot', input)),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { a as WebhookEvent } from '../_types/billing.d-Cj9gNbAW.js';
|
|
2
|
+
export { W as WebhookConfig, b as WebhookSecretOutput } from '../_types/billing.d-Cj9gNbAW.js';
|
|
3
|
+
import { T as TotalumClientOptions, a as TotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
4
|
+
export { i as isTotalumError } from '../_types/errors.d-ZrZKjL3U.js';
|
|
5
|
+
|
|
6
|
+
/** A webhook refusal (plan 05 §7.2): the codes never travel, the app answers 400. */
|
|
7
|
+
declare class TotalumWebhookError extends TotalumError {
|
|
8
|
+
name: string;
|
|
9
|
+
readonly errorCode: 'WEBHOOK_SECRET_MISSING' | 'WEBHOOK_SIGNATURE_INVALID' | 'WEBHOOK_TIMESTAMP_EXPIRED';
|
|
10
|
+
constructor(errorCode: TotalumWebhookError['errorCode'], message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `totalum.webhooks.verify` (plan 04 §5.13): checks `Totalum-Signature: t=…,v1=…` — HMAC-SHA256 keyed with the
|
|
14
|
+
* endpoint's secret string over `${t}.${rawBody}` — within ±300 s, and returns the parsed event. Pass the body exactly
|
|
15
|
+
* as received (`await request.text()`). `secret` defaults to `TOTALUM_WEBHOOK_SECRET`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const event = await verifyWebhook(await request.text(), request.headers);
|
|
19
|
+
*/
|
|
20
|
+
declare function verifyWebhook(rawBody: string, headers: Headers | Record<string, string | undefined>, secret?: string | undefined, now?: number): Promise<WebhookEvent>;
|
|
21
|
+
/**
|
|
22
|
+
* `totalum.webhooks` (plan 04 §5.13) over SDK-API `/v1/webhooks` (a key with scope `*`): where the project receives its
|
|
23
|
+
* signed events (payments, e-mail, browser programs, `project.credits.exhausted`) and the secret they are signed with.
|
|
24
|
+
*/
|
|
25
|
+
declare function totalumWebhooks(options?: TotalumClientOptions): {
|
|
26
|
+
/** Same as `verifyWebhook`: checks `Totalum-Signature` and returns the parsed event. */
|
|
27
|
+
verify: typeof verifyWebhook;
|
|
28
|
+
/** The endpoint and the first characters of its secret (the secret itself is shown only by `rotateSecret`). */
|
|
29
|
+
get: () => Promise<{
|
|
30
|
+
url: string | null;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
secretPrefix: string | null;
|
|
33
|
+
}>;
|
|
34
|
+
/** Sets the `https://` URL the project's events are delivered to. */
|
|
35
|
+
setUrl: (url: string) => Promise<{
|
|
36
|
+
url: string | null;
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
secretPrefix: string | null;
|
|
39
|
+
}>;
|
|
40
|
+
/** A new signing secret, shown once; the old one keeps co-signing for 24 h. Store it as `TOTALUM_WEBHOOK_SECRET`. */
|
|
41
|
+
rotateSecret: () => Promise<{
|
|
42
|
+
secret: string;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
type TotalumWebhooks = ReturnType<typeof totalumWebhooks>;
|
|
46
|
+
|
|
47
|
+
export { TotalumClientOptions, TotalumError, TotalumWebhookError, WebhookEvent, totalumWebhooks, verifyWebhook };
|
|
48
|
+
export type { TotalumWebhooks };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TotalumError } from '../errors.js';
|
|
2
|
+
import { jsonClient, readEnv } from '../http.js';
|
|
3
|
+
export { TotalumError, isTotalumError } from '../errors.js';
|
|
4
|
+
/** Literals type-locked to contracts, so the published package imports nothing at runtime (plan 05 §1). */
|
|
5
|
+
const SIGNATURE_HEADER = 'Totalum-Signature';
|
|
6
|
+
const TOLERANCE_SECONDS = 300;
|
|
7
|
+
/** A webhook refusal (plan 05 §7.2): the codes never travel, the app answers 400. */
|
|
8
|
+
export class TotalumWebhookError extends TotalumError {
|
|
9
|
+
name = 'TotalumWebhookError';
|
|
10
|
+
constructor(errorCode, message) {
|
|
11
|
+
super(errorCode, message, errorCode === 'WEBHOOK_SECRET_MISSING' ? 500 : 401);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const enc = new TextEncoder();
|
|
15
|
+
/**
|
|
16
|
+
* `totalum.webhooks.verify` (plan 04 §5.13): checks `Totalum-Signature: t=…,v1=…` — HMAC-SHA256 keyed with the
|
|
17
|
+
* endpoint's secret string over `${t}.${rawBody}` — within ±300 s, and returns the parsed event. Pass the body exactly
|
|
18
|
+
* as received (`await request.text()`). `secret` defaults to `TOTALUM_WEBHOOK_SECRET`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const event = await verifyWebhook(await request.text(), request.headers);
|
|
22
|
+
*/
|
|
23
|
+
export async function verifyWebhook(rawBody, headers, secret = readEnv('TOTALUM_WEBHOOK_SECRET'), now = Date.now()) {
|
|
24
|
+
if (!secret)
|
|
25
|
+
throw new TotalumWebhookError('WEBHOOK_SECRET_MISSING', 'Set TOTALUM_WEBHOOK_SECRET or pass the secret.');
|
|
26
|
+
const name = SIGNATURE_HEADER;
|
|
27
|
+
const header = headers instanceof Headers ? headers.get(name) : (headers[name] ?? headers[name.toLowerCase()] ?? null);
|
|
28
|
+
const parts = (header ?? '').split(',').map((p) => p.split('=', 2));
|
|
29
|
+
const t = Number(parts.find(([k]) => k === 't')?.[1]);
|
|
30
|
+
if (!Number.isInteger(t))
|
|
31
|
+
throw new TotalumWebhookError('WEBHOOK_SIGNATURE_INVALID', 'Missing Totalum-Signature');
|
|
32
|
+
if (Math.abs(now / 1000 - t) > TOLERANCE_SECONDS)
|
|
33
|
+
throw new TotalumWebhookError('WEBHOOK_TIMESTAMP_EXPIRED', 'Signature timestamp outside ±300 s');
|
|
34
|
+
const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
|
|
35
|
+
const payload = enc.encode(`${String(t)}.${rawBody}`);
|
|
36
|
+
for (const [k, v] of parts) {
|
|
37
|
+
if (k !== 'v1' || !v || !/^[0-9a-f]{64}$/.test(v))
|
|
38
|
+
continue;
|
|
39
|
+
const sig = new Uint8Array(v.match(/../g)?.map((h) => parseInt(h, 16)) ?? []);
|
|
40
|
+
if (await crypto.subtle.verify('HMAC', key, sig, payload))
|
|
41
|
+
return JSON.parse(rawBody);
|
|
42
|
+
}
|
|
43
|
+
throw new TotalumWebhookError('WEBHOOK_SIGNATURE_INVALID', 'Totalum-Signature does not match');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `totalum.webhooks` (plan 04 §5.13) over SDK-API `/v1/webhooks` (a key with scope `*`): where the project receives its
|
|
47
|
+
* signed events (payments, e-mail, browser programs, `project.credits.exhausted`) and the secret they are signed with.
|
|
48
|
+
*/
|
|
49
|
+
export function totalumWebhooks(options = {}) {
|
|
50
|
+
const call = jsonClient(options);
|
|
51
|
+
return {
|
|
52
|
+
/** Same as `verifyWebhook`: checks `Totalum-Signature` and returns the parsed event. */
|
|
53
|
+
verify: verifyWebhook,
|
|
54
|
+
/** The endpoint and the first characters of its secret (the secret itself is shown only by `rotateSecret`). */
|
|
55
|
+
get: () => call('GET', '/v1/webhooks'),
|
|
56
|
+
/** Sets the `https://` URL the project's events are delivered to. */
|
|
57
|
+
setUrl: (url) => call('PUT', '/v1/webhooks', { url }),
|
|
58
|
+
/** A new signing secret, shown once; the old one keeps co-signing for 24 h. Store it as `TOTALUM_WEBHOOK_SECRET`. */
|
|
59
|
+
rotateSecret: () => call('POST', '/v1/webhooks/rotate-secret'),
|
|
60
|
+
};
|
|
61
|
+
}
|