@abyss-project/tools 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.eslintrc.js +33 -33
  2. package/.prettierrc +5 -5
  3. package/dist/api/atlas-group.api.js.map +1 -1
  4. package/dist/api/atlas-page.api.js.map +1 -1
  5. package/dist/api/atlas.api.js +3 -3
  6. package/dist/api/atlas.api.js.map +1 -1
  7. package/dist/api/atlas.public.api.js.map +1 -1
  8. package/dist/api/board-card.api.js.map +1 -1
  9. package/dist/api/board-column.api.js.map +1 -1
  10. package/dist/api/board-label.api.js +1 -2
  11. package/dist/api/board-label.api.js.map +1 -1
  12. package/dist/api/board.api.js +7 -5
  13. package/dist/api/board.api.js.map +1 -1
  14. package/dist/api/draw.api.js +1 -2
  15. package/dist/api/draw.api.js.map +1 -1
  16. package/dist/api/draw.public.api.js.map +1 -1
  17. package/dist/api/extraction-plan-preset.api.js +19 -6
  18. package/dist/api/extraction-plan-preset.api.js.map +1 -1
  19. package/dist/api/extraction-settings.api.js.map +1 -1
  20. package/dist/api/extraction.api.d.ts +2 -1
  21. package/dist/api/extraction.api.js +43 -82
  22. package/dist/api/extraction.api.js.map +1 -1
  23. package/dist/api/extraction.application.api.d.ts +4 -0
  24. package/dist/api/extraction.application.api.js +30 -0
  25. package/dist/api/extraction.application.api.js.map +1 -0
  26. package/dist/api/index.d.ts +1 -0
  27. package/dist/api/index.js +1 -0
  28. package/dist/api/index.js.map +1 -1
  29. package/dist/api/retrospective.api.js +2 -1
  30. package/dist/api/retrospective.api.js.map +1 -1
  31. package/dist/api/short-link.admin.api.js.map +1 -1
  32. package/dist/api/short-link.api.js.map +1 -1
  33. package/dist/api/short-link.public.api.js.map +1 -1
  34. package/dist/api/tag.api.js.map +1 -1
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.js +4 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/server-sent-events/server-sent-events.manager.d.ts +1 -2
  39. package/dist/server-sent-events/server-sent-events.manager.js +1 -2
  40. package/dist/server-sent-events/server-sent-events.manager.js.map +1 -1
  41. package/dist/types/enum/extraction-warning.enum.d.ts +2 -1
  42. package/dist/types/enum/extraction-warning.enum.js +1 -0
  43. package/dist/types/enum/extraction-warning.enum.js.map +1 -1
  44. package/dist/types/enum/rcb-bank.enum.js +252 -36
  45. package/dist/types/enum/rcb-bank.enum.js.map +1 -1
  46. package/dist/types/interface/models/extraction-fetched-provenance.dto.test.js +8 -2
  47. package/dist/types/interface/models/extraction-fetched-provenance.dto.test.js.map +1 -1
  48. package/dist/types/interface/models/extraction-plan.dto.test.js +6 -1
  49. package/dist/types/interface/models/extraction-plan.dto.test.js.map +1 -1
  50. package/dist/types/interface/models/extraction-spatial-template.dto.test.js +20 -3
  51. package/dist/types/interface/models/extraction-spatial-template.dto.test.js.map +1 -1
  52. package/package.json +4 -1
  53. package/src/api/atlas-group.api.ts +4 -1
  54. package/src/api/atlas-page.api.ts +12 -6
  55. package/src/api/atlas.api.ts +12 -7
  56. package/src/api/atlas.public.api.ts +3 -1
  57. package/src/api/board-card.api.ts +6 -3
  58. package/src/api/board-column.api.ts +4 -1
  59. package/src/api/board-label.api.ts +7 -3
  60. package/src/api/board.api.ts +19 -10
  61. package/src/api/draw.api.ts +16 -12
  62. package/src/api/draw.public.api.ts +3 -1
  63. package/src/api/extraction-plan-preset.api.ts +64 -18
  64. package/src/api/extraction-settings.api.ts +2 -8
  65. package/src/api/extraction.api.ts +64 -84
  66. package/src/api/extraction.application.api.ts +79 -0
  67. package/src/api/index.ts +1 -0
  68. package/src/api/retrospective.api.ts +8 -5
  69. package/src/api/short-link.admin.api.ts +14 -4
  70. package/src/api/short-link.api.ts +39 -17
  71. package/src/api/short-link.public.api.ts +25 -11
  72. package/src/api/tag.api.ts +3 -1
  73. package/src/index.ts +17 -0
  74. package/src/server-sent-events/server-sent-events.manager.ts +5 -3
  75. package/src/types/enum/extraction-warning.enum.ts +16 -0
  76. package/src/types/enum/rcb-bank.enum.ts +255 -41
  77. package/src/types/interface/api/responses/retrospective.response.ts +2 -1
  78. package/src/types/interface/models/content-share.dto.ts +5 -1
  79. package/src/types/interface/models/extraction-fetched-provenance.dto.test.ts +20 -9
  80. package/src/types/interface/models/extraction-plan.dto.test.ts +17 -4
  81. package/src/types/interface/models/extraction-spatial-template.dto.test.ts +26 -5
  82. package/src/types/interface/models/probe-webhook.dto.ts +3 -4
  83. package/tsconfig.check.json +15 -0
  84. package/dist/api/memo.api.d.ts +0 -15
  85. package/dist/api/memo.api.js +0 -49
  86. package/dist/api/memo.api.js.map +0 -1
  87. package/dist/constants/memo.constants.d.ts +0 -3
  88. package/dist/constants/memo.constants.js +0 -7
  89. package/dist/constants/memo.constants.js.map +0 -1
  90. package/dist/types/interface/api/requests/memo.request.d.ts +0 -41
  91. package/dist/types/interface/api/requests/memo.request.js +0 -3
  92. package/dist/types/interface/api/requests/memo.request.js.map +0 -1
  93. package/dist/types/interface/api/responses/memo.response.d.ts +0 -27
  94. package/dist/types/interface/api/responses/memo.response.js +0 -3
  95. package/dist/types/interface/api/responses/memo.response.js.map +0 -1
  96. package/dist/types/interface/models/memo.dto.d.ts +0 -20
  97. package/dist/types/interface/models/memo.dto.js +0 -3
  98. package/dist/types/interface/models/memo.dto.js.map +0 -1
@@ -31,7 +31,8 @@ export const getBoard = async (params: IGetBoardParams): Promise<IGetBoardRespon
31
31
  export const getBySlugBoard = async (
32
32
  params: IGetBySlugBoardParams,
33
33
  ): Promise<IGetBySlugBoardResponse> => {
34
- return (await AbyssToolsCore.axios.get<IGetBySlugBoardResponse>(`board/slug/${params.slug}`)).data;
34
+ return (await AbyssToolsCore.axios.get<IGetBySlugBoardResponse>(`board/slug/${params.slug}`))
35
+ .data;
35
36
  };
36
37
 
37
38
  export const deleteBoard = async (params: IDeleteBoardParams): Promise<IDeleteBoardResponse> => {
@@ -42,7 +43,8 @@ export const updateBoard = async (
42
43
  params: IUpdateBoardParams,
43
44
  body: IUpdateBoardBody,
44
45
  ): Promise<IUpdateBoardResponse> => {
45
- return (await AbyssToolsCore.axios.put<IUpdateBoardResponse>(`board/${params.boardId}`, body)).data;
46
+ return (await AbyssToolsCore.axios.put<IUpdateBoardResponse>(`board/${params.boardId}`, body))
47
+ .data;
46
48
  };
47
49
 
48
50
  export const createBoard = async (body: ICreateBoardBody): Promise<ICreateBoardResponse> => {
@@ -54,12 +56,18 @@ export const createForOrganizationBoard = async (
54
56
  body: ICreateBoardBody,
55
57
  ): Promise<ICreateBoardResponse> => {
56
58
  return (
57
- await AbyssToolsCore.axios.post<ICreateBoardResponse>(`board/organization/${params.organizationId}`, body)
59
+ await AbyssToolsCore.axios.post<ICreateBoardResponse>(
60
+ `board/organization/${params.organizationId}`,
61
+ body,
62
+ )
58
63
  ).data;
59
64
  };
60
65
 
61
- export const listBoard = async (query?: { sharedWithMe?: boolean }): Promise<IListBoardUserResponse> => {
62
- return (await AbyssToolsCore.axios.get<IListBoardUserResponse>('board/list', { params: query })).data;
66
+ export const listBoard = async (query?: {
67
+ sharedWithMe?: boolean;
68
+ }): Promise<IListBoardUserResponse> => {
69
+ return (await AbyssToolsCore.axios.get<IListBoardUserResponse>('board/list', { params: query }))
70
+ .data;
63
71
  };
64
72
 
65
73
  export const listBoardProject = async (params: {
@@ -71,8 +79,9 @@ export const listBoardProject = async (params: {
71
79
  };
72
80
 
73
81
  export const getNextCardPreviewBoard = async (): Promise<IGetNextCardPreviewBoardResponse> => {
74
- return (await AbyssToolsCore.axios.post<IGetNextCardPreviewBoardResponse>(`board/next-card-preview`))
75
- .data;
82
+ return (
83
+ await AbyssToolsCore.axios.post<IGetNextCardPreviewBoardResponse>(`board/next-card-preview`)
84
+ ).data;
76
85
  };
77
86
 
78
87
  export const listBoardOrganization = async (
@@ -111,9 +120,9 @@ export const transferBoard = async (
111
120
  ).data;
112
121
  };
113
122
 
114
- export const countOrganizationBoard = async (
115
- params: { organizationId: string },
116
- ): Promise<ICountOrganizationResponse> => {
123
+ export const countOrganizationBoard = async (params: {
124
+ organizationId: string;
125
+ }): Promise<ICountOrganizationResponse> => {
117
126
  return (
118
127
  await AbyssToolsCore.axios.get<ICountOrganizationResponse>(
119
128
  `board/organization/${params.organizationId}/count`,
@@ -29,9 +29,12 @@ export const paginateProjectDraw = async (
29
29
  query: IPaginateDrawQuery,
30
30
  ): Promise<IPaginateDrawResponse> => {
31
31
  return (
32
- await AbyssToolsCore.axios.get<IPaginateDrawResponse>(`project/${params.projectId}/draw/paginate`, {
33
- params: query,
34
- })
32
+ await AbyssToolsCore.axios.get<IPaginateDrawResponse>(
33
+ `project/${params.projectId}/draw/paginate`,
34
+ {
35
+ params: query,
36
+ },
37
+ )
35
38
  ).data;
36
39
  };
37
40
 
@@ -58,8 +61,12 @@ export const createForOrganizationDraw = async (
58
61
  params: ICreateForOrganizationDrawParams,
59
62
  body: ICreateDrawBody,
60
63
  ): Promise<ICreateDrawResponse> => {
61
- return (await AbyssToolsCore.axios.post<ICreateDrawResponse>(`draw/organization/${params.organizationId}`, body))
62
- .data;
64
+ return (
65
+ await AbyssToolsCore.axios.post<ICreateDrawResponse>(
66
+ `draw/organization/${params.organizationId}`,
67
+ body,
68
+ )
69
+ ).data;
63
70
  };
64
71
 
65
72
  export const paginateOrganizationDraw = async (
@@ -79,16 +86,13 @@ export const transferDraw = async (
79
86
  body: ITransferContentBody,
80
87
  ): Promise<ITransferDrawResponse> => {
81
88
  return (
82
- await AbyssToolsCore.axios.post<ITransferDrawResponse>(
83
- `draw/${params.drawId}/transfer`,
84
- body,
85
- )
89
+ await AbyssToolsCore.axios.post<ITransferDrawResponse>(`draw/${params.drawId}/transfer`, body)
86
90
  ).data;
87
91
  };
88
92
 
89
- export const countOrganizationDraw = async (
90
- params: { organizationId: string },
91
- ): Promise<ICountOrganizationResponse> => {
93
+ export const countOrganizationDraw = async (params: {
94
+ organizationId: string;
95
+ }): Promise<ICountOrganizationResponse> => {
92
96
  return (
93
97
  await AbyssToolsCore.axios.get<ICountOrganizationResponse>(
94
98
  `draw/organization/${params.organizationId}/count`,
@@ -15,7 +15,9 @@ export const getDrawPublic = async (
15
15
  params: IGetDrawPublicParams,
16
16
  ): Promise<IGetDrawPublicResponse> => {
17
17
  return (
18
- await AbyssToolsCore.axios.get<IGetDrawPublicResponse>(`draw/public/${params.publicAccessToken}`)
18
+ await AbyssToolsCore.axios.get<IGetDrawPublicResponse>(
19
+ `draw/public/${params.publicAccessToken}`,
20
+ )
19
21
  ).data;
20
22
  };
21
23
 
@@ -11,11 +11,7 @@
11
11
  // ──────────────────────────────────────────────
12
12
 
13
13
  import { AbyssToolsCore } from '..';
14
- import type {
15
- ICreatePlanPresetBody,
16
- IUpdatePlanPresetBody,
17
- IListPlanPresetQuery,
18
- } from '../types';
14
+ import type { ICreatePlanPresetBody, IUpdatePlanPresetBody, IListPlanPresetQuery } from '../types';
19
15
  import type {
20
16
  IListModesResponse,
21
17
  IListPlanPresetsResponse,
@@ -30,22 +26,72 @@ function authHeader(apiKey?: string): Record<string, string> {
30
26
  }
31
27
 
32
28
  export const listModes = async (apiKey?: string): Promise<IListModesResponse> =>
33
- (await AbyssToolsCore.axios.get<IListModesResponse>(`extract/modes`, { headers: authHeader(apiKey) })).data;
29
+ (
30
+ await AbyssToolsCore.axios.get<IListModesResponse>(`extract/modes`, {
31
+ headers: authHeader(apiKey),
32
+ })
33
+ ).data;
34
34
 
35
- export const listExtractionPlanPresets = async (query: IListPlanPresetQuery = {}, apiKey?: string): Promise<IListPlanPresetsResponse> =>
36
- (await AbyssToolsCore.axios.get<IListPlanPresetsResponse>(`extract/plans`, { params: query, headers: authHeader(apiKey) })).data;
35
+ export const listExtractionPlanPresets = async (
36
+ query: IListPlanPresetQuery = {},
37
+ apiKey?: string,
38
+ ): Promise<IListPlanPresetsResponse> =>
39
+ (
40
+ await AbyssToolsCore.axios.get<IListPlanPresetsResponse>(`extract/plans`, {
41
+ params: query,
42
+ headers: authHeader(apiKey),
43
+ })
44
+ ).data;
37
45
 
38
- export const getExtractionPlanPreset = async (id: string, apiKey?: string): Promise<IGetPlanPresetResponse> =>
39
- (await AbyssToolsCore.axios.get<IGetPlanPresetResponse>(`extract/plans/${id}`, { headers: authHeader(apiKey) })).data;
46
+ export const getExtractionPlanPreset = async (
47
+ id: string,
48
+ apiKey?: string,
49
+ ): Promise<IGetPlanPresetResponse> =>
50
+ (
51
+ await AbyssToolsCore.axios.get<IGetPlanPresetResponse>(`extract/plans/${id}`, {
52
+ headers: authHeader(apiKey),
53
+ })
54
+ ).data;
40
55
 
41
- export const createExtractionPlanPreset = async (body: ICreatePlanPresetBody, apiKey?: string): Promise<IGetPlanPresetResponse> =>
42
- (await AbyssToolsCore.axios.post<IGetPlanPresetResponse>(`extract/plans`, body, { headers: authHeader(apiKey) })).data;
56
+ export const createExtractionPlanPreset = async (
57
+ body: ICreatePlanPresetBody,
58
+ apiKey?: string,
59
+ ): Promise<IGetPlanPresetResponse> =>
60
+ (
61
+ await AbyssToolsCore.axios.post<IGetPlanPresetResponse>(`extract/plans`, body, {
62
+ headers: authHeader(apiKey),
63
+ })
64
+ ).data;
43
65
 
44
- export const updateExtractionPlanPreset = async (id: string, body: IUpdatePlanPresetBody, apiKey?: string): Promise<IGetPlanPresetResponse> =>
45
- (await AbyssToolsCore.axios.put<IGetPlanPresetResponse>(`extract/plans/${id}`, body, { headers: authHeader(apiKey) })).data;
66
+ export const updateExtractionPlanPreset = async (
67
+ id: string,
68
+ body: IUpdatePlanPresetBody,
69
+ apiKey?: string,
70
+ ): Promise<IGetPlanPresetResponse> =>
71
+ (
72
+ await AbyssToolsCore.axios.put<IGetPlanPresetResponse>(`extract/plans/${id}`, body, {
73
+ headers: authHeader(apiKey),
74
+ })
75
+ ).data;
46
76
 
47
- export const deleteExtractionPlanPreset = async (id: string, apiKey?: string): Promise<IDeletePlanPresetResponse> =>
48
- (await AbyssToolsCore.axios.delete<IDeletePlanPresetResponse>(`extract/plans/${id}`, { headers: authHeader(apiKey) })).data;
77
+ export const deleteExtractionPlanPreset = async (
78
+ id: string,
79
+ apiKey?: string,
80
+ ): Promise<IDeletePlanPresetResponse> =>
81
+ (
82
+ await AbyssToolsCore.axios.delete<IDeletePlanPresetResponse>(`extract/plans/${id}`, {
83
+ headers: authHeader(apiKey),
84
+ })
85
+ ).data;
49
86
 
50
- export const setDefaultExtractionPlanPreset = async (id: string, apiKey?: string): Promise<IGetPlanPresetResponse> =>
51
- (await AbyssToolsCore.axios.post<IGetPlanPresetResponse>(`extract/plans/${id}/default`, {}, { headers: authHeader(apiKey) })).data;
87
+ export const setDefaultExtractionPlanPreset = async (
88
+ id: string,
89
+ apiKey?: string,
90
+ ): Promise<IGetPlanPresetResponse> =>
91
+ (
92
+ await AbyssToolsCore.axios.post<IGetPlanPresetResponse>(
93
+ `extract/plans/${id}/default`,
94
+ {},
95
+ { headers: authHeader(apiKey) },
96
+ )
97
+ ).data;
@@ -9,14 +9,8 @@
9
9
  // ──────────────────────────────────────────────
10
10
 
11
11
  import { AbyssToolsCore } from '..';
12
- import type {
13
- IGetExtractionSettingsQuery,
14
- IUpsertExtractionSettingsBody,
15
- } from '../types';
16
- import type {
17
- IGetExtractionSettingsResponse,
18
- IUpsertExtractionSettingsResponse,
19
- } from '../types';
12
+ import type { IGetExtractionSettingsQuery, IUpsertExtractionSettingsBody } from '../types';
13
+ import type { IGetExtractionSettingsResponse, IUpsertExtractionSettingsResponse } from '../types';
20
14
 
21
15
  const DEFAULT_API_KEY_HEADER = 'abyss-api-key';
22
16
 
@@ -3,6 +3,11 @@
3
3
  //
4
4
  // Sync helper accepts an inline PDF buffer (multipart) or a storage_file_id.
5
5
  // Queue helper enqueues for async processing + webhook delivery.
6
+ //
7
+ // Multipart bodies are built with the global `FormData` + `Blob` — the same
8
+ // code in a browser and on Node (>= 18, see package.json `engines`). axios
9
+ // serializes a spec-compliant FormData on both and sets the multipart
10
+ // boundary itself, so no content-type header is ever set by hand.
6
11
  // ──────────────────────────────────────────────
7
12
 
8
13
  import { AbyssToolsCore } from '..';
@@ -92,16 +97,9 @@ function authHeader(apiKey?: string): Record<string, string> {
92
97
  return apiKey ? { [DEFAULT_API_KEY_HEADER]: apiKey } : {};
93
98
  }
94
99
 
95
- async function loadFormData(): Promise<any> {
96
- if (typeof FormData !== 'undefined' && typeof window !== 'undefined') {
97
- return FormData;
98
- }
99
- // Use indirect eval so bundlers (esbuild/vite/webpack) do not try to
100
- // resolve the Node-only "form-data" package when this file is included
101
- // in a browser bundle. The require only runs at runtime on Node.
102
- // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
103
- const nodeRequire: NodeRequire = new Function('return require')();
104
- return nodeRequire('form-data');
100
+ /** The PDF bytes as a multipart file part: filename kept, `application/pdf` type. */
101
+ function appendPdfPart(form: FormData, pdf: Buffer | Uint8Array, filename: string): void {
102
+ form.append('file', new Blob([pdf], { type: 'application/pdf' }), filename);
105
103
  }
106
104
 
107
105
  function serializeParams<T extends object>(o: T): Record<string, unknown> {
@@ -124,18 +122,22 @@ export interface ExtractCallOptions {
124
122
  idempotencyKey?: string;
125
123
  }
126
124
 
125
+ /**
126
+ * `POST /extract/extract` with `processing: 'sync'`. The route still moves an
127
+ * async plan (an OCR pass, or more than one pass — inline or reached through
128
+ * `plan_preset_id`) onto the queue and answers 202 with the queue body, so the
129
+ * call resolves either the extraction (`extraction_id`) or the queued job
130
+ * (`job_id`); narrow on `'job_id' in response.data`.
131
+ */
127
132
  export const extractSync = async (
128
133
  body: IExtractBody,
129
134
  scope: IExtractionContentScopeQuery = {},
130
135
  apiKey?: string,
131
136
  opts: ExtractCallOptions = {},
132
- ): Promise<IExtractSyncResponse> => {
133
- return postExtract(
134
- { ...body, processing: 'sync' },
135
- scope,
136
- apiKey,
137
- opts,
138
- ) as Promise<IExtractSyncResponse>;
137
+ ): Promise<IExtractSyncResponse | IExtractQueueResponse> => {
138
+ return postExtract({ ...body, processing: 'sync' }, scope, apiKey, opts) as Promise<
139
+ IExtractSyncResponse | IExtractQueueResponse
140
+ >;
139
141
  };
140
142
 
141
143
  export const extractQueue = async (
@@ -164,54 +166,11 @@ const postExtract = async (
164
166
  }
165
167
  const params = serializeParams(scope);
166
168
  if (body.pdf) {
167
- const isBrowser = typeof window !== 'undefined' && typeof FormData !== 'undefined';
168
- const filename = body.pdf_filename ?? 'document.pdf';
169
- if (isBrowser) {
170
- const form = new FormData();
171
- const blob = new Blob([body.pdf as Uint8Array], { type: 'application/pdf' });
172
- form.append('file', blob, filename);
173
- appendIf(form, 'mode', body.mode);
174
- if (body.hint) form.append('hint', JSON.stringify(body.hint));
175
- appendIf(form, 'template_id', body.template_id);
176
- if (body.template) form.append('template', JSON.stringify(body.template));
177
- appendIf(form, 'user_external_id', body.user_external_id);
178
- if (body.user_metadata) form.append('user_metadata', JSON.stringify(body.user_metadata));
179
- if (typeof body.archive === 'boolean') form.append('archive', String(body.archive));
180
- if (typeof body.force === 'boolean') form.append('force', String(body.force));
181
- if (body.processing) form.append('processing', body.processing);
182
- return (
183
- await AbyssToolsCore.axios.post(`extract/extract`, form, {
184
- params,
185
- headers: { ...authHeader(apiKey), ...extraHeaders },
186
- maxBodyLength: Infinity,
187
- maxContentLength: Infinity,
188
- })
189
- ).data;
190
- }
191
- const FormDataCtor = await loadFormData();
192
- const form = new FormDataCtor();
193
- const buf = Buffer.isBuffer(body.pdf) ? body.pdf : Buffer.from(body.pdf);
194
- form.append('file', buf, {
195
- filename,
196
- contentType: 'application/pdf',
197
- });
198
- appendIf(form, 'mode', body.mode);
199
- if (body.hint) form.append('hint', JSON.stringify(body.hint));
200
- appendIf(form, 'template_id', body.template_id);
201
- if (body.template) form.append('template', JSON.stringify(body.template));
202
- appendIf(form, 'user_external_id', body.user_external_id);
203
- if (body.user_metadata) form.append('user_metadata', JSON.stringify(body.user_metadata));
204
- if (typeof body.archive === 'boolean') form.append('archive', String(body.archive));
205
- if (typeof body.force === 'boolean') form.append('force', String(body.force));
206
- if (body.processing) form.append('processing', body.processing);
169
+ const form = buildExtractMultipart(body);
207
170
  return (
208
171
  await AbyssToolsCore.axios.post(`extract/extract`, form, {
209
172
  params,
210
- headers: {
211
- ...(typeof form.getHeaders === 'function' ? form.getHeaders() : {}),
212
- ...authHeader(apiKey),
213
- ...extraHeaders,
214
- },
173
+ headers: { ...authHeader(apiKey), ...extraHeaders },
215
174
  maxBodyLength: Infinity,
216
175
  maxContentLength: Infinity,
217
176
  })
@@ -228,11 +187,50 @@ const postExtract = async (
228
187
  ).data;
229
188
  };
230
189
 
231
- function appendIf(form: any, key: string, value: unknown): void {
190
+ function appendIf(form: FormData, key: string, value: unknown): void {
232
191
  if (value === undefined || value === null) return;
233
192
  form.append(key, String(value));
234
193
  }
235
194
 
195
+ /** Append the shared extract text fields (all optional) onto a multipart form. */
196
+ function appendExtractFields(form: FormData, body: IExtractBody): void {
197
+ appendIf(form, 'mode', body.mode);
198
+ if (body.hint) form.append('hint', JSON.stringify(body.hint));
199
+ appendIf(form, 'template_id', body.template_id);
200
+ if (body.template) form.append('template', JSON.stringify(body.template));
201
+ appendIf(form, 'user_external_id', body.user_external_id);
202
+ if (body.user_metadata) form.append('user_metadata', JSON.stringify(body.user_metadata));
203
+ if (typeof body.archive === 'boolean') form.append('archive', String(body.archive));
204
+ if (typeof body.force === 'boolean') form.append('force', String(body.force));
205
+ if (body.processing) form.append('processing', body.processing);
206
+ if (body.plan) form.append('plan', JSON.stringify(body.plan));
207
+ appendIf(form, 'plan_preset_id', body.plan_preset_id);
208
+ // Wire name read by the API's parseExtractRequest ('true' / 'false').
209
+ if (typeof body.improvementConsent === 'boolean') {
210
+ form.append('improvement_consent', String(body.improvementConsent));
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Build the multipart form for an extract upload, shared by the personal and
216
+ * application extract clients: the PDF as the `file` part (filename from
217
+ * `body.pdf_filename`, default `document.pdf`) plus the extract text fields.
218
+ * Scope params + auth headers are added by the caller; axios sets the
219
+ * multipart content type. Requires `body.pdf` to be set.
220
+ */
221
+ export function buildExtractMultipart(body: IExtractBody): FormData {
222
+ const pdf = body.pdf;
223
+ if (!pdf) {
224
+ // Precondition: only the multipart-upload path calls this (both callers
225
+ // guard on `body.pdf` first). Enforced here so the helper is self-contained.
226
+ throw new Error('buildExtractMultipart requires body.pdf');
227
+ }
228
+ const form = new FormData();
229
+ appendPdfPart(form, pdf, body.pdf_filename ?? 'document.pdf');
230
+ appendExtractFields(form, body);
231
+ return form;
232
+ }
233
+
236
234
  export const extractBulk = async (
237
235
  body: IExtractBulkBody,
238
236
  scope: IExtractionContentScopeQuery = {},
@@ -253,29 +251,11 @@ export const detect = async (
253
251
  pdfFilename = 'document.pdf',
254
252
  apiKey?: string,
255
253
  ): Promise<IDetectResponse> => {
256
- const isBrowser = typeof window !== 'undefined' && typeof FormData !== 'undefined';
257
- if (isBrowser) {
258
- const form = new FormData();
259
- const blob = new Blob([pdf as Uint8Array], { type: 'application/pdf' });
260
- form.append('file', blob, pdfFilename);
261
- return (
262
- await AbyssToolsCore.axios.post<IDetectResponse>(`extract/detect`, form, {
263
- headers: authHeader(apiKey),
264
- maxBodyLength: Infinity,
265
- maxContentLength: Infinity,
266
- })
267
- ).data;
268
- }
269
- const FormDataCtor = await loadFormData();
270
- const form = new FormDataCtor();
271
- const buf = Buffer.isBuffer(pdf) ? pdf : Buffer.from(pdf);
272
- form.append('file', buf, { filename: pdfFilename, contentType: 'application/pdf' });
254
+ const form = new FormData();
255
+ appendPdfPart(form, pdf, pdfFilename);
273
256
  return (
274
257
  await AbyssToolsCore.axios.post<IDetectResponse>(`extract/detect`, form, {
275
- headers: {
276
- ...(typeof form.getHeaders === 'function' ? form.getHeaders() : {}),
277
- ...authHeader(apiKey),
278
- },
258
+ headers: authHeader(apiKey),
279
259
  maxBodyLength: Infinity,
280
260
  maxContentLength: Infinity,
281
261
  })
@@ -0,0 +1,79 @@
1
+ // ──────────────────────────────────────────────
2
+ // AbyssExtract APPLICATION extract API — POST
3
+ // /extract/application/:applicationId/extract. Mirrors
4
+ // apps/api/src/api/controllers/extraction-extract-application.controller.ts
5
+ // EXACTLY — the application counterpart of extraction.api.ts's `extractSync`,
6
+ // for inter-service callers (e.g. AbyssBanking) authenticating with a project
7
+ // API key instead of a personal/org session.
8
+ //
9
+ // Reuses extraction.api.ts's `buildExtractMultipart` (the same global
10
+ // `FormData` + `Blob` multipart body as the personal client) and every
11
+ // request/response TYPE verbatim (`IExtractBody`, `IExtractSyncResponse`) —
12
+ // never redefined here. There is NO scope query param: unlike the
13
+ // personal/org route's `?organization_id=`, an application caller's scope is
14
+ // entirely its `:applicationId` URL segment, resolved server-side by
15
+ // ApplicationMiddleware (round-trips to abyss-main).
16
+ //
17
+ // `authHeaderApplication` is kept LOCAL and unexported, same convention as the
18
+ // personal client's own `authHeader` — it sends `API_KEY_APPLICATION_HEADER`
19
+ // (`abyss-api-key-application`) instead of the personal `abyss-api-key`.
20
+ //
21
+ // The endpoint is SYNCHRONOUS (the machine caller gets the extraction inline —
22
+ // no queue), so the return is always the `IExtractSyncResponse` shape.
23
+ // ──────────────────────────────────────────────
24
+
25
+ import { AbyssToolsCore, API_KEY_APPLICATION_HEADER } from '..';
26
+ import type { IExtractBody } from '../types';
27
+ import type { IExtractSyncResponse } from '../types';
28
+ import { buildExtractMultipart, ExtractCallOptions } from './extraction.api';
29
+
30
+ function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
31
+ return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
32
+ }
33
+
34
+ /**
35
+ * `POST /extract/application/:applicationId/extract` — run one synchronous
36
+ * extraction OWNED by `applicationId` and BILLED to its resolved owning
37
+ * organization. The route accepts only a multipart PDF upload: send
38
+ * `body.pdf`, with `body.mode` `auto` or `hint`; `body.plan` (inline, single
39
+ * pass, no OCR, no template pass) and `body.improvementConsent` travel as
40
+ * multipart fields. The route refuses `storage_file_id`, `plan_preset_id`,
41
+ * `template_id` and `template` with a 4xx. A body without `pdf` is sent as
42
+ * JSON and never runs an extraction: the memory watchdog may answer 503 before
43
+ * the route reads the body; past it, the route answers a 4xx — the
44
+ * `storage_file_id` refusal when the body carries one, otherwise "Provide file
45
+ * (multipart pdf)". `options` forwards a Stripe-style
46
+ * `Idempotency-Key` header (24h replay window) when set.
47
+ */
48
+ export const runExtractionApplication = async (
49
+ applicationId: string,
50
+ body: IExtractBody,
51
+ options: ExtractCallOptions = {},
52
+ applicationApiKey?: string,
53
+ ): Promise<IExtractSyncResponse> => {
54
+ const extraHeaders: Record<string, string> = {};
55
+ if (options.idempotencyKey) {
56
+ extraHeaders['Idempotency-Key'] = options.idempotencyKey;
57
+ }
58
+ const url = `extract/application/${applicationId}/extract`;
59
+
60
+ if (body.pdf) {
61
+ const form = buildExtractMultipart(body);
62
+ return (
63
+ await AbyssToolsCore.axios.post<IExtractSyncResponse>(url, form, {
64
+ headers: { ...authHeaderApplication(applicationApiKey), ...extraHeaders },
65
+ maxBodyLength: Infinity,
66
+ maxContentLength: Infinity,
67
+ })
68
+ ).data;
69
+ }
70
+
71
+ // Strip `pdf` from the JSON body (only used for the multipart upload above).
72
+ const jsonBody: Omit<IExtractBody, 'pdf'> = { ...body };
73
+ delete (jsonBody as { pdf?: unknown }).pdf;
74
+ return (
75
+ await AbyssToolsCore.axios.post<IExtractSyncResponse>(url, jsonBody, {
76
+ headers: { ...authHeaderApplication(applicationApiKey), ...extraHeaders },
77
+ })
78
+ ).data;
79
+ };
package/src/api/index.ts CHANGED
@@ -46,6 +46,7 @@ export * from './tag.api';
46
46
 
47
47
  // Extraction
48
48
  export * from './extraction.api';
49
+ export * from './extraction.application.api';
49
50
  export * from './extraction-settings.api';
50
51
  export * from './extraction-plan-preset.api';
51
52
 
@@ -23,7 +23,9 @@ export const getRetrospective = async (
23
23
  params: IGetRetrospectiveParams,
24
24
  ): Promise<IGetRetrospectiveResponse> => {
25
25
  return (
26
- await AbyssToolsCore.axios.get<IGetRetrospectiveResponse>(`retrospective/${params.retrospectiveId}`)
26
+ await AbyssToolsCore.axios.get<IGetRetrospectiveResponse>(
27
+ `retrospective/${params.retrospectiveId}`,
28
+ )
27
29
  ).data;
28
30
  };
29
31
 
@@ -52,7 +54,8 @@ export const paginateRetrospective = async (
52
54
  export const createRetrospective = async (
53
55
  body: ICreateRetrospectiveBody,
54
56
  ): Promise<ICreateRetrospectiveResponse> => {
55
- return (await AbyssToolsCore.axios.post<ICreateRetrospectiveResponse>(`retrospective`, body)).data;
57
+ return (await AbyssToolsCore.axios.post<ICreateRetrospectiveResponse>(`retrospective`, body))
58
+ .data;
56
59
  };
57
60
 
58
61
  export const createForOrganizationRetrospective = async (
@@ -113,9 +116,9 @@ export const transferRetrospective = async (
113
116
  ).data;
114
117
  };
115
118
 
116
- export const countOrganizationRetrospective = async (
117
- params: { organizationId: string },
118
- ): Promise<ICountOrganizationResponse> => {
119
+ export const countOrganizationRetrospective = async (params: {
120
+ organizationId: string;
121
+ }): Promise<ICountOrganizationResponse> => {
119
122
  return (
120
123
  await AbyssToolsCore.axios.get<ICountOrganizationResponse>(
121
124
  `retrospective/organization/${params.organizationId}/count`,
@@ -15,12 +15,17 @@ export const shortLinkAdminApi = {
15
15
  paginate: async (
16
16
  query: IPaginateShortLinkAdminQuery,
17
17
  ): Promise<IPaginateShortLinkAdminResponse> => {
18
- const response = await AbyssToolsCore.axios.get<IPaginateShortLinkAdminResponse>('/admin/short-link/paginate', { params: query });
18
+ const response = await AbyssToolsCore.axios.get<IPaginateShortLinkAdminResponse>(
19
+ '/admin/short-link/paginate',
20
+ { params: query },
21
+ );
19
22
  return response.data;
20
23
  },
21
24
 
22
25
  get: async (params: IGetShortLinkAdminParams): Promise<IGetShortLinkAdminResponse> => {
23
- const response = await AbyssToolsCore.axios.get<IGetShortLinkAdminResponse>(`/admin/short-link/${params.shortLinkId}`);
26
+ const response = await AbyssToolsCore.axios.get<IGetShortLinkAdminResponse>(
27
+ `/admin/short-link/${params.shortLinkId}`,
28
+ );
24
29
  return response.data;
25
30
  },
26
31
 
@@ -28,12 +33,17 @@ export const shortLinkAdminApi = {
28
33
  params: IUpdateShortLinkAdminParams,
29
34
  body: IUpdateShortLinkAdminBody,
30
35
  ): Promise<IUpdateShortLinkAdminResponse> => {
31
- const response = await AbyssToolsCore.axios.put<IUpdateShortLinkAdminResponse>(`/admin/short-link/${params.shortLinkId}`, body);
36
+ const response = await AbyssToolsCore.axios.put<IUpdateShortLinkAdminResponse>(
37
+ `/admin/short-link/${params.shortLinkId}`,
38
+ body,
39
+ );
32
40
  return response.data;
33
41
  },
34
42
 
35
43
  delete: async (params: IDeleteShortLinkAdminParams): Promise<IDeleteShortLinkAdminResponse> => {
36
- const response = await AbyssToolsCore.axios.delete<IDeleteShortLinkAdminResponse>(`/admin/short-link/${params.shortLinkId}`);
44
+ const response = await AbyssToolsCore.axios.delete<IDeleteShortLinkAdminResponse>(
45
+ `/admin/short-link/${params.shortLinkId}`,
46
+ );
37
47
  return response.data;
38
48
  },
39
49
  };