@abyss-project/tools 1.2.0 → 1.3.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 (34) hide show
  1. package/.eslintrc.js +33 -33
  2. package/.prettierrc +5 -5
  3. package/README.md +32 -32
  4. package/dist/api/beam.api.d.ts +2 -1
  5. package/dist/api/beam.api.js +10 -4
  6. package/dist/api/beam.api.js.map +1 -1
  7. package/dist/api/extraction.api.d.ts +5 -5
  8. package/dist/api/extraction.api.js +21 -4
  9. package/dist/api/extraction.api.js.map +1 -1
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +3 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/types/enum/beam.enum.d.ts +4 -0
  14. package/dist/types/enum/beam.enum.js +6 -1
  15. package/dist/types/enum/beam.enum.js.map +1 -1
  16. package/dist/types/interface/api/requests/beam.request.d.ts +16 -0
  17. package/dist/types/interface/api/requests/extraction.request.d.ts +8 -0
  18. package/dist/types/interface/api/responses/beam.response.d.ts +23 -1
  19. package/dist/types/interface/api/responses/extraction.response.d.ts +6 -0
  20. package/dist/types/interface/models/beam.dto.d.ts +13 -1
  21. package/dist/types/interface/models/extraction.dto.d.ts +8 -1
  22. package/dist/types/interface/models/extraction.dto.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/api/beam.api.ts +29 -4
  25. package/src/api/extraction.api.ts +57 -17
  26. package/src/api/tests/beam.api.spec.ts +64 -0
  27. package/src/index.ts +9 -0
  28. package/src/types/enum/beam.enum.ts +8 -0
  29. package/src/types/interface/api/requests/beam.request.ts +27 -2
  30. package/src/types/interface/api/requests/extraction.request.ts +22 -0
  31. package/src/types/interface/api/responses/beam.response.ts +30 -1
  32. package/src/types/interface/api/responses/extraction.response.ts +19 -2
  33. package/src/types/interface/models/beam.dto.ts +20 -1
  34. package/src/types/interface/models/extraction.dto.ts +24 -6
@@ -2,7 +2,7 @@ import { BasePaginate } from '../type-message/base-paginate';
2
2
  import { IResponse } from '../type-message/response';
3
3
  import { IBeamTransfer, IBeamFile, IBeamRecipient } from '../../models/beam.dto';
4
4
  import { IContentShare } from '../../models/content-share.dto';
5
- import { BeamVisibility, BeamServeDenyReasonCode } from '../../../enum/beam.enum';
5
+ import { BeamVisibility, BeamServeDenyReasonCode, BeamDownloadKind } from '../../../enum/beam.enum';
6
6
  export interface IGetBeamData {
7
7
  beamTransfer: IBeamTransfer;
8
8
  }
@@ -90,3 +90,25 @@ export interface IListRecipientsBeamData {
90
90
  recipients: IBeamRecipient[];
91
91
  }
92
92
  export type IListRecipientsBeamResponse = IResponse<IListRecipientsBeamData>;
93
+ export interface IBeamStatsDayBucket {
94
+ day: string;
95
+ count: number;
96
+ zipCount: number;
97
+ }
98
+ export interface IBeamStatsRecentEvent {
99
+ downloadedAt: string;
100
+ kind: BeamDownloadKind;
101
+ fileId: string | null;
102
+ fileName: string | null;
103
+ userId: string | null;
104
+ ipHash: string;
105
+ }
106
+ export interface IGetBeamStatsData {
107
+ byDay: IBeamStatsDayBucket[];
108
+ recent: IBeamStatsRecentEvent[];
109
+ totals: {
110
+ downloadCount: number;
111
+ lastDownloadedAt: string | null;
112
+ };
113
+ }
114
+ export type IGetBeamStatsResponse = IResponse<IGetBeamStatsData>;
@@ -169,6 +169,12 @@ export interface IGetTemplateData {
169
169
  }
170
170
  export type IGetTemplateResponse = IResponse<IGetTemplateData>;
171
171
  export type IListTemplateVersionsResponse = IResponse<IExtractionTemplateVersion[]>;
172
+ export interface IGetTemplateWitnessUrlData {
173
+ url: string;
174
+ filename: string | null;
175
+ size_bytes: number | null;
176
+ }
177
+ export type IGetTemplateWitnessUrlResponse = IResponse<IGetTemplateWitnessUrlData>;
172
178
  export interface IExtractionTemplateCollection {
173
179
  $schema: 'abyss-rcb-template-collection/v1';
174
180
  exported_at: string;
@@ -1,7 +1,7 @@
1
1
  import { IOrganization } from './organization.dto';
2
2
  import { IUser } from './user.dto';
3
3
  import { IProject } from './project.dto';
4
- import { BeamVisibility } from '../../enum/beam.enum';
4
+ import { BeamVisibility, BeamDownloadKind } from '../../enum/beam.enum';
5
5
  export interface IBeamTransfer {
6
6
  id: string;
7
7
  slug: string;
@@ -56,3 +56,15 @@ export interface IBeamRecipient {
56
56
  createdAt?: Date;
57
57
  transfer?: IBeamTransfer;
58
58
  }
59
+ export interface IBeamDownloadEvent {
60
+ id: string;
61
+ transferId: string;
62
+ fileId: string | null;
63
+ kind: BeamDownloadKind;
64
+ ipHash: string;
65
+ userId: string | null;
66
+ downloadedAt: Date;
67
+ createdAt?: Date;
68
+ transfer?: IBeamTransfer;
69
+ file?: IBeamFile | null;
70
+ }
@@ -101,10 +101,14 @@ export interface IExtraction {
101
101
  }
102
102
  export interface IExtractionTemplate {
103
103
  id: string;
104
- userId: string;
104
+ userId: string | null;
105
+ organizationId: string | null;
106
+ projectId: string | null;
107
+ createdByUserId: string | null;
105
108
  name: string;
106
109
  description: string | null;
107
110
  isActive: boolean;
111
+ lastUsedAt: string | null;
108
112
  createdAt: string;
109
113
  updatedAt: string;
110
114
  }
@@ -141,6 +145,9 @@ export interface IExtractionTemplateVersion {
141
145
  version: number;
142
146
  templateJson: Record<string, unknown>;
143
147
  comment: string | null;
148
+ witnessFileId: string | null;
149
+ witnessSizeBytes: number | null;
150
+ witnessFilename: string | null;
144
151
  createdAt: string;
145
152
  }
146
153
  export interface IExtractionFeedback {
@@ -1 +1 @@
1
- {"version":3,"file":"extraction.dto.js","sourceRoot":"","sources":["../../../../src/types/interface/models/extraction.dto.ts"],"names":[],"mappings":";;;AAKA,yFAAoF;AA+E3E,sGA/EA,+CAAqB,OA+EA;AA9E9B,iHAAsG;AAQ7F,4GARA,iEAA2B,OAQA;AAwOpC,IAAY,sBAYX;AAZD,WAAY,sBAAsB;IAChC,qEAA2C,CAAA;IAC3C,+DAAqC,CAAA;IACrC,+EAAqD,CAAA;IACrD,uEAA6C,CAAA;IAC7C,6DAAmC,CAAA;IACnC,2DAAiC,CAAA;IACjC,6DAAmC,CAAA;IACnC,uEAA6C,CAAA;IAC7C,uDAA6B,CAAA;IAC7B,mEAAyC,CAAA;IACzC,6DAAmC,CAAA;AACrC,CAAC,EAZW,sBAAsB,sCAAtB,sBAAsB,QAYjC;AAEY,QAAA,yBAAyB,GACpC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAuCxC,IAAY,2BAGX;AAHD,WAAY,2BAA2B;IACrC,4CAAa,CAAA;IACb,0CAAW,CAAA;AACb,CAAC,EAHW,2BAA2B,2CAA3B,2BAA2B,QAGtC"}
1
+ {"version":3,"file":"extraction.dto.js","sourceRoot":"","sources":["../../../../src/types/interface/models/extraction.dto.ts"],"names":[],"mappings":";;;AAKA,yFAAoF;AA2E3E,sGA3EA,+CAAqB,OA2EA;AA1E9B,iHAAsG;AAI7F,4GAJA,iEAA2B,OAIA;AA8PpC,IAAY,sBAYX;AAZD,WAAY,sBAAsB;IAChC,qEAA2C,CAAA;IAC3C,+DAAqC,CAAA;IACrC,+EAAqD,CAAA;IACrD,uEAA6C,CAAA;IAC7C,6DAAmC,CAAA;IACnC,2DAAiC,CAAA;IACjC,6DAAmC,CAAA;IACnC,uEAA6C,CAAA;IAC7C,uDAA6B,CAAA;IAC7B,mEAAyC,CAAA;IACzC,6DAAmC,CAAA;AACrC,CAAC,EAZW,sBAAsB,sCAAtB,sBAAsB,QAYjC;AAEY,QAAA,yBAAyB,GACpC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAuCxC,IAAY,2BAGX;AAHD,WAAY,2BAA2B;IACrC,4CAAa,CAAA;IACb,0CAAW,CAAA;AACb,CAAC,EAHW,2BAA2B,2CAA3B,2BAA2B,QAGtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abyss-project/tools",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Core package to interact with Abyss-Project Tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -42,6 +42,9 @@ import type {
42
42
  IAddRecipientsBeamBody,
43
43
  IAddRecipientsBeamResponse,
44
44
  IListRecipientsBeamResponse,
45
+ IGetBeamStatsParams,
46
+ IGetBeamStatsQuery,
47
+ IGetBeamStatsResponse,
45
48
  } from '../types';
46
49
  import { BEAM_MANAGE_TOKEN_HEADER } from '../constants/beam.constants';
47
50
 
@@ -51,6 +54,10 @@ import { BEAM_MANAGE_TOKEN_HEADER } from '../constants/beam.constants';
51
54
  const manageTokenHeader = (manageToken?: string): { headers?: Record<string, string> } =>
52
55
  manageToken ? { headers: { [BEAM_MANAGE_TOKEN_HEADER]: manageToken } } : {};
53
56
 
57
+ // renew/reactivate POST body: only carry `days` when the caller requested a
58
+ // specific extension window (the API clamps it to the tier cap; default 7).
59
+ const renewalBody = (days?: number): { days?: number } => (days !== undefined ? { days } : {});
60
+
54
61
  export const beamApi = {
55
62
  get: async (params: IGetBeamParams): Promise<IGetBeamResponse> => {
56
63
  const response = await AbyssToolsCore.axios.get<IGetBeamResponse>(`/beam/${params.beamId}`);
@@ -138,13 +145,31 @@ export const beamApi = {
138
145
 
139
146
  renew: async (params: IRenewBeamParams): Promise<IRenewBeamResponse> => {
140
147
  return (
141
- await AbyssToolsCore.axios.post<IRenewBeamResponse>(`/beam/${params.beamId}/renew`)
148
+ await AbyssToolsCore.axios.post<IRenewBeamResponse>(
149
+ `/beam/${params.beamId}/renew`,
150
+ renewalBody(params.days),
151
+ )
142
152
  ).data;
143
153
  },
144
154
 
145
155
  reactivate: async (params: IReactivateBeamParams): Promise<IReactivateBeamResponse> => {
146
156
  return (
147
- await AbyssToolsCore.axios.post<IReactivateBeamResponse>(`/beam/${params.beamId}/reactivate`)
157
+ await AbyssToolsCore.axios.post<IReactivateBeamResponse>(
158
+ `/beam/${params.beamId}/reactivate`,
159
+ renewalBody(params.days),
160
+ )
161
+ ).data;
162
+ },
163
+
164
+ // Owner download stats (MANAGE-gated): per-day series + recent events + totals.
165
+ stats: async (
166
+ params: IGetBeamStatsParams,
167
+ query?: IGetBeamStatsQuery,
168
+ ): Promise<IGetBeamStatsResponse> => {
169
+ return (
170
+ await AbyssToolsCore.axios.get<IGetBeamStatsResponse>(`/beam/${params.beamId}/stats`, {
171
+ params: query,
172
+ })
148
173
  ).data;
149
174
  },
150
175
 
@@ -258,7 +283,7 @@ export const beamApi = {
258
283
  return (
259
284
  await AbyssToolsCore.axios.post<IRenewBeamResponse>(
260
285
  `/beam/${params.beamId}/renew`,
261
- undefined,
286
+ renewalBody(params.days),
262
287
  manageTokenHeader(params.manageToken),
263
288
  )
264
289
  ).data;
@@ -269,7 +294,7 @@ export const beamApi = {
269
294
  return (
270
295
  await AbyssToolsCore.axios.post<IReactivateBeamResponse>(
271
296
  `/beam/${params.beamId}/reactivate`,
272
- undefined,
297
+ renewalBody(params.days),
273
298
  manageTokenHeader(params.manageToken),
274
299
  )
275
300
  ).data;
@@ -42,6 +42,7 @@ import {
42
42
  IListTemplatesResponse,
43
43
  IGetTemplateResponse,
44
44
  IListTemplateVersionsResponse,
45
+ IGetTemplateWitnessUrlResponse,
45
46
  IExtractionTemplateCollection,
46
47
  IImportTemplateCollectionResponse,
47
48
  IGetJobResponse,
@@ -62,6 +63,7 @@ import {
62
63
  ICreateTemplateBody,
63
64
  IUpdateTemplateBody,
64
65
  IPatchTemplateMetaBody,
66
+ IListTemplatesQuery,
65
67
  ISubmitFeedbackExtractionBody,
66
68
  IPatchFeedbackBody,
67
69
  IListFeedbackQuery,
@@ -478,7 +480,7 @@ export const toggleExtractionTraining = async (
478
480
  // ─── TEMPLATES ───────────────────────────────
479
481
 
480
482
  export const listTemplates = async (
481
- query: { kind?: 'spatial' | 'dsl' } & IExtractionTemplateScopeQuery = {},
483
+ query: IListTemplatesQuery & IExtractionTemplateScopeQuery = {},
482
484
  apiKey?: string,
483
485
  ): Promise<IListTemplatesResponse> => {
484
486
  return (
@@ -566,19 +568,58 @@ export const listTemplateVersions = async (
566
568
  ).data;
567
569
  };
568
570
 
571
+ /**
572
+ * Presigned download URL for a template version's witness PDF.
573
+ * 404 when the version has no witness.
574
+ * GET /extract/templates/:id/versions/:version/witness-url
575
+ */
576
+ export const getTemplateWitnessUrl = async (
577
+ templateId: string,
578
+ version: number,
579
+ scope: IExtractionTemplateScopeQuery = {},
580
+ apiKey?: string,
581
+ ): Promise<IGetTemplateWitnessUrlResponse> => {
582
+ return (
583
+ await AbyssToolsCore.axios.get<IGetTemplateWitnessUrlResponse>(
584
+ `extract/templates/${templateId}/versions/${version}/witness-url`,
585
+ { params: serializeParams(scope), headers: authHeader(apiKey) },
586
+ )
587
+ ).data;
588
+ };
589
+
590
+ /**
591
+ * Delete a template version's witness PDF (frees the billed storage).
592
+ * 404 when the version has no witness. Allowed on archived templates.
593
+ * DELETE /extract/templates/:id/versions/:version/witness
594
+ */
595
+ export const deleteTemplateWitness = async (
596
+ templateId: string,
597
+ version: number,
598
+ scope: IExtractionTemplateScopeQuery = {},
599
+ apiKey?: string,
600
+ ): Promise<void> => {
601
+ await AbyssToolsCore.axios.delete(`extract/templates/${templateId}/versions/${version}/witness`, {
602
+ params: serializeParams(scope),
603
+ headers: authHeader(apiKey),
604
+ });
605
+ };
606
+
569
607
  export const exportTemplateCollection = async (
570
608
  ids: string[] | null,
571
609
  scope: IExtractionTemplateScopeQuery = {},
572
610
  apiKey?: string,
573
611
  ): Promise<IExtractionTemplateCollection> => {
574
- const res = await AbyssToolsCore.axios.get<IExtractionTemplateCollection>(`extract/templates/export`, {
575
- params: {
576
- ...(ids && ids.length ? { ids: ids.join(',') } : {}),
577
- ...serializeParams(scope),
612
+ const res = await AbyssToolsCore.axios.get<IExtractionTemplateCollection>(
613
+ `extract/templates/export`,
614
+ {
615
+ params: {
616
+ ...(ids && ids.length ? { ids: ids.join(',') } : {}),
617
+ ...serializeParams(scope),
618
+ },
619
+ headers: authHeader(apiKey),
620
+ responseType: 'json',
578
621
  },
579
- headers: authHeader(apiKey),
580
- responseType: 'json',
581
- });
622
+ );
582
623
  return res.data;
583
624
  };
584
625
 
@@ -792,10 +833,10 @@ export const getExtractionWebhook = async (
792
833
  apiKey?: string,
793
834
  ): Promise<IGetExtractionWebhookResponse> =>
794
835
  (
795
- await AbyssToolsCore.axios.get<IGetExtractionWebhookResponse>(
796
- `extract/webhook/${webhookId}`,
797
- { params: serializeParams(scope), headers: authHeader(apiKey) },
798
- )
836
+ await AbyssToolsCore.axios.get<IGetExtractionWebhookResponse>(`extract/webhook/${webhookId}`, {
837
+ params: serializeParams(scope),
838
+ headers: authHeader(apiKey),
839
+ })
799
840
  ).data;
800
841
 
801
842
  export const createExtractionWebhook = async (
@@ -804,11 +845,10 @@ export const createExtractionWebhook = async (
804
845
  apiKey?: string,
805
846
  ): Promise<ICreateExtractionWebhookResponse> =>
806
847
  (
807
- await AbyssToolsCore.axios.post<ICreateExtractionWebhookResponse>(
808
- `extract/webhook`,
809
- body,
810
- { params: serializeParams(scope), headers: authHeader(apiKey) },
811
- )
848
+ await AbyssToolsCore.axios.post<ICreateExtractionWebhookResponse>(`extract/webhook`, body, {
849
+ params: serializeParams(scope),
850
+ headers: authHeader(apiKey),
851
+ })
812
852
  ).data;
813
853
 
814
854
  export const updateExtractionWebhook = async (
@@ -39,4 +39,68 @@ describe('beamApi', () => {
39
39
  expect(get).toHaveBeenCalledWith('/b/abcdefghij');
40
40
  get.mockRestore();
41
41
  });
42
+
43
+ it('renew posts an empty body by default and { days } when a window is requested', async () => {
44
+ const post = jest
45
+ .spyOn(AbyssToolsCore.axios, 'post')
46
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
47
+
48
+ await AbyssToolsCore.sdk.beam.renew({ beamId: 't1' });
49
+ expect(post).toHaveBeenCalledWith('/beam/t1/renew', {});
50
+
51
+ await AbyssToolsCore.sdk.beam.renew({ beamId: 't1', days: 30 });
52
+ expect(post).toHaveBeenCalledWith('/beam/t1/renew', { days: 30 });
53
+ post.mockRestore();
54
+ });
55
+
56
+ it('reactivate posts an empty body by default and { days } when a window is requested', async () => {
57
+ const post = jest
58
+ .spyOn(AbyssToolsCore.axios, 'post')
59
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
60
+
61
+ await AbyssToolsCore.sdk.beam.reactivate({ beamId: 't1' });
62
+ expect(post).toHaveBeenCalledWith('/beam/t1/reactivate', {});
63
+
64
+ await AbyssToolsCore.sdk.beam.reactivate({ beamId: 't1', days: 14 });
65
+ expect(post).toHaveBeenCalledWith('/beam/t1/reactivate', { days: 14 });
66
+ post.mockRestore();
67
+ });
68
+
69
+ it('public.renew carries the manage token header AND the optional days body', async () => {
70
+ const post = jest
71
+ .spyOn(AbyssToolsCore.axios, 'post')
72
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
73
+
74
+ await AbyssToolsCore.sdk.beam.public.renew({ beamId: 't1', manageToken: 'tok', days: 7 });
75
+
76
+ expect(post).toHaveBeenCalledWith(
77
+ '/beam/t1/renew',
78
+ { days: 7 },
79
+ { headers: { 'x-beam-manage-token': 'tok' } },
80
+ );
81
+ post.mockRestore();
82
+ });
83
+
84
+ it('stats reads /beam/:beamId/stats with the window/pagination query', async () => {
85
+ const get = jest.spyOn(AbyssToolsCore.axios, 'get').mockResolvedValue({
86
+ data: {
87
+ data: {
88
+ byDay: [{ day: '2026-07-09', count: 2, zipCount: 1 }],
89
+ recent: [],
90
+ totals: { downloadCount: 2, lastDownloadedAt: null },
91
+ },
92
+ },
93
+ });
94
+
95
+ const res = await AbyssToolsCore.sdk.beam.stats(
96
+ { beamId: 't1' },
97
+ { days: 7, limit: 10, offset: 0 },
98
+ );
99
+
100
+ expect(get).toHaveBeenCalledWith('/beam/t1/stats', {
101
+ params: { days: 7, limit: 10, offset: 0 },
102
+ });
103
+ expect(res.data.byDay[0].zipCount).toBe(1);
104
+ get.mockRestore();
105
+ });
42
106
  });
package/src/index.ts CHANGED
@@ -286,6 +286,7 @@ type AbyssToolsCoreSDK = {
286
286
  delete: typeof beamApiModule.beamApi.delete;
287
287
  renew: typeof beamApiModule.beamApi.renew;
288
288
  reactivate: typeof beamApiModule.beamApi.reactivate;
289
+ stats: typeof beamApiModule.beamApi.stats;
289
290
  share: {
290
291
  create: typeof beamApiModule.beamApi.share.create;
291
292
  list: typeof beamApiModule.beamApi.share.list;
@@ -362,8 +363,13 @@ type AbyssToolsCoreSDK = {
362
363
  create: typeof extractionApi.createTemplate;
363
364
  update: typeof extractionApi.updateTemplate;
364
365
  patchMeta: typeof extractionApi.patchTemplateMeta;
366
+ /** DESTRUCTIVE: template + versions + witness PDFs. Archive via patchMeta instead. */
365
367
  delete: typeof extractionApi.deleteTemplate;
366
368
  listVersions: typeof extractionApi.listTemplateVersions;
369
+ /** Presigned download URL for a version's witness PDF (404 when none). */
370
+ getWitnessUrl: typeof extractionApi.getTemplateWitnessUrl;
371
+ /** Delete a version's witness PDF and free the billed storage. */
372
+ deleteWitness: typeof extractionApi.deleteTemplateWitness;
367
373
  exportCollection: typeof extractionApi.exportTemplateCollection;
368
374
  importCollection: typeof extractionApi.importTemplateCollection;
369
375
  };
@@ -671,6 +677,7 @@ export class AbyssToolsCore {
671
677
  delete: beamApiModule.beamApi.delete,
672
678
  renew: beamApiModule.beamApi.renew,
673
679
  reactivate: beamApiModule.beamApi.reactivate,
680
+ stats: beamApiModule.beamApi.stats,
674
681
  share: {
675
682
  create: beamApiModule.beamApi.share.create,
676
683
  list: beamApiModule.beamApi.share.list,
@@ -748,6 +755,8 @@ export class AbyssToolsCore {
748
755
  patchMeta: extractionApi.patchTemplateMeta,
749
756
  delete: extractionApi.deleteTemplate,
750
757
  listVersions: extractionApi.listTemplateVersions,
758
+ getWitnessUrl: extractionApi.getTemplateWitnessUrl,
759
+ deleteWitness: extractionApi.deleteTemplateWitness,
751
760
  exportCollection: extractionApi.exportTemplateCollection,
752
761
  importCollection: extractionApi.importTemplateCollection,
753
762
  },
@@ -22,3 +22,11 @@ export enum BeamServeDenyReasonCode {
22
22
  SCAN_PENDING = 'scan_pending',
23
23
  SCAN_INFECTED = 'scan_infected',
24
24
  }
25
+
26
+ // What a beam_download_events row counted: a single-file download (/b/:slug/
27
+ // file/:fileId) or the whole-transfer zip (/b/:slug/zip). Inline embeds (/raw)
28
+ // and thumbnails (/thumb) are deliberately NOT download events (spec §17.6).
29
+ export enum BeamDownloadKind {
30
+ FILE = 'file',
31
+ ZIP = 'zip',
32
+ }
@@ -105,14 +105,26 @@ export interface IUnlockBeamPublicBody {
105
105
  password: string;
106
106
  }
107
107
 
108
- // Management lifecycle (B4): renew/reactivate take only the transfer id. Both
109
- // push expiresAt forward and reactivate the transfer server-side.
108
+ // Management lifecycle (B4): renew/reactivate push expiresAt forward and
109
+ // reactivate the transfer server-side. `days` optionally requests the extension
110
+ // window; the API clamps it to the tier cap (7 days for anonymous transfers,
111
+ // the plan's TOOLS_BEAM_EXPIRY_MAX_DAYS for owned ones; default 7).
110
112
  export interface IRenewBeamParams {
111
113
  beamId: string;
114
+ days?: number;
115
+ }
116
+
117
+ export interface IRenewBeamBody {
118
+ days?: number;
112
119
  }
113
120
 
114
121
  export interface IReactivateBeamParams {
115
122
  beamId: string;
123
+ days?: number;
124
+ }
125
+
126
+ export interface IReactivateBeamBody {
127
+ days?: number;
116
128
  }
117
129
 
118
130
  // Sharing (B4): a MANAGE holder grants a content_share (BEAM content type) on the
@@ -150,3 +162,16 @@ export interface IAddRecipientsBeamBody {
150
162
  export interface IListRecipientsBeamParams {
151
163
  beamId: string;
152
164
  }
165
+
166
+ // Download stats (owner view): per-day series over the last `days` days plus the
167
+ // paginated recent-event feed (limit/offset, newest first). Defaults are applied
168
+ // server-side (days=30, limit=50, offset=0).
169
+ export interface IGetBeamStatsParams {
170
+ beamId: string;
171
+ }
172
+
173
+ export interface IGetBeamStatsQuery {
174
+ days?: number;
175
+ limit?: number;
176
+ offset?: number;
177
+ }
@@ -150,16 +150,38 @@ export interface IAsyncExportExtractionBody {
150
150
 
151
151
  // ─── TEMPLATES ───────────────────────────────
152
152
 
153
+ /**
154
+ * Query for GET /extract/templates.
155
+ * `archived`: 'false' (default) = active templates, 'true' = the archive,
156
+ * 'all' = everything.
157
+ */
158
+ export interface IListTemplatesQuery {
159
+ kind?: 'spatial' | 'dsl';
160
+ archived?: 'true' | 'false' | 'all';
161
+ }
162
+
153
163
  export interface ICreateTemplateBody {
154
164
  name: string;
155
165
  description?: string;
156
166
  template_json: Record<string, unknown>;
157
167
  comment?: string;
168
+ /**
169
+ * Optional witness (sample) PDF, base64-encoded (max 25 MB decoded).
170
+ * Stored per version, billed to the owner's storage pool.
171
+ */
172
+ witness_pdf_base64?: string;
173
+ witness_filename?: string;
158
174
  }
159
175
 
160
176
  export interface IUpdateTemplateBody {
161
177
  template_json: Record<string, unknown>;
162
178
  comment?: string;
179
+ /**
180
+ * Optional witness (sample) PDF, base64-encoded (max 25 MB decoded).
181
+ * Stored per version, billed to the owner's storage pool.
182
+ */
183
+ witness_pdf_base64?: string;
184
+ witness_filename?: string;
163
185
  }
164
186
 
165
187
  export interface IPatchTemplateMetaBody {
@@ -2,7 +2,7 @@ import { BasePaginate } from '../type-message/base-paginate';
2
2
  import { IResponse } from '../type-message/response';
3
3
  import { IBeamTransfer, IBeamFile, IBeamRecipient } from '../../models/beam.dto';
4
4
  import { IContentShare } from '../../models/content-share.dto';
5
- import { BeamVisibility, BeamServeDenyReasonCode } from '../../../enum/beam.enum';
5
+ import { BeamVisibility, BeamServeDenyReasonCode, BeamDownloadKind } from '../../../enum/beam.enum';
6
6
 
7
7
  export interface IGetBeamData {
8
8
  beamTransfer: IBeamTransfer;
@@ -120,3 +120,32 @@ export interface IListRecipientsBeamData {
120
120
  recipients: IBeamRecipient[];
121
121
  }
122
122
  export type IListRecipientsBeamResponse = IResponse<IListRecipientsBeamData>;
123
+
124
+ // Download stats (owner view, MANAGE-gated): a per-day series (SQL date_trunc,
125
+ // UTC buckets; days with zero downloads are omitted), the paginated recent-event
126
+ // feed (newest first) and the denormalized transfer totals. `fileName` is null
127
+ // for a zip event and after the downloaded file was deleted (FK SET NULL).
128
+ export interface IBeamStatsDayBucket {
129
+ day: string; // 'YYYY-MM-DD'
130
+ count: number;
131
+ zipCount: number;
132
+ }
133
+
134
+ export interface IBeamStatsRecentEvent {
135
+ downloadedAt: string;
136
+ kind: BeamDownloadKind;
137
+ fileId: string | null;
138
+ fileName: string | null;
139
+ userId: string | null;
140
+ ipHash: string;
141
+ }
142
+
143
+ export interface IGetBeamStatsData {
144
+ byDay: IBeamStatsDayBucket[];
145
+ recent: IBeamStatsRecentEvent[];
146
+ totals: {
147
+ downloadCount: number;
148
+ lastDownloadedAt: string | null;
149
+ };
150
+ }
151
+ export type IGetBeamStatsResponse = IResponse<IGetBeamStatsData>;
@@ -3,7 +3,11 @@
3
3
  // ──────────────────────────────────────────────
4
4
 
5
5
  import { IResponse } from '../type-message/response';
6
- import type { IExtractionPlanPreset, IExtractModesData, IFieldValue } from '../../models/extraction-plan.dto';
6
+ import type {
7
+ IExtractionPlanPreset,
8
+ IExtractModesData,
9
+ IFieldValue,
10
+ } from '../../models/extraction-plan.dto';
7
11
  import {
8
12
  IExtractionBankVersion,
9
13
  IExtraction,
@@ -134,7 +138,11 @@ export interface IDiffExtractionsData {
134
138
  same: number;
135
139
  added: IExtractionTransaction[];
136
140
  removed: IExtractionTransaction[];
137
- changed: Array<{ before: IExtractionTransaction; after: IExtractionTransaction; fields: string[] }>;
141
+ changed: Array<{
142
+ before: IExtractionTransaction;
143
+ after: IExtractionTransaction;
144
+ fields: string[];
145
+ }>;
138
146
  summary: { leftCount: number; rightCount: number; sumDeltaCents: number };
139
147
  }
140
148
  export type IDiffExtractionsResponse = IResponse<IDiffExtractionsData>;
@@ -180,6 +188,15 @@ export type IGetTemplateResponse = IResponse<IGetTemplateData>;
180
188
 
181
189
  export type IListTemplateVersionsResponse = IResponse<IExtractionTemplateVersion[]>;
182
190
 
191
+ /** Presigned download reference for a version's witness PDF. */
192
+ export interface IGetTemplateWitnessUrlData {
193
+ /** Presigned DOWNLOAD URL (time-limited). */
194
+ url: string;
195
+ filename: string | null;
196
+ size_bytes: number | null;
197
+ }
198
+ export type IGetTemplateWitnessUrlResponse = IResponse<IGetTemplateWitnessUrlData>;
199
+
183
200
  // Deferred wire-schema rename — must match server SCHEMA constant in
184
201
  // extraction-template-portability.service.ts; rename together in the server/main batch.
185
202
  export interface IExtractionTemplateCollection {
@@ -1,7 +1,7 @@
1
1
  import { IOrganization } from './organization.dto';
2
2
  import { IUser } from './user.dto';
3
3
  import { IProject } from './project.dto';
4
- import { BeamVisibility } from '../../enum/beam.enum';
4
+ import { BeamVisibility, BeamDownloadKind } from '../../enum/beam.enum';
5
5
 
6
6
  export interface IBeamTransfer {
7
7
  id: string;
@@ -65,3 +65,22 @@ export interface IBeamRecipient {
65
65
 
66
66
  transfer?: IBeamTransfer;
67
67
  }
68
+
69
+ // One download-history event (B-phase download history). Written on the counted
70
+ // download paths only (/file + /zip); fileId is null for a zip and after the
71
+ // downloaded file was deleted (FK SET NULL). ipHash is the sha256 of the client
72
+ // IP — the raw IP is never stored. userId is the optionally-resolved bearer of
73
+ // the public serve request (null for anonymous downloads).
74
+ export interface IBeamDownloadEvent {
75
+ id: string;
76
+ transferId: string;
77
+ fileId: string | null;
78
+ kind: BeamDownloadKind;
79
+ ipHash: string;
80
+ userId: string | null;
81
+ downloadedAt: Date;
82
+ createdAt?: Date;
83
+
84
+ transfer?: IBeamTransfer;
85
+ file?: IBeamFile | null;
86
+ }