unstructured-transform-client 0.18.18
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 +210 -0
- package/dist/_generated/apis/ExtractApi.d.ts +43 -0
- package/dist/_generated/apis/ExtractApi.js +70 -0
- package/dist/_generated/apis/JobsApi.d.ts +117 -0
- package/dist/_generated/apis/JobsApi.js +211 -0
- package/dist/_generated/apis/ParseApi.d.ts +79 -0
- package/dist/_generated/apis/ParseApi.js +111 -0
- package/dist/_generated/apis/UploadApi.d.ts +78 -0
- package/dist/_generated/apis/UploadApi.js +172 -0
- package/dist/_generated/apis/index.d.ts +4 -0
- package/dist/_generated/apis/index.js +6 -0
- package/dist/_generated/index.d.ts +3 -0
- package/dist/_generated/index.js +5 -0
- package/dist/_generated/models/Citation.d.ts +31 -0
- package/dist/_generated/models/Citation.js +44 -0
- package/dist/_generated/models/DocumentMetadata.d.ts +30 -0
- package/dist/_generated/models/DocumentMetadata.js +43 -0
- package/dist/_generated/models/Element.d.ts +43 -0
- package/dist/_generated/models/Element.js +56 -0
- package/dist/_generated/models/ElementMetadata.d.ts +40 -0
- package/dist/_generated/models/ElementMetadata.js +51 -0
- package/dist/_generated/models/ErrorCode.d.ts +46 -0
- package/dist/_generated/models/ErrorCode.js +64 -0
- package/dist/_generated/models/ExtractRequest.d.ts +40 -0
- package/dist/_generated/models/ExtractRequest.js +49 -0
- package/dist/_generated/models/ExtractionResult.d.ts +37 -0
- package/dist/_generated/models/ExtractionResult.js +47 -0
- package/dist/_generated/models/FieldMetadata.d.ts +31 -0
- package/dist/_generated/models/FieldMetadata.js +42 -0
- package/dist/_generated/models/JobAccepted.d.ts +67 -0
- package/dist/_generated/models/JobAccepted.js +73 -0
- package/dist/_generated/models/JobOperation.d.ts +25 -0
- package/dist/_generated/models/JobOperation.js +43 -0
- package/dist/_generated/models/JobPage.d.ts +35 -0
- package/dist/_generated/models/JobPage.js +48 -0
- package/dist/_generated/models/JobResult.d.ts +61 -0
- package/dist/_generated/models/JobResult.js +64 -0
- package/dist/_generated/models/JobResultState.d.ts +26 -0
- package/dist/_generated/models/JobResultState.js +44 -0
- package/dist/_generated/models/JobStatus.d.ts +29 -0
- package/dist/_generated/models/JobStatus.js +47 -0
- package/dist/_generated/models/JobSummary.d.ts +74 -0
- package/dist/_generated/models/JobSummary.js +86 -0
- package/dist/_generated/models/Locator.d.ts +45 -0
- package/dist/_generated/models/Locator.js +57 -0
- package/dist/_generated/models/ModelError.d.ts +35 -0
- package/dist/_generated/models/ModelError.js +48 -0
- package/dist/_generated/models/OutputFormat.d.ts +25 -0
- package/dist/_generated/models/OutputFormat.js +43 -0
- package/dist/_generated/models/ParseResult.d.ts +87 -0
- package/dist/_generated/models/ParseResult.js +98 -0
- package/dist/_generated/models/SourceFile.d.ts +42 -0
- package/dist/_generated/models/SourceFile.js +56 -0
- package/dist/_generated/models/TransformStatus.d.ts +26 -0
- package/dist/_generated/models/TransformStatus.js +44 -0
- package/dist/_generated/models/TransformWarning.d.ts +34 -0
- package/dist/_generated/models/TransformWarning.js +47 -0
- package/dist/_generated/models/UploadResult.d.ts +38 -0
- package/dist/_generated/models/UploadResult.js +52 -0
- package/dist/_generated/models/index.d.ts +23 -0
- package/dist/_generated/models/index.js +25 -0
- package/dist/_generated/runtime.d.ts +196 -0
- package/dist/_generated/runtime.js +398 -0
- package/dist/client.d.ts +145 -0
- package/dist/client.js +259 -0
- package/dist/files.d.ts +32 -0
- package/dist/files.js +46 -0
- package/dist/hostHeaders.d.ts +36 -0
- package/dist/hostHeaders.js +133 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +35 -0
- package/dist/multipart.d.ts +50 -0
- package/dist/multipart.js +65 -0
- package/dist/outcome.d.ts +45 -0
- package/dist/outcome.js +40 -0
- package/dist/retry.d.ts +16 -0
- package/dist/retry.js +181 -0
- package/dist/sse.d.ts +50 -0
- package/dist/sse.js +140 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.js +10 -0
- package/package.json +41 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface: `client.parse.run(...)`, `client.extract.run(...)`.
|
|
3
|
+
*
|
|
4
|
+
* openapi-generator produces `new ParseApi(config).parseRun(...)` across four
|
|
5
|
+
* unrelated Api classes. openapi.yaml declares a different surface — `tags`
|
|
6
|
+
* plus `x-sdk-method-name` per operation, so the call reads
|
|
7
|
+
* `client.parse.run(document)` (decision N1, API v2 Surface Proposal) — and no
|
|
8
|
+
* generator reads those fields. This module is where that becomes real, and
|
|
9
|
+
* `tests/facade.test.ts` holds it to the contract.
|
|
10
|
+
*
|
|
11
|
+
* It also supplies what the generator cannot:
|
|
12
|
+
*
|
|
13
|
+
* - `extract.fromDocument(...)`. POST /api/v2/extract declares BOTH an
|
|
14
|
+
* application/json body and a multipart/form-data one; the generator emits
|
|
15
|
+
* only the first and drops ExtractDocumentRequest from the models
|
|
16
|
+
* entirely. One of the three launch flows, silently uncallable.
|
|
17
|
+
* - `jobs.stream(...)`. The SSE progress stream.
|
|
18
|
+
* - `schema` as an object on both flows. The wire format is a JSON *string*
|
|
19
|
+
* on parse and a JSON *object* on extract — an inconsistency in the
|
|
20
|
+
* contract that callers should not have to know.
|
|
21
|
+
*/
|
|
22
|
+
import type { ParseRunProfileEnum } from "./_generated/apis/ParseApi.js";
|
|
23
|
+
import { Configuration } from "./_generated/runtime.js";
|
|
24
|
+
import type { JobPage, JobResult, JobStatus, OutputFormat, ParseResult, UploadResult } from "./_generated/models/index.js";
|
|
25
|
+
import { type FileInput } from "./files.js";
|
|
26
|
+
import { type Outcome } from "./outcome.js";
|
|
27
|
+
import { type RetryConfig } from "./retry.js";
|
|
28
|
+
import { type JobEvent } from "./sse.js";
|
|
29
|
+
export declare const DEFAULT_SERVER_URL = "https://platform.unstructuredapp.io";
|
|
30
|
+
export interface TransformClientOptions {
|
|
31
|
+
/** Pass an API key, or leave credentials out to use UNSTRUCTURED_API_KEY. */
|
|
32
|
+
apiKey?: string;
|
|
33
|
+
bearerToken?: string;
|
|
34
|
+
serverUrl?: string;
|
|
35
|
+
/** Suppress the X-Unstructured-Client-* headers. The User-Agent remains. */
|
|
36
|
+
sendHostHeaders?: boolean;
|
|
37
|
+
/** Appended to the User-Agent, for a caller wrapping this SDK. */
|
|
38
|
+
userAgentSuffix?: string;
|
|
39
|
+
/** Override the fetch implementation, e.g. for tests or a proxy agent. */
|
|
40
|
+
fetchApi?: typeof fetch;
|
|
41
|
+
/** Per-client retries; pass `false` to disable them. */
|
|
42
|
+
retries?: RetryConfig | false;
|
|
43
|
+
}
|
|
44
|
+
export declare class TransformClient {
|
|
45
|
+
readonly parse: ParseNamespace;
|
|
46
|
+
readonly extract: ExtractNamespace;
|
|
47
|
+
readonly jobs: JobsNamespace;
|
|
48
|
+
readonly upload: UploadNamespace;
|
|
49
|
+
/** @internal */ readonly serverUrl: string;
|
|
50
|
+
/** @internal */ readonly requestHeaders: Record<string, string>;
|
|
51
|
+
/** @internal */ readonly configuration: Configuration;
|
|
52
|
+
/** @internal */ readonly fetchApi?: typeof fetch;
|
|
53
|
+
/** @internal */ readonly retries: Required<RetryConfig> | undefined;
|
|
54
|
+
constructor(options?: TransformClientOptions);
|
|
55
|
+
}
|
|
56
|
+
export interface ParseOptions {
|
|
57
|
+
input?: FileInput;
|
|
58
|
+
fileId?: string;
|
|
59
|
+
schema?: Record<string, unknown> | string;
|
|
60
|
+
prompt?: string;
|
|
61
|
+
output?: OutputFormat;
|
|
62
|
+
include?: string[];
|
|
63
|
+
profile?: ParseRunProfileEnum;
|
|
64
|
+
waitSeconds?: number;
|
|
65
|
+
}
|
|
66
|
+
declare class ParseNamespace {
|
|
67
|
+
private readonly client;
|
|
68
|
+
constructor(client: TransformClient);
|
|
69
|
+
/**
|
|
70
|
+
* Parse a document. Supplying `schema` extracts in the same job.
|
|
71
|
+
*
|
|
72
|
+
* Returns either the finished result (200) or a job handle (202) — the
|
|
73
|
+
* latter is what `waitSeconds: 0` always produces. Discriminate with
|
|
74
|
+
* `isAccepted`.
|
|
75
|
+
*/
|
|
76
|
+
run(options: ParseOptions): Promise<Outcome<ParseResult>>;
|
|
77
|
+
}
|
|
78
|
+
export interface ExtractOptions {
|
|
79
|
+
parseId: string;
|
|
80
|
+
schema: Record<string, unknown>;
|
|
81
|
+
prompt?: string;
|
|
82
|
+
waitSeconds?: number;
|
|
83
|
+
}
|
|
84
|
+
export interface ExtractFromDocumentOptions {
|
|
85
|
+
input?: FileInput;
|
|
86
|
+
fileId?: string;
|
|
87
|
+
/** The original filename to attach when extracting from an already-uploaded file. */
|
|
88
|
+
filename?: string;
|
|
89
|
+
schema: Record<string, unknown>;
|
|
90
|
+
prompt?: string;
|
|
91
|
+
profile?: ParseRunProfileEnum;
|
|
92
|
+
waitSeconds?: number;
|
|
93
|
+
}
|
|
94
|
+
declare class ExtractNamespace {
|
|
95
|
+
private readonly client;
|
|
96
|
+
constructor(client: TransformClient);
|
|
97
|
+
/** Extract against an existing parse. The document is not parsed again. */
|
|
98
|
+
run(options: ExtractOptions): Promise<Outcome<ParseResult>>;
|
|
99
|
+
/**
|
|
100
|
+
* Extract directly from a raw document, in one call.
|
|
101
|
+
*
|
|
102
|
+
* The multipart variant of POST /api/v2/extract, reconstructed because
|
|
103
|
+
* openapi-generator emits only the JSON body for this path.
|
|
104
|
+
*/
|
|
105
|
+
fromDocument(options: ExtractFromDocumentOptions): Promise<Outcome<ParseResult>>;
|
|
106
|
+
}
|
|
107
|
+
export interface JobsGetOptions {
|
|
108
|
+
output?: OutputFormat;
|
|
109
|
+
include?: string[];
|
|
110
|
+
}
|
|
111
|
+
export interface JobsListOptions {
|
|
112
|
+
cursor?: string;
|
|
113
|
+
limit?: number;
|
|
114
|
+
status?: JobStatus;
|
|
115
|
+
}
|
|
116
|
+
export interface JobsStreamOptions {
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
output?: OutputFormat;
|
|
119
|
+
include?: string[];
|
|
120
|
+
}
|
|
121
|
+
declare class JobsNamespace {
|
|
122
|
+
private readonly client;
|
|
123
|
+
constructor(client: TransformClient);
|
|
124
|
+
get(jobId: string, options?: JobsGetOptions): Promise<JobResult>;
|
|
125
|
+
list(options?: JobsListOptions): Promise<JobPage>;
|
|
126
|
+
iterate(options?: JobsListOptions): AsyncGenerator<JobPage["jobs"][number], void, undefined>;
|
|
127
|
+
cancel(jobId: string): Promise<JobResult>;
|
|
128
|
+
delete(jobId: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Progress events for a running job, as they happen.
|
|
131
|
+
*
|
|
132
|
+
* The same resource as `get(jobId)`, selected by Accept — not a second
|
|
133
|
+
* route. Yields `status` events and then exactly one terminal `result` or
|
|
134
|
+
* `error`, after which the stream ends.
|
|
135
|
+
*/
|
|
136
|
+
stream(jobId: string, options?: JobsStreamOptions): AsyncGenerator<JobEvent>;
|
|
137
|
+
}
|
|
138
|
+
declare class UploadNamespace {
|
|
139
|
+
private readonly client;
|
|
140
|
+
constructor(client: TransformClient);
|
|
141
|
+
run(input: FileInput): Promise<UploadResult>;
|
|
142
|
+
get(fileId: string): Promise<Blob>;
|
|
143
|
+
delete(fileId: string): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface: `client.parse.run(...)`, `client.extract.run(...)`.
|
|
3
|
+
*
|
|
4
|
+
* openapi-generator produces `new ParseApi(config).parseRun(...)` across four
|
|
5
|
+
* unrelated Api classes. openapi.yaml declares a different surface — `tags`
|
|
6
|
+
* plus `x-sdk-method-name` per operation, so the call reads
|
|
7
|
+
* `client.parse.run(document)` (decision N1, API v2 Surface Proposal) — and no
|
|
8
|
+
* generator reads those fields. This module is where that becomes real, and
|
|
9
|
+
* `tests/facade.test.ts` holds it to the contract.
|
|
10
|
+
*
|
|
11
|
+
* It also supplies what the generator cannot:
|
|
12
|
+
*
|
|
13
|
+
* - `extract.fromDocument(...)`. POST /api/v2/extract declares BOTH an
|
|
14
|
+
* application/json body and a multipart/form-data one; the generator emits
|
|
15
|
+
* only the first and drops ExtractDocumentRequest from the models
|
|
16
|
+
* entirely. One of the three launch flows, silently uncallable.
|
|
17
|
+
* - `jobs.stream(...)`. The SSE progress stream.
|
|
18
|
+
* - `schema` as an object on both flows. The wire format is a JSON *string*
|
|
19
|
+
* on parse and a JSON *object* on extract — an inconsistency in the
|
|
20
|
+
* contract that callers should not have to know.
|
|
21
|
+
*/
|
|
22
|
+
import { ExtractApi } from "./_generated/apis/ExtractApi.js";
|
|
23
|
+
import { JobsApi } from "./_generated/apis/JobsApi.js";
|
|
24
|
+
import { ParseApi } from "./_generated/apis/ParseApi.js";
|
|
25
|
+
import { UploadApi } from "./_generated/apis/UploadApi.js";
|
|
26
|
+
import { Configuration } from "./_generated/runtime.js";
|
|
27
|
+
import { ParseResultFromJSON } from "./_generated/models/index.js";
|
|
28
|
+
import { asFile } from "./files.js";
|
|
29
|
+
import * as hostHeaders from "./hostHeaders.js";
|
|
30
|
+
import { postMultipart } from "./multipart.js";
|
|
31
|
+
import { resolveOutcome } from "./outcome.js";
|
|
32
|
+
import { fetchWithRetries, normalizeRetries } from "./retry.js";
|
|
33
|
+
import { streamJobEvents } from "./sse.js";
|
|
34
|
+
export const DEFAULT_SERVER_URL = "https://platform.unstructuredapp.io";
|
|
35
|
+
/** Matches the service's `securitySchemes`; see openapi.yaml. */
|
|
36
|
+
const API_KEY_HEADER = "unstructured-api-key";
|
|
37
|
+
const API_KEY_ENV_VAR = "UNSTRUCTURED_API_KEY";
|
|
38
|
+
function apiKeyFromEnvironment() {
|
|
39
|
+
// The package supports Node, but keeping this guarded also makes importing
|
|
40
|
+
// the client safe in browser bundles where `process` is not defined.
|
|
41
|
+
if (typeof process === "undefined")
|
|
42
|
+
return undefined;
|
|
43
|
+
return process.env[API_KEY_ENV_VAR] || undefined;
|
|
44
|
+
}
|
|
45
|
+
export class TransformClient {
|
|
46
|
+
parse;
|
|
47
|
+
extract;
|
|
48
|
+
jobs;
|
|
49
|
+
upload;
|
|
50
|
+
/** @internal */ serverUrl;
|
|
51
|
+
/** @internal */ requestHeaders;
|
|
52
|
+
/** @internal */ configuration;
|
|
53
|
+
/** @internal */ fetchApi;
|
|
54
|
+
/** @internal */ retries;
|
|
55
|
+
constructor(options = {}) {
|
|
56
|
+
const apiKey = options.apiKey ??
|
|
57
|
+
(options.bearerToken === undefined ? apiKeyFromEnvironment() : undefined);
|
|
58
|
+
const hasApiKey = apiKey !== undefined;
|
|
59
|
+
const hasBearer = options.bearerToken !== undefined;
|
|
60
|
+
if (hasApiKey === hasBearer) {
|
|
61
|
+
throw new Error("pass exactly one of apiKey or bearerToken, or set " +
|
|
62
|
+
`${API_KEY_ENV_VAR} — they are different identities, not ` +
|
|
63
|
+
"interchangeable spellings of one credential");
|
|
64
|
+
}
|
|
65
|
+
this.serverUrl = (options.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
|
|
66
|
+
this.fetchApi = options.fetchApi;
|
|
67
|
+
this.retries = normalizeRetries(options.retries);
|
|
68
|
+
const auth = hasApiKey
|
|
69
|
+
? { [API_KEY_HEADER]: apiKey }
|
|
70
|
+
: { Authorization: `Bearer ${options.bearerToken}` };
|
|
71
|
+
// NOTE: in a browser, `User-Agent` is a forbidden header name and is
|
|
72
|
+
// dropped silently by fetch. That is acceptable degradation rather than a
|
|
73
|
+
// bug to work around — the X-Unstructured-Client-* headers still arrive,
|
|
74
|
+
// and spoofing a UA past the browser's own is not something to attempt.
|
|
75
|
+
const identification = hostHeaders.build({
|
|
76
|
+
sendHostHeaders: options.sendHostHeaders,
|
|
77
|
+
userAgentSuffix: options.userAgentSuffix,
|
|
78
|
+
});
|
|
79
|
+
this.requestHeaders = { ...auth, ...identification };
|
|
80
|
+
this.configuration = new Configuration({
|
|
81
|
+
basePath: this.serverUrl,
|
|
82
|
+
headers: this.requestHeaders,
|
|
83
|
+
fetchApi: (url, init) => fetchWithRetries(this.retries, {
|
|
84
|
+
url: String(url),
|
|
85
|
+
init: init ?? {},
|
|
86
|
+
fetchApi: options.fetchApi,
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
this.parse = new ParseNamespace(this);
|
|
90
|
+
this.extract = new ExtractNamespace(this);
|
|
91
|
+
this.jobs = new JobsNamespace(this);
|
|
92
|
+
this.upload = new UploadNamespace(this);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** `Prefer: wait=N`. `0` is meaningful, so the check is against undefined. */
|
|
96
|
+
function prefer(waitSeconds) {
|
|
97
|
+
return waitSeconds === undefined ? undefined : `wait=${waitSeconds}`;
|
|
98
|
+
}
|
|
99
|
+
class ParseNamespace {
|
|
100
|
+
client;
|
|
101
|
+
constructor(client) {
|
|
102
|
+
this.client = client;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse a document. Supplying `schema` extracts in the same job.
|
|
106
|
+
*
|
|
107
|
+
* Returns either the finished result (200) or a job handle (202) — the
|
|
108
|
+
* latter is what `waitSeconds: 0` always produces. Discriminate with
|
|
109
|
+
* `isAccepted`.
|
|
110
|
+
*/
|
|
111
|
+
async run(options) {
|
|
112
|
+
if ((options.input === undefined) === (options.fileId === undefined)) {
|
|
113
|
+
throw new Error("pass exactly one of input or fileId");
|
|
114
|
+
}
|
|
115
|
+
return resolveOutcome(await new ParseApi(this.client.configuration).parseRunRaw({
|
|
116
|
+
prefer: prefer(options.waitSeconds),
|
|
117
|
+
include: options.include,
|
|
118
|
+
input: options.input === undefined ? undefined : asFile(options.input),
|
|
119
|
+
fileId: options.fileId,
|
|
120
|
+
output: options.output,
|
|
121
|
+
// A string is passed through so a caller holding a pre-serialized schema
|
|
122
|
+
// is not forced to round-trip it through JSON.parse.
|
|
123
|
+
schema: typeof options.schema === "object" && options.schema !== null
|
|
124
|
+
? JSON.stringify(options.schema)
|
|
125
|
+
: options.schema,
|
|
126
|
+
prompt: options.prompt,
|
|
127
|
+
profile: options.profile,
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
class ExtractNamespace {
|
|
132
|
+
client;
|
|
133
|
+
constructor(client) {
|
|
134
|
+
this.client = client;
|
|
135
|
+
}
|
|
136
|
+
/** Extract against an existing parse. The document is not parsed again. */
|
|
137
|
+
async run(options) {
|
|
138
|
+
return resolveOutcome(await new ExtractApi(this.client.configuration).extractRunRaw({
|
|
139
|
+
extractRequest: {
|
|
140
|
+
parseId: options.parseId,
|
|
141
|
+
schema: options.schema,
|
|
142
|
+
prompt: options.prompt,
|
|
143
|
+
},
|
|
144
|
+
prefer: prefer(options.waitSeconds),
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Extract directly from a raw document, in one call.
|
|
149
|
+
*
|
|
150
|
+
* The multipart variant of POST /api/v2/extract, reconstructed because
|
|
151
|
+
* openapi-generator emits only the JSON body for this path.
|
|
152
|
+
*/
|
|
153
|
+
async fromDocument(options) {
|
|
154
|
+
// A document OR the id of one already uploaded, exactly as parse.run
|
|
155
|
+
// takes: ExtractDocumentRequest declares both, so requiring the bytes
|
|
156
|
+
// would make an already-uploaded file unusable here.
|
|
157
|
+
if ((options.input === undefined) === (options.fileId === undefined)) {
|
|
158
|
+
throw new Error("pass exactly one of input or fileId");
|
|
159
|
+
}
|
|
160
|
+
if (options.input !== undefined && options.filename !== undefined) {
|
|
161
|
+
throw new Error("filename only applies to fileId; input already carries its name");
|
|
162
|
+
}
|
|
163
|
+
const file = options.input === undefined ? undefined : asFile(options.input);
|
|
164
|
+
return postMultipart(this.client, {
|
|
165
|
+
path: "/api/v2/extract",
|
|
166
|
+
fileField: "input",
|
|
167
|
+
file,
|
|
168
|
+
filename: file?.name,
|
|
169
|
+
fields: {
|
|
170
|
+
file_id: options.fileId,
|
|
171
|
+
filename: options.filename,
|
|
172
|
+
schema: JSON.stringify(options.schema),
|
|
173
|
+
prompt: options.prompt,
|
|
174
|
+
profile: options.profile,
|
|
175
|
+
},
|
|
176
|
+
waitSeconds: options.waitSeconds,
|
|
177
|
+
fromJSON: ParseResultFromJSON,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
class JobsNamespace {
|
|
182
|
+
client;
|
|
183
|
+
constructor(client) {
|
|
184
|
+
this.client = client;
|
|
185
|
+
}
|
|
186
|
+
async get(jobId, options = {}) {
|
|
187
|
+
return new JobsApi(this.client.configuration).jobsGet({
|
|
188
|
+
jobId,
|
|
189
|
+
output: options.output,
|
|
190
|
+
include: options.include,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async list(options = {}) {
|
|
194
|
+
return new JobsApi(this.client.configuration).jobsList({
|
|
195
|
+
cursor: options.cursor,
|
|
196
|
+
limit: options.limit,
|
|
197
|
+
status: options.status,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async *iterate(options = {}) {
|
|
201
|
+
let cursor = options.cursor;
|
|
202
|
+
for (;;) {
|
|
203
|
+
const page = await this.list({ ...options, cursor });
|
|
204
|
+
for (const job of page.jobs) {
|
|
205
|
+
yield job;
|
|
206
|
+
}
|
|
207
|
+
cursor = page.nextCursor ?? undefined;
|
|
208
|
+
if (cursor === undefined)
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async cancel(jobId) {
|
|
213
|
+
return new JobsApi(this.client.configuration).jobsCancel({ jobId });
|
|
214
|
+
}
|
|
215
|
+
async delete(jobId) {
|
|
216
|
+
await new JobsApi(this.client.configuration).jobsDelete({ jobId });
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Progress events for a running job, as they happen.
|
|
220
|
+
*
|
|
221
|
+
* The same resource as `get(jobId)`, selected by Accept — not a second
|
|
222
|
+
* route. Yields `status` events and then exactly one terminal `result` or
|
|
223
|
+
* `error`, after which the stream ends.
|
|
224
|
+
*/
|
|
225
|
+
stream(jobId, options = {}) {
|
|
226
|
+
// The terminal `result` event carries the same representation the JSON
|
|
227
|
+
// path would return, so these belong on the stream too — otherwise it
|
|
228
|
+
// could only ever deliver the server default, making it a second-class way
|
|
229
|
+
// to read the same resource.
|
|
230
|
+
const query = new URLSearchParams();
|
|
231
|
+
if (options.output !== undefined)
|
|
232
|
+
query.set("output", options.output);
|
|
233
|
+
for (const value of options.include ?? [])
|
|
234
|
+
query.append("include", value);
|
|
235
|
+
const suffix = query.size > 0 ? `?${query}` : "";
|
|
236
|
+
return streamJobEvents({
|
|
237
|
+
url: `${this.client.serverUrl}/api/v2/jobs/${encodeURIComponent(jobId)}${suffix}`,
|
|
238
|
+
headers: this.client.requestHeaders,
|
|
239
|
+
signal: options.signal,
|
|
240
|
+
fetchApi: this.client.fetchApi,
|
|
241
|
+
retries: this.client.retries,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
class UploadNamespace {
|
|
246
|
+
client;
|
|
247
|
+
constructor(client) {
|
|
248
|
+
this.client = client;
|
|
249
|
+
}
|
|
250
|
+
async run(input) {
|
|
251
|
+
return new UploadApi(this.client.configuration).uploadRun({ file: asFile(input) });
|
|
252
|
+
}
|
|
253
|
+
async get(fileId) {
|
|
254
|
+
return new UploadApi(this.client.configuration).uploadGet({ fileId });
|
|
255
|
+
}
|
|
256
|
+
async delete(fileId) {
|
|
257
|
+
await new UploadApi(this.client.configuration).uploadDelete({ fileId });
|
|
258
|
+
}
|
|
259
|
+
}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning what a caller has into a part the service will accept.
|
|
3
|
+
*
|
|
4
|
+
* THE FILENAME IS NOT DECORATION. The service validates an upload's extension
|
|
5
|
+
* against a published allowlist, so a part with no filename is rejected as
|
|
6
|
+
* "Unsupported file type" — for a document that is fine.
|
|
7
|
+
*
|
|
8
|
+
* `FormData.append(name, blob)` labels the part `blob`. That is what the
|
|
9
|
+
* GENERATED code does for `parse.run` and `upload.run`
|
|
10
|
+
* (`formParams.append('input', requestParameters['input'])`), so passing a
|
|
11
|
+
* plain `Blob` to either would have been rejected by the real service while
|
|
12
|
+
* passing every test against a stub that does not validate extensions.
|
|
13
|
+
*
|
|
14
|
+
* A `File` carries its own name and FormData uses it, so normalizing to `File`
|
|
15
|
+
* fixes the generated paths without patching generated code.
|
|
16
|
+
*
|
|
17
|
+
* The Python SDK refuses a nameless document for the same reason and with the
|
|
18
|
+
* same wording; the two SDKs should not disagree about what is a valid call.
|
|
19
|
+
*/
|
|
20
|
+
/** A document, in any of the shapes a caller reasonably has one. */
|
|
21
|
+
export type FileInput = File | {
|
|
22
|
+
data: Blob | Uint8Array | ArrayBuffer;
|
|
23
|
+
filename: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Normalize to a `File`, or throw with the reason.
|
|
27
|
+
*
|
|
28
|
+
* A bare `Blob` is refused rather than given a made-up name: guessing a
|
|
29
|
+
* filename means guessing the extension the service validates, and being wrong
|
|
30
|
+
* produces a rejection that points at the document instead of at the call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function asFile(input: FileInput): File;
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning what a caller has into a part the service will accept.
|
|
3
|
+
*
|
|
4
|
+
* THE FILENAME IS NOT DECORATION. The service validates an upload's extension
|
|
5
|
+
* against a published allowlist, so a part with no filename is rejected as
|
|
6
|
+
* "Unsupported file type" — for a document that is fine.
|
|
7
|
+
*
|
|
8
|
+
* `FormData.append(name, blob)` labels the part `blob`. That is what the
|
|
9
|
+
* GENERATED code does for `parse.run` and `upload.run`
|
|
10
|
+
* (`formParams.append('input', requestParameters['input'])`), so passing a
|
|
11
|
+
* plain `Blob` to either would have been rejected by the real service while
|
|
12
|
+
* passing every test against a stub that does not validate extensions.
|
|
13
|
+
*
|
|
14
|
+
* A `File` carries its own name and FormData uses it, so normalizing to `File`
|
|
15
|
+
* fixes the generated paths without patching generated code.
|
|
16
|
+
*
|
|
17
|
+
* The Python SDK refuses a nameless document for the same reason and with the
|
|
18
|
+
* same wording; the two SDKs should not disagree about what is a valid call.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Normalize to a `File`, or throw with the reason.
|
|
22
|
+
*
|
|
23
|
+
* A bare `Blob` is refused rather than given a made-up name: guessing a
|
|
24
|
+
* filename means guessing the extension the service validates, and being wrong
|
|
25
|
+
* produces a rejection that points at the document instead of at the call.
|
|
26
|
+
*/
|
|
27
|
+
export function asFile(input) {
|
|
28
|
+
if (typeof File !== "undefined" && input instanceof File) {
|
|
29
|
+
if (!input.name) {
|
|
30
|
+
throw new TypeError("this File has no name, so the extension the service validates is " +
|
|
31
|
+
'unknown. Pass { data, filename: "invoice.pdf" } instead.');
|
|
32
|
+
}
|
|
33
|
+
return input;
|
|
34
|
+
}
|
|
35
|
+
if (input && typeof input === "object" && "data" in input && "filename" in input) {
|
|
36
|
+
const { data, filename } = input;
|
|
37
|
+
if (!filename) {
|
|
38
|
+
throw new TypeError("filename must not be empty");
|
|
39
|
+
}
|
|
40
|
+
return new File([data], filename);
|
|
41
|
+
}
|
|
42
|
+
throw new TypeError("pass a File, or { data, filename }. A bare Blob is refused because the " +
|
|
43
|
+
"service validates the upload's extension against an allowlist, and a " +
|
|
44
|
+
"Blob has no filename to validate — it would be rejected as an " +
|
|
45
|
+
"unsupported file type.");
|
|
46
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host and client identification headers (TRNSFRM-91).
|
|
3
|
+
*
|
|
4
|
+
* The TypeScript half of the same contract the Python SDK implements. Keep the
|
|
5
|
+
* two in step: a consumer correlating traffic from both languages should not
|
|
6
|
+
* have to special-case either.
|
|
7
|
+
*
|
|
8
|
+
* WHAT IS SENT: SDK name and version, language, runtime version, OS family,
|
|
9
|
+
* OS release, CPU architecture.
|
|
10
|
+
*
|
|
11
|
+
* NOT SENT, deliberately: hostname, username, working directory, environment
|
|
12
|
+
* variables, local IP or MAC address, or anything read from the document being
|
|
13
|
+
* parsed. `hostHeaders.test.ts` asserts the exclusions against this machine's
|
|
14
|
+
* real values rather than trusting this comment.
|
|
15
|
+
*
|
|
16
|
+
* Nothing here sets `X-Unstructured-Request-Source`. That header is trusted
|
|
17
|
+
* (TRNSFRM-113) — the edge owns it, the values are ui/mcp/api, and a public
|
|
18
|
+
* caller must not be able to claim `ui` or `mcp`. An SDK is a public caller.
|
|
19
|
+
* A wrapping caller identifies itself with `userAgentSuffix` instead, which is
|
|
20
|
+
* advisory and carries no privilege.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CLIENT_NAME = "@utic/transform-client";
|
|
23
|
+
export declare const HEADER_NAME = "X-Unstructured-Client-Name";
|
|
24
|
+
export declare const HEADER_VERSION = "X-Unstructured-Client-Version";
|
|
25
|
+
export declare const HEADER_LANGUAGE = "X-Unstructured-Client-Language";
|
|
26
|
+
export declare const HEADER_RUNTIME = "X-Unstructured-Client-Runtime";
|
|
27
|
+
export declare const HEADER_PLATFORM = "X-Unstructured-Client-Platform";
|
|
28
|
+
export declare const DISABLE_ENV_VAR = "UNSTRUCTURED_TRANSFORM_DISABLE_HOST_HEADERS";
|
|
29
|
+
/** `@utic/transform-client/0.18.0 (node/22.4.0; darwin/arm64)` */
|
|
30
|
+
export declare function userAgent(suffix?: string): string;
|
|
31
|
+
export interface HostHeaderOptions {
|
|
32
|
+
sendHostHeaders?: boolean;
|
|
33
|
+
userAgentSuffix?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The headers to merge into every request. */
|
|
36
|
+
export declare function build(options?: HostHeaderOptions): Record<string, string>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host and client identification headers (TRNSFRM-91).
|
|
3
|
+
*
|
|
4
|
+
* The TypeScript half of the same contract the Python SDK implements. Keep the
|
|
5
|
+
* two in step: a consumer correlating traffic from both languages should not
|
|
6
|
+
* have to special-case either.
|
|
7
|
+
*
|
|
8
|
+
* WHAT IS SENT: SDK name and version, language, runtime version, OS family,
|
|
9
|
+
* OS release, CPU architecture.
|
|
10
|
+
*
|
|
11
|
+
* NOT SENT, deliberately: hostname, username, working directory, environment
|
|
12
|
+
* variables, local IP or MAC address, or anything read from the document being
|
|
13
|
+
* parsed. `hostHeaders.test.ts` asserts the exclusions against this machine's
|
|
14
|
+
* real values rather than trusting this comment.
|
|
15
|
+
*
|
|
16
|
+
* Nothing here sets `X-Unstructured-Request-Source`. That header is trusted
|
|
17
|
+
* (TRNSFRM-113) — the edge owns it, the values are ui/mcp/api, and a public
|
|
18
|
+
* caller must not be able to claim `ui` or `mcp`. An SDK is a public caller.
|
|
19
|
+
* A wrapping caller identifies itself with `userAgentSuffix` instead, which is
|
|
20
|
+
* advisory and carries no privilege.
|
|
21
|
+
*/
|
|
22
|
+
import { VERSION } from "./version.js";
|
|
23
|
+
export const CLIENT_NAME = "@utic/transform-client";
|
|
24
|
+
export const HEADER_NAME = "X-Unstructured-Client-Name";
|
|
25
|
+
export const HEADER_VERSION = "X-Unstructured-Client-Version";
|
|
26
|
+
export const HEADER_LANGUAGE = "X-Unstructured-Client-Language";
|
|
27
|
+
export const HEADER_RUNTIME = "X-Unstructured-Client-Runtime";
|
|
28
|
+
export const HEADER_PLATFORM = "X-Unstructured-Client-Platform";
|
|
29
|
+
export const DISABLE_ENV_VAR = "UNSTRUCTURED_TRANSFORM_DISABLE_HOST_HEADERS";
|
|
30
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
31
|
+
/**
|
|
32
|
+
* Where this is running. Returns `unknown` values rather than throwing.
|
|
33
|
+
*
|
|
34
|
+
* The SDK is expected to run in a browser as well as in Node, and `process` is
|
|
35
|
+
* simply absent there — reading it unguarded would make importing this module
|
|
36
|
+
* throw in a bundler's output. A browser deliberately gets no OS or
|
|
37
|
+
* architecture: that information is not reliably available, and the values
|
|
38
|
+
* that ARE available are fingerprinting surface we have no reason to collect.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The OS release, resolved once at load and only where it exists.
|
|
42
|
+
*
|
|
43
|
+
* `node:os` is the only source of it and does not exist in a browser, so a
|
|
44
|
+
* static import would break any browser bundle of this package. A guarded
|
|
45
|
+
* top-level dynamic import gets the value under Node and leaves it "unknown"
|
|
46
|
+
* everywhere else — `require` is not an option, this package is ESM.
|
|
47
|
+
*/
|
|
48
|
+
let osRelease = "unknown";
|
|
49
|
+
try {
|
|
50
|
+
osRelease = (await import("node:os")).release();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Not Node, or bundled without it. Family and architecture still ship.
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Node's architecture name, spelled the way Python spells it.
|
|
57
|
+
*
|
|
58
|
+
* `process.arch` says `x64` where `platform.machine()` says `x86_64` for the
|
|
59
|
+
* same physical CPU, so the two SDKs bucketed the same architecture under two
|
|
60
|
+
* different labels — which defeats the point of a header consumers group by.
|
|
61
|
+
* Python's spelling wins because it is the uname value the rest of the world
|
|
62
|
+
* reports.
|
|
63
|
+
*/
|
|
64
|
+
const ARCH_ALIASES = {
|
|
65
|
+
x64: "x86_64",
|
|
66
|
+
ia32: "i386",
|
|
67
|
+
arm: "armv7l",
|
|
68
|
+
ppc64: "ppc64le",
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* `arm64` is the case a flat alias map gets wrong, in both directions.
|
|
72
|
+
*
|
|
73
|
+
* Python reports whatever uname says, and uname disagrees with itself across
|
|
74
|
+
* operating systems: Linux says `aarch64`, macOS says `arm64`. Node says
|
|
75
|
+
* `arm64` on both. So mapping arm64 -> aarch64 unconditionally would fix
|
|
76
|
+
* Graviton and BREAK macOS, where the two SDKs currently agree.
|
|
77
|
+
*
|
|
78
|
+
* Hence the OS check. Adding this to ARCH_ALIASES instead would have traded
|
|
79
|
+
* one mismatch for another and looked like a fix.
|
|
80
|
+
*/
|
|
81
|
+
function architecture(platform, arch) {
|
|
82
|
+
if (arch === "arm64") {
|
|
83
|
+
return platform === "linux" ? "aarch64" : "arm64";
|
|
84
|
+
}
|
|
85
|
+
return ARCH_ALIASES[arch] ?? arch;
|
|
86
|
+
}
|
|
87
|
+
function environment() {
|
|
88
|
+
const proc = globalThis.process;
|
|
89
|
+
if (proc?.versions?.node) {
|
|
90
|
+
// `<family>/<release>/<arch>`, the same three-part shape the Python SDK
|
|
91
|
+
// sends. An earlier version omitted the release, so the two SDKs disagreed
|
|
92
|
+
// about the format of a header consumers are meant to group by.
|
|
93
|
+
return {
|
|
94
|
+
runtime: `node/${proc.versions.node}`,
|
|
95
|
+
platform: `${proc.platform}/${osRelease}/${architecture(proc.platform, proc.arch)}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { runtime: "browser", platform: "unknown" };
|
|
99
|
+
}
|
|
100
|
+
function disabled(explicit) {
|
|
101
|
+
// An explicit argument wins over the environment, so a caller can force the
|
|
102
|
+
// headers ON where they are disabled by default. Only `undefined` defers —
|
|
103
|
+
// `false` is a decision, and treating it as absence would make an explicit
|
|
104
|
+
// `true` unable to override.
|
|
105
|
+
if (explicit !== undefined) {
|
|
106
|
+
return !explicit;
|
|
107
|
+
}
|
|
108
|
+
const proc = globalThis.process;
|
|
109
|
+
const value = proc?.env?.[DISABLE_ENV_VAR]?.trim().toLowerCase() ?? "";
|
|
110
|
+
return TRUTHY.has(value);
|
|
111
|
+
}
|
|
112
|
+
/** `@utic/transform-client/0.18.0 (node/22.4.0; darwin/arm64)` */
|
|
113
|
+
export function userAgent(suffix) {
|
|
114
|
+
const { runtime, platform } = environment();
|
|
115
|
+
const agent = `${CLIENT_NAME}/${VERSION} (${runtime}; ${platform})`;
|
|
116
|
+
return suffix ? `${agent} ${suffix}` : agent;
|
|
117
|
+
}
|
|
118
|
+
/** The headers to merge into every request. */
|
|
119
|
+
export function build(options = {}) {
|
|
120
|
+
const headers = {
|
|
121
|
+
"User-Agent": userAgent(options.userAgentSuffix),
|
|
122
|
+
};
|
|
123
|
+
if (disabled(options.sendHostHeaders)) {
|
|
124
|
+
return headers;
|
|
125
|
+
}
|
|
126
|
+
const { runtime, platform } = environment();
|
|
127
|
+
headers[HEADER_NAME] = CLIENT_NAME;
|
|
128
|
+
headers[HEADER_VERSION] = VERSION;
|
|
129
|
+
headers[HEADER_LANGUAGE] = "typescript";
|
|
130
|
+
headers[HEADER_RUNTIME] = runtime;
|
|
131
|
+
headers[HEADER_PLATFORM] = platform;
|
|
132
|
+
return headers;
|
|
133
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript client for the Unstructured Transform v2 API.
|
|
3
|
+
*
|
|
4
|
+
* import { TransformClient } from "@utic/transform-client";
|
|
5
|
+
*
|
|
6
|
+
* const client = new TransformClient({ apiKey: "..." });
|
|
7
|
+
* const result = await client.parse.run({ input: file });
|
|
8
|
+
*
|
|
9
|
+
* Everything exported here is public. The `_generated` directory is not — it
|
|
10
|
+
* is produced from openapi.yaml at build time and its layout is the
|
|
11
|
+
* generator's business, so importing from it directly will break without
|
|
12
|
+
* notice. The models re-exported below are the exception: they are the
|
|
13
|
+
* response types this SDK returns, so a caller needs to be able to name them.
|
|
14
|
+
*/
|
|
15
|
+
export { DEFAULT_SERVER_URL, TransformClient } from "./client.js";
|
|
16
|
+
export type { ExtractFromDocumentOptions, ExtractOptions, JobsGetOptions, JobsListOptions, JobsStreamOptions, ParseOptions, TransformClientOptions, } from "./client.js";
|
|
17
|
+
export type { RetryConfig } from "./retry.js";
|
|
18
|
+
export { isTerminal, type JobEvent } from "./sse.js";
|
|
19
|
+
export { asFile, type FileInput } from "./files.js";
|
|
20
|
+
export { isAccepted, type Outcome } from "./outcome.js";
|
|
21
|
+
export { VERSION } from "./version.js";
|
|
22
|
+
export { FetchError, RequiredError, ResponseError } from "./_generated/runtime.js";
|
|
23
|
+
export type { ModelError as TransformErrorBody } from "./_generated/models/index.js";
|
|
24
|
+
export { ErrorCode, JobStatus, OutputFormat, } from "./_generated/models/index.js";
|
|
25
|
+
export { ParseRunProfileEnum as Profile } from "./_generated/apis/ParseApi.js";
|
|
26
|
+
export type { Element, ExtractionResult, JobAccepted, JobPage, JobResult, JobSummary, ParseResult, UploadResult, } from "./_generated/models/index.js";
|