@retab/node 1.0.63 → 1.0.65

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.
@@ -1,21 +1,20 @@
1
1
  import { CompositionClient, RequestOptions } from "../../client.js";
2
- import { BaseProjectInput, Project, CreateProjectRequest, MIMEDataInput, RetabParsedChatCompletion } from "../../types.js";
3
- import APIProjectsDocuments from "./documents/client";
4
- import APIProjectsIterations from "./iterations/client";
2
+ import { Project, CreateProjectRequest, MIMEDataInput, RetabParsedChatCompletion } from "../../types.js";
5
3
  export default class APIProjects extends CompositionClient {
6
4
  constructor(client: CompositionClient);
7
- documents: APIProjectsDocuments;
8
- iterations: APIProjectsIterations;
9
5
  create(body: CreateProjectRequest, options?: RequestOptions): Promise<Project>;
10
- update(projectId: string, body: Partial<BaseProjectInput>, options?: RequestOptions): Promise<Project>;
11
6
  list(options?: RequestOptions): Promise<Project[]>;
12
7
  get(projectId: string, options?: RequestOptions): Promise<Project>;
13
8
  delete(projectId: string, options?: RequestOptions): Promise<void>;
14
- extract({ project_id, iteration_id, document, documents, temperature, seed, store }: {
9
+ publish(projectId: string, body?: Record<string, unknown>, options?: RequestOptions): Promise<Project>;
10
+ extract({ project_id, iteration_id, document, documents, model, image_resolution_dpi, n_consensus, temperature, seed, store }: {
15
11
  project_id: string;
16
12
  iteration_id?: string;
17
13
  document?: MIMEDataInput;
18
14
  documents?: MIMEDataInput[];
15
+ model?: string;
16
+ image_resolution_dpi?: number;
17
+ n_consensus?: number;
19
18
  temperature?: number;
20
19
  seed?: number;
21
20
  store?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/api/projects/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAa,OAAO,EAAkE,oBAAoB,EAAE,aAAa,EAAa,yBAAyB,EAA8B,MAAM,gBAAgB,CAAC;AAC7O,OAAO,oBAAoB,MAAM,oBAAoB,CAAC;AACtD,OAAO,qBAAqB,MAAM,qBAAqB,CAAC;AAExD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,iBAAiB;gBAC1C,MAAM,EAAE,iBAAiB;IAIrC,SAAS,uBAAkC;IAC3C,UAAU,wBAAmC;IAEvC,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAU9E,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtG,IAAI,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IASlD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IASlE,OAAO,CAAC,EACV,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,WAAW,EACX,IAAI,EACJ,KAAK,EACR,EAAE;QACC,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,aAAa,CAAC;QACzB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,OAAO,CAAC;KACnB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,yBAAyB,CAAC;CAwBnE"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/api/projects/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAa,OAAO,EAAmC,oBAAoB,EAAE,aAAa,EAAa,yBAAyB,EAA8B,MAAM,gBAAgB,CAAC;AAE5L,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,iBAAiB;gBAC1C,MAAM,EAAE,iBAAiB;IAI/B,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAU9E,IAAI,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IASlD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IASlE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAYtG,OAAO,CAAC,EACV,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,KAAK,EACL,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,IAAI,EACJ,KAAK,EACR,EAAE;QACC,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,aAAa,CAAC;QACzB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,OAAO,CAAC;KACnB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,yBAAyB,CAAC;CA2BnE"}
@@ -1,13 +1,9 @@
1
1
  import { CompositionClient } from "../../client.js";
2
2
  import { mimeToBlob } from "../../mime.js";
3
- import { dataArray, ZBaseProject, ZProjectLoose as ZProject, ZCreateProjectRequest, ZMIMEData, ZRetabParsedChatCompletion } from "../../types.js";
4
- import APIProjectsDocuments from "./documents/client";
5
- import APIProjectsIterations from "./iterations/client";
3
+ import { dataArray, ZProject, ZCreateProjectRequest, ZMIMEData, ZRetabParsedChatCompletion } from "../../types.js";
6
4
  export default class APIProjects extends CompositionClient {
7
5
  constructor(client) {
8
6
  super(client);
9
- this.documents = new APIProjectsDocuments(this);
10
- this.iterations = new APIProjectsIterations(this);
11
7
  }
12
8
  async create(body, options) {
13
9
  return this._fetchJson(ZProject, {
@@ -18,15 +14,6 @@ export default class APIProjects extends CompositionClient {
18
14
  headers: options?.headers,
19
15
  });
20
16
  }
21
- async update(projectId, body, options) {
22
- return this._fetchJson(ZProject, {
23
- url: `/v1/projects/${projectId}`,
24
- method: "PATCH",
25
- body: { ...(await ZBaseProject.partial().parseAsync(body)), ...(options?.body || {}) },
26
- params: options?.params,
27
- headers: options?.headers,
28
- });
29
- }
30
17
  async list(options) {
31
18
  return this._fetchJson(dataArray(ZProject), {
32
19
  url: "/v1/projects",
@@ -51,7 +38,17 @@ export default class APIProjects extends CompositionClient {
51
38
  headers: options?.headers,
52
39
  });
53
40
  }
54
- async extract({ project_id, iteration_id, document, documents, temperature, seed, store }, options) {
41
+ async publish(projectId, body, options) {
42
+ const mergedBody = body || options?.body ? { ...(body || {}), ...(options?.body || {}) } : undefined;
43
+ return this._fetchJson(ZProject, {
44
+ url: `/v1/projects/${projectId}/publish`,
45
+ method: "POST",
46
+ body: mergedBody,
47
+ params: options?.params,
48
+ headers: options?.headers,
49
+ });
50
+ }
51
+ async extract({ project_id, iteration_id, document, documents, model, image_resolution_dpi, n_consensus, temperature, seed, store }, options) {
55
52
  if (!document && (!documents || documents.length === 0)) {
56
53
  throw new Error("Either 'document' or 'documents' must be provided.");
57
54
  }
@@ -60,6 +57,12 @@ export default class APIProjects extends CompositionClient {
60
57
  const bodyParams = {
61
58
  documents: (await ZMIMEData.array().parseAsync([...document ? [document] : [], ...(documents || [])])).map(mimeToBlob)
62
59
  };
60
+ if (model !== undefined)
61
+ bodyParams.model = model;
62
+ if (image_resolution_dpi !== undefined)
63
+ bodyParams.image_resolution_dpi = image_resolution_dpi;
64
+ if (n_consensus !== undefined)
65
+ bodyParams.n_consensus = n_consensus;
63
66
  if (temperature !== undefined)
64
67
  bodyParams.temperature = temperature;
65
68
  if (seed !== undefined)
package/dist/client.d.ts CHANGED
@@ -24,12 +24,16 @@ export declare class APIError extends Error {
24
24
  info: string;
25
25
  constructor(status: number, info: string);
26
26
  }
27
+ export declare class SignatureVerificationError extends Error {
28
+ constructor(message: string);
29
+ }
27
30
  export declare const DateOrISO: z.ZodUnion<[z.ZodDate, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, Date, string>]>;
28
31
  type AuthTypes = {
29
32
  apiKey: string;
30
33
  } | {};
31
34
  export type ClientOptions = {
32
35
  baseUrl?: string;
36
+ timeout?: number;
33
37
  } & AuthTypes;
34
38
  export type RequestOptions = {
35
39
  params?: Record<string, any>;
@@ -38,6 +42,7 @@ export type RequestOptions = {
38
42
  };
39
43
  export declare class FetcherClient extends AbstractClient {
40
44
  options: ClientOptions;
45
+ timeout: number;
41
46
  constructor(options?: ClientOptions);
42
47
  _fetch(params: {
43
48
  url: string;
@@ -47,6 +52,33 @@ export declare class FetcherClient extends AbstractClient {
47
52
  bodyMime?: "application/json" | "multipart/form-data";
48
53
  body?: Record<string, any>;
49
54
  }): Promise<Response>;
55
+ /**
56
+ * Verify the signature of a webhook event.
57
+ *
58
+ * @param eventBody - The raw request body as a string or Buffer
59
+ * @param eventSignature - The signature from the request header (x-retab-signature)
60
+ * @param secret - The webhook secret key used for signing
61
+ * @returns The parsed event payload (JSON)
62
+ * @throws {SignatureVerificationError} If the signature verification fails
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * import { FetcherClient } from './client';
67
+ *
68
+ * // In your webhook handler
69
+ * const secret = "your_webhook_secret";
70
+ * const body = req.body; // Raw string or Buffer
71
+ * const signature = req.headers['x-retab-signature'];
72
+ *
73
+ * try {
74
+ * const event = FetcherClient.verifyEvent(body, signature, secret);
75
+ * console.log(`Verified event: ${event}`);
76
+ * } catch (error) {
77
+ * console.log("Invalid signature!");
78
+ * }
79
+ * ```
80
+ */
81
+ static verifyEvent(eventBody: string | Buffer, eventSignature: string, secret: string): any;
50
82
  }
51
83
  export {};
52
84
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,kBAAkB,GAAG,qBAAqB,CAAC;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAyCF,qBAAa,cAAc;IACzB,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;cAGnC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;cAC9C,UAAU,CAAC,SAAS,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;cAc9H,YAAY,CAAC,SAAS,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;CAQ/J;AAED,qBAAa,iBAAkB,SAAQ,cAAc;IACnD,SAAS,CAAC,OAAO,EAAE,cAAc,CAAC;gBACtB,MAAM,EAAE,cAAc;IAIlC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGzD;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;gBACD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAKzC;AAED,eAAO,MAAM,SAAS,gGAKpB,CAAC;AAEH,KAAK,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,EAAE,CAAC;AACzC,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,SAAS,CAAC;AAEd,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,CAAC;AAEF,qBAAa,aAAc,SAAQ,cAAc;IAC/C,OAAO,EAAE,aAAa,CAAC;gBACX,OAAO,CAAC,EAAE,aAAa;IAe7B,MAAM,CAAC,MAAM,EAAE;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,QAAQ,CAAC,EAAE,kBAAkB,GAAG,qBAAqB,CAAC;QACtD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC5B,GAAG,OAAO,CAAC,QAAQ,CAAC;CA2CtB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as z from "zod";
2
+ import * as crypto from "crypto";
2
3
  async function* streamResponse(schema, response) {
3
4
  let body = "";
4
5
  let depth = 0;
@@ -86,6 +87,12 @@ export class APIError extends Error {
86
87
  this.info = info;
87
88
  }
88
89
  }
90
+ export class SignatureVerificationError extends Error {
91
+ constructor(message) {
92
+ super(message);
93
+ this.name = "SignatureVerificationError";
94
+ }
95
+ }
89
96
  export const DateOrISO = z.union([
90
97
  z.date(),
91
98
  z.string().refine(val => !isNaN(Date.parse(val)), {
@@ -96,6 +103,7 @@ export class FetcherClient extends AbstractClient {
96
103
  constructor(options) {
97
104
  super();
98
105
  this.options = options || {};
106
+ this.timeout = this.options.timeout ?? 1800000; // Default 1800 seconds (in milliseconds)
99
107
  // Validate that at least one authentication method is provided
100
108
  const apiKey = "apiKey" in this.options ? this.options.apiKey : process.env["RETAB_API_KEY"];
101
109
  if (!apiKey) {
@@ -137,10 +145,49 @@ export class FetcherClient extends AbstractClient {
137
145
  const apiKey = "apiKey" in this.options ? this.options.apiKey : process.env["RETAB_API_KEY"];
138
146
  headers["Api-Key"] = apiKey;
139
147
  init.headers = headers;
148
+ init.signal = AbortSignal.timeout(this.timeout);
140
149
  let res = await fetch(url, init);
141
150
  if (!res.ok) {
142
151
  throw new APIError(res.status, await res.text());
143
152
  }
144
153
  return res;
145
154
  }
155
+ /**
156
+ * Verify the signature of a webhook event.
157
+ *
158
+ * @param eventBody - The raw request body as a string or Buffer
159
+ * @param eventSignature - The signature from the request header (x-retab-signature)
160
+ * @param secret - The webhook secret key used for signing
161
+ * @returns The parsed event payload (JSON)
162
+ * @throws {SignatureVerificationError} If the signature verification fails
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * import { FetcherClient } from './client';
167
+ *
168
+ * // In your webhook handler
169
+ * const secret = "your_webhook_secret";
170
+ * const body = req.body; // Raw string or Buffer
171
+ * const signature = req.headers['x-retab-signature'];
172
+ *
173
+ * try {
174
+ * const event = FetcherClient.verifyEvent(body, signature, secret);
175
+ * console.log(`Verified event: ${event}`);
176
+ * } catch (error) {
177
+ * console.log("Invalid signature!");
178
+ * }
179
+ * ```
180
+ */
181
+ static verifyEvent(eventBody, eventSignature, secret) {
182
+ const bodyBuffer = typeof eventBody === 'string' ? Buffer.from(eventBody, 'utf-8') : eventBody;
183
+ const expectedSignature = crypto
184
+ .createHmac('sha256', secret)
185
+ .update(bodyBuffer)
186
+ .digest('hex');
187
+ // Use constant-time comparison to prevent timing attacks
188
+ if (!crypto.timingSafeEqual(Buffer.from(eventSignature), Buffer.from(expectedSignature))) {
189
+ throw new SignatureVerificationError("Invalid signature");
190
+ }
191
+ return JSON.parse(bodyBuffer.toString('utf-8'));
192
+ }
146
193
  }