@abyss-project/beam 0.0.0-stage → 1.0.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 (123) hide show
  1. package/.eslintrc.js +32 -0
  2. package/.prettierrc +5 -0
  3. package/README.md +21 -2
  4. package/dist/api/beam.api.d.ts +49 -0
  5. package/dist/api/beam.api.js +110 -0
  6. package/dist/api/beam.api.js.map +1 -0
  7. package/dist/api/index.d.ts +1 -0
  8. package/dist/api/index.js +18 -0
  9. package/dist/api/index.js.map +1 -0
  10. package/dist/constants/beam.constants.d.ts +1 -0
  11. package/dist/constants/beam.constants.js +5 -0
  12. package/dist/constants/beam.constants.js.map +1 -0
  13. package/dist/constants/index.d.ts +1 -0
  14. package/dist/constants/index.js +18 -0
  15. package/dist/constants/index.js.map +1 -0
  16. package/dist/index.d.ts +62 -0
  17. package/dist/index.js +149 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/types/enum/api-error.enum.d.ts +31 -0
  20. package/dist/types/enum/api-error.enum.js +37 -0
  21. package/dist/types/enum/api-error.enum.js.map +1 -0
  22. package/dist/types/enum/beam.enum.d.ts +22 -0
  23. package/dist/types/enum/beam.enum.js +30 -0
  24. package/dist/types/enum/beam.enum.js.map +1 -0
  25. package/dist/types/enum/content-share.enum.d.ts +34 -0
  26. package/dist/types/enum/content-share.enum.js +42 -0
  27. package/dist/types/enum/content-share.enum.js.map +1 -0
  28. package/dist/types/enum/index.d.ts +3 -0
  29. package/dist/types/enum/index.js +20 -0
  30. package/dist/types/enum/index.js.map +1 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/dist/types/index.js +19 -0
  33. package/dist/types/index.js.map +1 -0
  34. package/dist/types/interface/api/index.d.ts +5 -0
  35. package/dist/types/interface/api/index.js +22 -0
  36. package/dist/types/interface/api/index.js.map +1 -0
  37. package/dist/types/interface/api/requests/beam.request.d.ts +139 -0
  38. package/dist/types/interface/api/requests/beam.request.js +3 -0
  39. package/dist/types/interface/api/requests/beam.request.js.map +1 -0
  40. package/dist/types/interface/api/requests/index.d.ts +1 -0
  41. package/dist/types/interface/api/requests/index.js +18 -0
  42. package/dist/types/interface/api/requests/index.js.map +1 -0
  43. package/dist/types/interface/api/responses/beam.response.d.ts +115 -0
  44. package/dist/types/interface/api/responses/beam.response.js +3 -0
  45. package/dist/types/interface/api/responses/beam.response.js.map +1 -0
  46. package/dist/types/interface/api/responses/index.d.ts +1 -0
  47. package/dist/types/interface/api/responses/index.js +18 -0
  48. package/dist/types/interface/api/responses/index.js.map +1 -0
  49. package/dist/types/interface/api/type-message/api-error.d.ts +6 -0
  50. package/dist/types/interface/api/type-message/api-error.js +3 -0
  51. package/dist/types/interface/api/type-message/api-error.js.map +1 -0
  52. package/dist/types/interface/api/type-message/base-paginate.d.ts +10 -0
  53. package/dist/types/interface/api/type-message/base-paginate.js +3 -0
  54. package/dist/types/interface/api/type-message/base-paginate.js.map +1 -0
  55. package/dist/types/interface/api/type-message/response.d.ts +5 -0
  56. package/dist/types/interface/api/type-message/response.js +3 -0
  57. package/dist/types/interface/api/type-message/response.js.map +1 -0
  58. package/dist/types/interface/index.d.ts +2 -0
  59. package/dist/types/interface/index.js +19 -0
  60. package/dist/types/interface/index.js.map +1 -0
  61. package/dist/types/interface/models/beam.dto.d.ts +70 -0
  62. package/dist/types/interface/models/beam.dto.js +3 -0
  63. package/dist/types/interface/models/beam.dto.js.map +1 -0
  64. package/dist/types/interface/models/content-share.dto.d.ts +15 -0
  65. package/dist/types/interface/models/content-share.dto.js +3 -0
  66. package/dist/types/interface/models/content-share.dto.js.map +1 -0
  67. package/dist/types/interface/models/index.d.ts +5 -0
  68. package/dist/types/interface/models/index.js +22 -0
  69. package/dist/types/interface/models/index.js.map +1 -0
  70. package/dist/types/interface/models/organization.dto.d.ts +11 -0
  71. package/dist/types/interface/models/organization.dto.js +3 -0
  72. package/dist/types/interface/models/organization.dto.js.map +1 -0
  73. package/dist/types/interface/models/project.dto.d.ts +12 -0
  74. package/dist/types/interface/models/project.dto.js +3 -0
  75. package/dist/types/interface/models/project.dto.js.map +1 -0
  76. package/dist/types/interface/models/user.dto.d.ts +11 -0
  77. package/dist/types/interface/models/user.dto.js +3 -0
  78. package/dist/types/interface/models/user.dto.js.map +1 -0
  79. package/dist/utils/enum.utils.d.ts +1 -0
  80. package/dist/utils/enum.utils.js +3 -0
  81. package/dist/utils/enum.utils.js.map +1 -0
  82. package/dist/utils/error.utils.d.ts +15 -0
  83. package/dist/utils/error.utils.js +25 -0
  84. package/dist/utils/error.utils.js.map +1 -0
  85. package/dist/utils/index.d.ts +2 -0
  86. package/dist/utils/index.js +19 -0
  87. package/dist/utils/index.js.map +1 -0
  88. package/package.json +58 -4
  89. package/src/api/beam.api.ts +325 -0
  90. package/src/api/index.ts +1 -0
  91. package/src/api/tests/beam.api.spec.ts +106 -0
  92. package/src/constants/beam.constants.ts +4 -0
  93. package/src/constants/index.ts +1 -0
  94. package/src/index.ts +167 -0
  95. package/src/types/enum/api-error.enum.ts +33 -0
  96. package/src/types/enum/beam.enum.ts +48 -0
  97. package/src/types/enum/content-share.enum.ts +37 -0
  98. package/src/types/enum/index.ts +3 -0
  99. package/src/types/index.ts +2 -0
  100. package/src/types/interface/api/index.ts +5 -0
  101. package/src/types/interface/api/requests/beam.request.ts +180 -0
  102. package/src/types/interface/api/requests/index.ts +1 -0
  103. package/src/types/interface/api/responses/beam.response.ts +162 -0
  104. package/src/types/interface/api/responses/index.ts +1 -0
  105. package/src/types/interface/api/type-message/api-error.ts +7 -0
  106. package/src/types/interface/api/type-message/base-paginate.ts +11 -0
  107. package/src/types/interface/api/type-message/response.ts +6 -0
  108. package/src/types/interface/index.ts +2 -0
  109. package/src/types/interface/models/beam.dto.ts +86 -0
  110. package/src/types/interface/models/content-share.dto.ts +20 -0
  111. package/src/types/interface/models/index.ts +5 -0
  112. package/src/types/interface/models/organization.dto.ts +21 -0
  113. package/src/types/interface/models/project.dto.ts +16 -0
  114. package/src/types/interface/models/user.dto.ts +19 -0
  115. package/src/types/tests/beam-management-types.spec.ts +38 -0
  116. package/src/types/tests/beam-share-enums.spec.ts +11 -0
  117. package/src/types/tests/beam.dto.spec.ts +61 -0
  118. package/src/types/tests/beam.request-response.spec.ts +43 -0
  119. package/src/utils/enum.utils.ts +1 -0
  120. package/src/utils/error.utils.ts +30 -0
  121. package/src/utils/index.ts +2 -0
  122. package/tsconfig.check.json +15 -0
  123. package/tsconfig.json +36 -0
@@ -0,0 +1,325 @@
1
+ import { AbyssBeamCore } from '..';
2
+ import type {
3
+ IGetBeamParams,
4
+ IGetBeamResponse,
5
+ IGetBeamBySlugParams,
6
+ IGetBeamPublicResponse,
7
+ IUnlockBeamPublicParams,
8
+ IUnlockBeamPublicBody,
9
+ IUnlockBeamPublicResponse,
10
+ IPaginateBeamQuery,
11
+ IPaginateBeamResponse,
12
+ IPaginateProjectBeamParams,
13
+ IPaginateOrganizationBeamParams,
14
+ ICreateBeamBody,
15
+ ICreateBeamResponse,
16
+ ICreateForOrganizationBeamParams,
17
+ IUpdateBeamParams,
18
+ IUpdateBeamBody,
19
+ IUpdateBeamResponse,
20
+ IDeleteBeamParams,
21
+ IDeleteBeamResponse,
22
+ ICreateBeamPublicBody,
23
+ IAddBeamFilesParams,
24
+ IAddBeamFilesBody,
25
+ IAddBeamFilesResponse,
26
+ IImportCloudBeamFilesParams,
27
+ IImportCloudBeamFilesBody,
28
+ IFinalizeBeamParams,
29
+ IFinalizeBeamBody,
30
+ IFinalizeBeamResponse,
31
+ IRenewBeamParams,
32
+ IRenewBeamResponse,
33
+ IReactivateBeamParams,
34
+ IReactivateBeamResponse,
35
+ ICreateBeamShareParams,
36
+ ICreateBeamShareBody,
37
+ ICreateBeamShareResponse,
38
+ IListBeamSharesParams,
39
+ IListBeamSharesResponse,
40
+ IRevokeBeamShareParams,
41
+ IRevokeBeamShareResponse,
42
+ IAddRecipientsBeamBody,
43
+ IAddRecipientsBeamResponse,
44
+ IListRecipientsBeamResponse,
45
+ IGetBeamStatsParams,
46
+ IGetBeamStatsQuery,
47
+ IGetBeamStatsResponse,
48
+ } from '../types';
49
+ import { BEAM_MANAGE_TOKEN_HEADER } from '../constants/beam.constants';
50
+
51
+ // Anonymous management requests carry the plaintext manage token as a header the
52
+ // API (BeamMiddleware) compares to beam_transfers.manageTokenHash. Owner requests
53
+ // omit it and authorize via the session JWT instead.
54
+ const manageTokenHeader = (manageToken?: string): { headers?: Record<string, string> } =>
55
+ manageToken ? { headers: { [BEAM_MANAGE_TOKEN_HEADER]: manageToken } } : {};
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
+
61
+ export const beamApi = {
62
+ get: async (params: IGetBeamParams): Promise<IGetBeamResponse> => {
63
+ const response = await AbyssBeamCore.axios.get<IGetBeamResponse>(`/beam/${params.beamId}`);
64
+ return response.data;
65
+ },
66
+
67
+ getBySlug: async (params: IGetBeamBySlugParams): Promise<IGetBeamPublicResponse> => {
68
+ const response = await AbyssBeamCore.axios.get<IGetBeamPublicResponse>(`/b/${params.slug}`);
69
+ return response.data;
70
+ },
71
+
72
+ unlock: async (
73
+ params: IUnlockBeamPublicParams,
74
+ body: IUnlockBeamPublicBody,
75
+ ): Promise<IUnlockBeamPublicResponse> => {
76
+ const response = await AbyssBeamCore.axios.post<IUnlockBeamPublicResponse>(
77
+ `/b/${params.slug}/unlock`,
78
+ body,
79
+ );
80
+ return response.data;
81
+ },
82
+
83
+ paginate: async (query: IPaginateBeamQuery): Promise<IPaginateBeamResponse> => {
84
+ const response = await AbyssBeamCore.axios.get<IPaginateBeamResponse>('/beam/paginate', {
85
+ params: query,
86
+ });
87
+ return response.data;
88
+ },
89
+
90
+ paginateProject: async (
91
+ params: IPaginateProjectBeamParams,
92
+ query: IPaginateBeamQuery,
93
+ ): Promise<IPaginateBeamResponse> => {
94
+ const response = await AbyssBeamCore.axios.get<IPaginateBeamResponse>(
95
+ `/beam/project/${params.projectId}/paginate`,
96
+ { params: query },
97
+ );
98
+ return response.data;
99
+ },
100
+
101
+ paginateOrganization: async (
102
+ params: IPaginateOrganizationBeamParams,
103
+ query: IPaginateBeamQuery,
104
+ ): Promise<IPaginateBeamResponse> => {
105
+ const response = await AbyssBeamCore.axios.get<IPaginateBeamResponse>(
106
+ `/beam/organization/${params.organizationId}/paginate`,
107
+ { params: query },
108
+ );
109
+ return response.data;
110
+ },
111
+
112
+ create: async (body: ICreateBeamBody): Promise<ICreateBeamResponse> => {
113
+ const response = await AbyssBeamCore.axios.post<ICreateBeamResponse>('/beam', body);
114
+ return response.data;
115
+ },
116
+
117
+ createForOrganization: async (
118
+ params: ICreateForOrganizationBeamParams,
119
+ body: ICreateBeamBody,
120
+ ): Promise<ICreateBeamResponse> => {
121
+ const response = await AbyssBeamCore.axios.post<ICreateBeamResponse>(
122
+ `/beam/organization/${params.organizationId}`,
123
+ body,
124
+ );
125
+ return response.data;
126
+ },
127
+
128
+ update: async (
129
+ params: IUpdateBeamParams,
130
+ body: IUpdateBeamBody,
131
+ ): Promise<IUpdateBeamResponse> => {
132
+ const response = await AbyssBeamCore.axios.put<IUpdateBeamResponse>(
133
+ `/beam/${params.beamId}`,
134
+ body,
135
+ );
136
+ return response.data;
137
+ },
138
+
139
+ delete: async (params: IDeleteBeamParams): Promise<IDeleteBeamResponse> => {
140
+ const response = await AbyssBeamCore.axios.delete<IDeleteBeamResponse>(
141
+ `/beam/${params.beamId}`,
142
+ );
143
+ return response.data;
144
+ },
145
+
146
+ renew: async (params: IRenewBeamParams): Promise<IRenewBeamResponse> => {
147
+ return (
148
+ await AbyssBeamCore.axios.post<IRenewBeamResponse>(
149
+ `/beam/${params.beamId}/renew`,
150
+ renewalBody(params.days),
151
+ )
152
+ ).data;
153
+ },
154
+
155
+ reactivate: async (params: IReactivateBeamParams): Promise<IReactivateBeamResponse> => {
156
+ return (
157
+ await AbyssBeamCore.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 AbyssBeamCore.axios.get<IGetBeamStatsResponse>(`/beam/${params.beamId}/stats`, {
171
+ params: query,
172
+ })
173
+ ).data;
174
+ },
175
+
176
+ share: {
177
+ create: async (
178
+ params: ICreateBeamShareParams,
179
+ body: ICreateBeamShareBody,
180
+ ): Promise<ICreateBeamShareResponse> => {
181
+ return (
182
+ await AbyssBeamCore.axios.post<ICreateBeamShareResponse>(
183
+ `/beam/${params.beamId}/share`,
184
+ body,
185
+ )
186
+ ).data;
187
+ },
188
+ list: async (params: IListBeamSharesParams): Promise<IListBeamSharesResponse> => {
189
+ return (
190
+ await AbyssBeamCore.axios.get<IListBeamSharesResponse>(`/beam/${params.beamId}/share`)
191
+ ).data;
192
+ },
193
+ revoke: async (params: IRevokeBeamShareParams): Promise<IRevokeBeamShareResponse> => {
194
+ return (
195
+ await AbyssBeamCore.axios.delete<IRevokeBeamShareResponse>(
196
+ `/beam/${params.beamId}/share/${params.shareId}`,
197
+ )
198
+ ).data;
199
+ },
200
+ },
201
+
202
+ recipients: {
203
+ add: async (
204
+ params: { beamId: string },
205
+ body: IAddRecipientsBeamBody,
206
+ ): Promise<IAddRecipientsBeamResponse> =>
207
+ (
208
+ await AbyssBeamCore.axios.post<IAddRecipientsBeamResponse>(
209
+ `beam/${params.beamId}/recipients`,
210
+ body,
211
+ )
212
+ ).data,
213
+ list: async (params: { beamId: string }): Promise<IListRecipientsBeamResponse> =>
214
+ (
215
+ await AbyssBeamCore.axios.get<IListRecipientsBeamResponse>(
216
+ `beam/${params.beamId}/recipients`,
217
+ )
218
+ ).data,
219
+ },
220
+
221
+ addFiles: async (
222
+ params: IAddBeamFilesParams,
223
+ body: IAddBeamFilesBody,
224
+ ): Promise<IAddBeamFilesResponse> => {
225
+ return (
226
+ await AbyssBeamCore.axios.post<IAddBeamFilesResponse>(`/beam/${params.beamId}/files`, body)
227
+ ).data;
228
+ },
229
+
230
+ importCloudFiles: async (
231
+ params: IImportCloudBeamFilesParams,
232
+ body: IImportCloudBeamFilesBody,
233
+ ): Promise<IAddBeamFilesResponse> => {
234
+ return (
235
+ await AbyssBeamCore.axios.post<IAddBeamFilesResponse>(
236
+ `/beam/${params.beamId}/files/import-cloud`,
237
+ body,
238
+ )
239
+ ).data;
240
+ },
241
+
242
+ finalize: async (
243
+ params: IFinalizeBeamParams,
244
+ body?: IFinalizeBeamBody,
245
+ ): Promise<IFinalizeBeamResponse> => {
246
+ return (
247
+ await AbyssBeamCore.axios.post<IFinalizeBeamResponse>(
248
+ `/beam/${params.beamId}/finalize`,
249
+ body ?? {},
250
+ )
251
+ ).data;
252
+ },
253
+
254
+ public: {
255
+ create: async (body: ICreateBeamPublicBody): Promise<ICreateBeamResponse> => {
256
+ return (await AbyssBeamCore.axios.post<ICreateBeamResponse>(`/beam/public`, body)).data;
257
+ },
258
+ addFiles: async (
259
+ params: IAddBeamFilesParams,
260
+ body: IAddBeamFilesBody,
261
+ ): Promise<IAddBeamFilesResponse> => {
262
+ return (
263
+ await AbyssBeamCore.axios.post<IAddBeamFilesResponse>(
264
+ `/beam/public/${params.beamId}/files`,
265
+ body,
266
+ )
267
+ ).data;
268
+ },
269
+ finalize: async (
270
+ params: IFinalizeBeamParams,
271
+ body?: IFinalizeBeamBody,
272
+ ): Promise<IFinalizeBeamResponse> => {
273
+ return (
274
+ await AbyssBeamCore.axios.post<IFinalizeBeamResponse>(
275
+ `/beam/public/${params.beamId}/finalize`,
276
+ body ?? {},
277
+ )
278
+ ).data;
279
+ },
280
+ renew: async (
281
+ params: IRenewBeamParams & { manageToken?: string },
282
+ ): Promise<IRenewBeamResponse> => {
283
+ return (
284
+ await AbyssBeamCore.axios.post<IRenewBeamResponse>(
285
+ `/beam/${params.beamId}/renew`,
286
+ renewalBody(params.days),
287
+ manageTokenHeader(params.manageToken),
288
+ )
289
+ ).data;
290
+ },
291
+ reactivate: async (
292
+ params: IReactivateBeamParams & { manageToken?: string },
293
+ ): Promise<IReactivateBeamResponse> => {
294
+ return (
295
+ await AbyssBeamCore.axios.post<IReactivateBeamResponse>(
296
+ `/beam/${params.beamId}/reactivate`,
297
+ renewalBody(params.days),
298
+ manageTokenHeader(params.manageToken),
299
+ )
300
+ ).data;
301
+ },
302
+ update: async (
303
+ params: IUpdateBeamParams & { manageToken?: string },
304
+ body: IUpdateBeamBody,
305
+ ): Promise<IUpdateBeamResponse> => {
306
+ return (
307
+ await AbyssBeamCore.axios.put<IUpdateBeamResponse>(
308
+ `/beam/${params.beamId}`,
309
+ body,
310
+ manageTokenHeader(params.manageToken),
311
+ )
312
+ ).data;
313
+ },
314
+ delete: async (
315
+ params: IDeleteBeamParams & { manageToken?: string },
316
+ ): Promise<IDeleteBeamResponse> => {
317
+ return (
318
+ await AbyssBeamCore.axios.delete<IDeleteBeamResponse>(
319
+ `/beam/${params.beamId}`,
320
+ manageTokenHeader(params.manageToken),
321
+ )
322
+ ).data;
323
+ },
324
+ },
325
+ };
@@ -0,0 +1 @@
1
+ export * from './beam.api';
@@ -0,0 +1,106 @@
1
+ import { AbyssBeamCore } from '../../index';
2
+ import { BeamVisibility } from '../../types';
3
+
4
+ describe('beamApi', () => {
5
+ it('exposes the beam CRUD namespace on the SDK facade', () => {
6
+ const beam = AbyssBeamCore.sdk.beam;
7
+ expect(typeof beam.get).toBe('function');
8
+ expect(typeof beam.getBySlug).toBe('function');
9
+ expect(typeof beam.paginate).toBe('function');
10
+ expect(typeof beam.paginateProject).toBe('function');
11
+ expect(typeof beam.paginateOrganization).toBe('function');
12
+ expect(typeof beam.create).toBe('function');
13
+ expect(typeof beam.update).toBe('function');
14
+ expect(typeof beam.delete).toBe('function');
15
+ });
16
+
17
+ it('create posts to /beam and returns the wrapped transfer', async () => {
18
+ const post = jest
19
+ .spyOn(AbyssBeamCore.axios, 'post')
20
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
21
+
22
+ const res = await AbyssBeamCore.sdk.beam.create({
23
+ title: 'T',
24
+ visibility: BeamVisibility.PUBLIC,
25
+ });
26
+
27
+ expect(post).toHaveBeenCalledWith('/beam', { title: 'T', visibility: BeamVisibility.PUBLIC });
28
+ expect(res.data.beamTransfer.id).toBe('t1');
29
+ post.mockRestore();
30
+ });
31
+
32
+ it('getBySlug reads the public /b/:slug path', async () => {
33
+ const get = jest
34
+ .spyOn(AbyssBeamCore.axios, 'get')
35
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1', slug: 'abcdefghij' } } } });
36
+
37
+ await AbyssBeamCore.sdk.beam.getBySlug({ slug: 'abcdefghij' });
38
+
39
+ expect(get).toHaveBeenCalledWith('/b/abcdefghij');
40
+ get.mockRestore();
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(AbyssBeamCore.axios, 'post')
46
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
47
+
48
+ await AbyssBeamCore.sdk.beam.renew({ beamId: 't1' });
49
+ expect(post).toHaveBeenCalledWith('/beam/t1/renew', {});
50
+
51
+ await AbyssBeamCore.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(AbyssBeamCore.axios, 'post')
59
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
60
+
61
+ await AbyssBeamCore.sdk.beam.reactivate({ beamId: 't1' });
62
+ expect(post).toHaveBeenCalledWith('/beam/t1/reactivate', {});
63
+
64
+ await AbyssBeamCore.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(AbyssBeamCore.axios, 'post')
72
+ .mockResolvedValue({ data: { data: { beamTransfer: { id: 't1' } } } });
73
+
74
+ await AbyssBeamCore.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(AbyssBeamCore.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 AbyssBeamCore.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
+ });
106
+ });
@@ -0,0 +1,4 @@
1
+ // Header carrying the anonymous transfer management secret (spec §7). The front
2
+ // keeps the plaintext manageToken in the URL fragment and sends it here; the API
3
+ // (BeamMiddleware) compares sha256(header) to beam_transfers.manageTokenHash.
4
+ export const BEAM_MANAGE_TOKEN_HEADER = 'x-beam-manage-token';
@@ -0,0 +1 @@
1
+ export * from './beam.constants';
package/src/index.ts ADDED
@@ -0,0 +1,167 @@
1
+ import axios from 'axios';
2
+ import axiosRetry, { IAxiosRetryConfig, isNetworkOrIdempotentRequestError } from 'axios-retry';
3
+
4
+ export * from './api';
5
+ export * from './types';
6
+ export * from './utils';
7
+ export * from './constants';
8
+
9
+ import * as beamApiModule from './api/beam.api';
10
+
11
+ const DEFAULT_BASE_URL = 'https://beam-api.abyss-project.fr/api/';
12
+
13
+ export const API_KEY_HEADER = 'abyss-api-key';
14
+ export const ADMIN_TOKEN_HEADER = 'abyss-admin-token';
15
+
16
+ const NUMBER_RETRY_API = 10;
17
+ const BASE_DELAY_BETWEEN_RETRY = 5000;
18
+ const RETRY_CODES = [
19
+ 502, // Bad Gateway
20
+ 503, // Service Unavailable
21
+ ];
22
+
23
+ type CoreConfig = {
24
+ token?: string;
25
+ apiKey?: string;
26
+ adminToken?: string;
27
+ baseURL?: string;
28
+ };
29
+
30
+ type AbyssBeamCoreSDK = {
31
+ beam: {
32
+ get: typeof beamApiModule.beamApi.get;
33
+ getBySlug: typeof beamApiModule.beamApi.getBySlug;
34
+ unlock: typeof beamApiModule.beamApi.unlock;
35
+ paginate: typeof beamApiModule.beamApi.paginate;
36
+ paginateProject: typeof beamApiModule.beamApi.paginateProject;
37
+ paginateOrganization: typeof beamApiModule.beamApi.paginateOrganization;
38
+ create: typeof beamApiModule.beamApi.create;
39
+ createForOrganization: typeof beamApiModule.beamApi.createForOrganization;
40
+ update: typeof beamApiModule.beamApi.update;
41
+ delete: typeof beamApiModule.beamApi.delete;
42
+ renew: typeof beamApiModule.beamApi.renew;
43
+ reactivate: typeof beamApiModule.beamApi.reactivate;
44
+ stats: typeof beamApiModule.beamApi.stats;
45
+ share: {
46
+ create: typeof beamApiModule.beamApi.share.create;
47
+ list: typeof beamApiModule.beamApi.share.list;
48
+ revoke: typeof beamApiModule.beamApi.share.revoke;
49
+ };
50
+ recipients: {
51
+ add: typeof beamApiModule.beamApi.recipients.add;
52
+ list: typeof beamApiModule.beamApi.recipients.list;
53
+ };
54
+ addFiles: typeof beamApiModule.beamApi.addFiles;
55
+ importCloudFiles: typeof beamApiModule.beamApi.importCloudFiles;
56
+ finalize: typeof beamApiModule.beamApi.finalize;
57
+ public: {
58
+ create: typeof beamApiModule.beamApi.public.create;
59
+ addFiles: typeof beamApiModule.beamApi.public.addFiles;
60
+ finalize: typeof beamApiModule.beamApi.public.finalize;
61
+ renew: typeof beamApiModule.beamApi.public.renew;
62
+ reactivate: typeof beamApiModule.beamApi.public.reactivate;
63
+ update: typeof beamApiModule.beamApi.public.update;
64
+ delete: typeof beamApiModule.beamApi.public.delete;
65
+ };
66
+ };
67
+ };
68
+
69
+ export class AbyssBeamCore {
70
+ private static config = {
71
+ token: '',
72
+ apiKey: '',
73
+ adminToken: '',
74
+ baseURL: DEFAULT_BASE_URL,
75
+ };
76
+ static axios = axios.create({
77
+ baseURL: this.config.baseURL,
78
+ withCredentials: true,
79
+ });
80
+
81
+ private static _sdk: AbyssBeamCoreSDK | null = null;
82
+
83
+ public static get sdk(): AbyssBeamCoreSDK {
84
+ if (!AbyssBeamCore._sdk) {
85
+ AbyssBeamCore._sdk = AbyssBeamCore.buildSDK();
86
+ }
87
+ return AbyssBeamCore._sdk;
88
+ }
89
+
90
+ private static buildSDK(): AbyssBeamCoreSDK {
91
+ return {
92
+ beam: {
93
+ get: beamApiModule.beamApi.get,
94
+ getBySlug: beamApiModule.beamApi.getBySlug,
95
+ unlock: beamApiModule.beamApi.unlock,
96
+ paginate: beamApiModule.beamApi.paginate,
97
+ paginateProject: beamApiModule.beamApi.paginateProject,
98
+ paginateOrganization: beamApiModule.beamApi.paginateOrganization,
99
+ create: beamApiModule.beamApi.create,
100
+ createForOrganization: beamApiModule.beamApi.createForOrganization,
101
+ update: beamApiModule.beamApi.update,
102
+ delete: beamApiModule.beamApi.delete,
103
+ renew: beamApiModule.beamApi.renew,
104
+ reactivate: beamApiModule.beamApi.reactivate,
105
+ stats: beamApiModule.beamApi.stats,
106
+ share: {
107
+ create: beamApiModule.beamApi.share.create,
108
+ list: beamApiModule.beamApi.share.list,
109
+ revoke: beamApiModule.beamApi.share.revoke,
110
+ },
111
+ recipients: {
112
+ add: beamApiModule.beamApi.recipients.add,
113
+ list: beamApiModule.beamApi.recipients.list,
114
+ },
115
+ addFiles: beamApiModule.beamApi.addFiles,
116
+ importCloudFiles: beamApiModule.beamApi.importCloudFiles,
117
+ finalize: beamApiModule.beamApi.finalize,
118
+ public: {
119
+ create: beamApiModule.beamApi.public.create,
120
+ addFiles: beamApiModule.beamApi.public.addFiles,
121
+ finalize: beamApiModule.beamApi.public.finalize,
122
+ renew: beamApiModule.beamApi.public.renew,
123
+ reactivate: beamApiModule.beamApi.public.reactivate,
124
+ update: beamApiModule.beamApi.public.update,
125
+ delete: beamApiModule.beamApi.public.delete,
126
+ },
127
+ },
128
+ };
129
+ }
130
+
131
+ public static enableAxiosRetry(axiosRetryConfig?: IAxiosRetryConfig): void {
132
+ axiosRetry(AbyssBeamCore.axios, {
133
+ retries: NUMBER_RETRY_API,
134
+ retryDelay: (retryCount) => retryCount * BASE_DELAY_BETWEEN_RETRY,
135
+ retryCondition: (error) =>
136
+ isNetworkOrIdempotentRequestError(error) ||
137
+ RETRY_CODES.includes(error.response?.status || 502),
138
+ ...axiosRetryConfig,
139
+ });
140
+ }
141
+
142
+ public static setConfig(config: CoreConfig): void {
143
+ AbyssBeamCore._sdk = null;
144
+
145
+ AbyssBeamCore.config = { ...AbyssBeamCore.config, ...config };
146
+
147
+ if (config.baseURL) {
148
+ AbyssBeamCore.axios.defaults.baseURL = config.baseURL;
149
+ }
150
+
151
+ if (config.token) {
152
+ AbyssBeamCore.axios.defaults.headers.common.Authorization = `Bearer ${config.token}`;
153
+ }
154
+
155
+ if (config.apiKey) {
156
+ AbyssBeamCore.axios.defaults.headers.common[API_KEY_HEADER] = `${config.apiKey}`;
157
+ }
158
+
159
+ if (config.adminToken) {
160
+ AbyssBeamCore.axios.defaults.headers.common[ADMIN_TOKEN_HEADER] = `${config.adminToken}`;
161
+ }
162
+ }
163
+
164
+ public static getConfig(): CoreConfig {
165
+ return AbyssBeamCore.config;
166
+ }
167
+ }
@@ -0,0 +1,33 @@
1
+ export enum ErrorType {
2
+ apiError = 'api_error',
3
+ authError = 'authentication_error',
4
+ resourceError = 'resource_error',
5
+ }
6
+
7
+ export enum Code {
8
+ // HTTP Error
9
+ tokenFailure = 'token_authentication_failure',
10
+ authenticationFailure = 'authentication_credentials_failure',
11
+ accessNotAllowed = 'access_not_allowed',
12
+ notFound = 'not_found',
13
+ validationError = 'resource_validation_error',
14
+ constraintError = 'resource_constraint_error',
15
+ internalServerError = 'internal_server_error',
16
+ serviceUnavailable = 'service_unavailable',
17
+ invalidData = 'invalid_data',
18
+ notImplemented = 'not_implemented',
19
+ retrospectiveCannotBeModifiedWhenCompleted = 'retrospective_cannot_be_modified_when_completed',
20
+ retrospectiveActionNotAllowedInPhase = 'retrospective_action_not_allowed_in_phase',
21
+ retrospectiveVoteBudgetExhausted = 'retrospective_vote_budget_exhausted',
22
+ retrospectiveActionLimitReached = 'retrospective_action_limit_reached',
23
+ atlasDocumentLimitReached = 'atlas_document_limit_reached',
24
+ boardCardDocumentLimitReached = 'board_card_document_limit_reached',
25
+ boardCardMoveInvalidDestination = 'board_card_move_invalid_destination',
26
+ boardCardLinkLimitReached = 'board_card_link_limit_reached',
27
+ boardCardLinkAlreadyExists = 'board_card_link_already_exists',
28
+ boardCardLinkToSelf = 'board_card_link_to_self',
29
+ shortLinkProtected = 'short_link_protected',
30
+ drawImageLimitReached = 'draw_image_limit_reached',
31
+ drawImageOwnerRequired = 'draw_image_owner_required',
32
+ drawImageNotInDraw = 'draw_image_not_in_draw',
33
+ }
@@ -0,0 +1,48 @@
1
+ // AbyssBeam transfer visibility (design D7). PUBLIC = a contentShare LINK so the
2
+ // /b/:slug link works for anyone until expiry; RESTRICTED = USER/ORG/TEAM/PROJECT
3
+ // grants resolved via contentShare.resolveAccess (>= VIEW). Password is an
4
+ // independent second factor, NOT a visibility level.
5
+ export enum BeamVisibility {
6
+ PUBLIC = 'PUBLIC',
7
+ RESTRICTED = 'RESTRICTED',
8
+ }
9
+
10
+ // Machine-readable denial reason returned on the public serving discovery path
11
+ // (GET /b/:slug) so the front renders the correct state from the reason code
12
+ // rather than inferring it from the bare HTTP status (spec §17.1). Mirrors the
13
+ // backend `BeamServeDenyReason`; only the non-password discovery denials are
14
+ // surfaced here (PASSWORD_REQUIRED is not a VIEW denial — the metadata is still
15
+ // returned with isLocked=true). Byte endpoints (/file, /raw, /thumb, /zip) stay
16
+ // bare-status by design (CDN-visible), so this reason is a VIEW-only signal.
17
+ export enum BeamServeDenyReasonCode {
18
+ NOT_FOUND = 'not_found',
19
+ EXPIRED = 'expired',
20
+ FORBIDDEN_VISIBILITY = 'forbidden_visibility',
21
+ MAX_DOWNLOADS = 'max_downloads',
22
+ SCAN_PENDING = 'scan_pending',
23
+ }
24
+
25
+ // Per-file AV verdict surfaced on the public listing (GET /b/:slug) so the
26
+ // front can render a per-file badge — clean / "en analyse" (pending) /
27
+ // "infecté" — independently of the whole-transfer denial reason above.
28
+ // Storage-neutral: AbyssBeam serves an INFECTED file's bytes (spec decision —
29
+ // infected files stay downloadable, not blocked; the front is expected to
30
+ // warn before/at download). Mirrors the storage extension's `FileScanStatus`
31
+ // (`@abyss-project/storage`) byte-for-byte; duplicated locally because this
32
+ // package does not depend on `@abyss-project/storage` (same rationale as
33
+ // BeamServeDenyReasonCode above).
34
+ export enum BeamFileScanStatus {
35
+ PENDING_SCAN = 'pending_scan',
36
+ CLEAN = 'clean',
37
+ INFECTED = 'infected',
38
+ SKIPPED = 'skipped',
39
+ ERROR = 'error',
40
+ }
41
+
42
+ // What a beam_download_events row counted: a single-file download (/b/:slug/
43
+ // file/:fileId) or the whole-transfer zip (/b/:slug/zip). Inline embeds (/raw)
44
+ // and thumbnails (/thumb) are deliberately NOT download events (spec §17.6).
45
+ export enum BeamDownloadKind {
46
+ FILE = 'file',
47
+ ZIP = 'zip',
48
+ }