@superblocksteam/shared 0.9611.0 → 2.0.0-SNAPSHOT.4665a67985a1ea20

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 (43) hide show
  1. package/dist/socket/heartbeat.d.ts +10 -0
  2. package/dist/socket/heartbeat.d.ts.map +1 -1
  3. package/dist/socket/heartbeat.js +5 -0
  4. package/dist/socket/heartbeat.js.map +1 -1
  5. package/dist/socket/heartbeat.test.js +36 -0
  6. package/dist/socket/heartbeat.test.js.map +1 -1
  7. package/dist/socket/socket.d.ts +1 -0
  8. package/dist/socket/socket.d.ts.map +1 -1
  9. package/dist/socket/socket.js +8 -1
  10. package/dist/socket/socket.js.map +1 -1
  11. package/dist/socket/socket.test.js +33 -0
  12. package/dist/socket/socket.test.js.map +1 -1
  13. package/dist/utils/attachment-upload.d.ts +9 -0
  14. package/dist/utils/attachment-upload.d.ts.map +1 -1
  15. package/dist/utils/attachment-upload.js +26 -0
  16. package/dist/utils/attachment-upload.js.map +1 -1
  17. package/dist/utils/attachment-upload.test.js +19 -0
  18. package/dist/utils/attachment-upload.test.js.map +1 -1
  19. package/dist-esm/socket/heartbeat.d.ts +10 -0
  20. package/dist-esm/socket/heartbeat.d.ts.map +1 -1
  21. package/dist-esm/socket/heartbeat.js +5 -0
  22. package/dist-esm/socket/heartbeat.js.map +1 -1
  23. package/dist-esm/socket/heartbeat.test.js +36 -0
  24. package/dist-esm/socket/heartbeat.test.js.map +1 -1
  25. package/dist-esm/socket/socket.d.ts +1 -0
  26. package/dist-esm/socket/socket.d.ts.map +1 -1
  27. package/dist-esm/socket/socket.js +8 -1
  28. package/dist-esm/socket/socket.js.map +1 -1
  29. package/dist-esm/socket/socket.test.js +33 -0
  30. package/dist-esm/socket/socket.test.js.map +1 -1
  31. package/dist-esm/utils/attachment-upload.d.ts +9 -0
  32. package/dist-esm/utils/attachment-upload.d.ts.map +1 -1
  33. package/dist-esm/utils/attachment-upload.js +25 -0
  34. package/dist-esm/utils/attachment-upload.js.map +1 -1
  35. package/dist-esm/utils/attachment-upload.test.js +20 -1
  36. package/dist-esm/utils/attachment-upload.test.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/socket/heartbeat.test.ts +44 -0
  39. package/src/socket/heartbeat.ts +15 -0
  40. package/src/socket/socket.test.ts +43 -0
  41. package/src/socket/socket.ts +10 -1
  42. package/src/utils/attachment-upload.test.ts +22 -0
  43. package/src/utils/attachment-upload.ts +28 -0
@@ -1,5 +1,24 @@
1
1
  import { describe, expect, test } from 'vitest';
2
- import { ATTACHMENT_CONTENT_MISMATCH_CODE, buildAttachmentUploadMetadata, buildAttachmentUploadFormData, createUploadedAttachmentFromServer, getTextAttachmentType, parseUserFacingUploadError, isOfficeXml, normalizeAttachmentMimeType, toAttachmentUploadDescriptor } from './attachment-upload.js';
2
+ import { ATTACHMENT_CONTENT_MISMATCH_CODE, buildAttachmentUploadMetadata, buildAttachmentUploadFormData, createUploadedAttachmentFromServer, getAttachmentMimeTypeFromFileName, getTextAttachmentType, parseUserFacingUploadError, isOfficeXml, normalizeAttachmentMimeType, toAttachmentUploadDescriptor } from './attachment-upload.js';
3
+ describe('getAttachmentMimeTypeFromFileName', () => {
4
+ test.each([
5
+ ['logo.png', 'image/png'],
6
+ ['logo.PNG', 'image/png'],
7
+ ['photo.jpg', 'image/jpeg'],
8
+ ['photo.jpeg', 'image/jpeg'],
9
+ ['animation.gif', 'image/gif'],
10
+ ['shot.webp', 'image/webp'],
11
+ ['report.pdf', 'application/pdf'],
12
+ ['server.log', 'text/plain'],
13
+ ['rows.csv', 'text/csv'],
14
+ ['config.json', 'application/json']
15
+ ])('reads %s as %s', (fileName, expected) => {
16
+ expect(getAttachmentMimeTypeFromFileName(fileName)).toBe(expected);
17
+ });
18
+ test.each(['app.exe', 'archive.dmg', 'noextension', ''])('has no type for %s', (fileName) => {
19
+ expect(getAttachmentMimeTypeFromFileName(fileName)).toBeUndefined();
20
+ });
21
+ });
3
22
  describe('toAttachmentUploadDescriptor', () => {
4
23
  test('preserves explicit names for non-File uploads', () => {
5
24
  const descriptor = toAttachmentUploadDescriptor({
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-upload.test.js","sourceRoot":"","sources":["../../src/utils/attachment-upload.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attachment-upload.test.js","sourceRoot":"","sources":["../../src/utils/attachment-upload.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/shared",
3
- "version": "0.9611.0",
3
+ "version": "2.0.0-SNAPSHOT.4665a67985a1ea20",
4
4
  "description": "Superblocks Shared Resources",
5
5
  "license": "Superblocks Community Software License",
6
6
  "repository": {
@@ -101,6 +101,50 @@ describe('SocketHeartbeat', () => {
101
101
  subject.stop();
102
102
  });
103
103
 
104
+ // A refusal is the only outcome where the peer stated why, and a refusal does
105
+ // not clear the counts around it. A silence afterwards overwrites `lastError`
106
+ // with its own generic cause, so the stated one has to be kept apart or the
107
+ // death report attributes a timeout's message to the refusal it still counts.
108
+ it('keeps the refusal reason when a later silence overwrites lastError', async () => {
109
+ const { refuseLast, subject } = heartbeat();
110
+ subject.start();
111
+ subject.recordReceive();
112
+
113
+ await elapse(INTERVAL_MS);
114
+ refuseLast(new Error('sandbox is shutting down (code 7)'));
115
+ await elapse(1);
116
+
117
+ // The beat after it is never answered at all, so it times out.
118
+ await elapse(INTERVAL_MS);
119
+ await elapse(TIMEOUT_MS + 1);
120
+
121
+ expect(subject.account).toMatchObject({
122
+ lastRefusalError: 'sandbox is shutting down (code 7)',
123
+ refusals: 1
124
+ });
125
+ expect(subject.account.lastError).not.toBe('sandbox is shutting down (code 7)');
126
+ subject.stop();
127
+ });
128
+
129
+ // An answered beat settles the account outright, so a refusal it supersedes
130
+ // must not keep explaining a connection that has since proved healthy.
131
+ it('drops the refusal reason once a beat is answered', async () => {
132
+ const { answerLast, refuseLast, subject } = heartbeat();
133
+ subject.start();
134
+ subject.recordReceive();
135
+
136
+ await elapse(INTERVAL_MS);
137
+ refuseLast(new Error('sandbox is shutting down (code 7)'));
138
+ await elapse(1);
139
+
140
+ await elapse(INTERVAL_MS);
141
+ answerLast();
142
+ await elapse(1);
143
+
144
+ expect(subject.account.lastRefusalError).toBeUndefined();
145
+ subject.stop();
146
+ });
147
+
104
148
  // A refusal is an answer: the peer is reachable and the request channel works,
105
149
  // so whatever is wrong is not the connection. Spending the grace meant for a
106
150
  // starved pong on one would tear a working socket down over an
@@ -76,6 +76,16 @@ export type HeartbeatAccount = {
76
76
  */
77
77
  readonly lapses: number;
78
78
  readonly lastError?: string;
79
+ /**
80
+ * Why the peer refused, kept apart from {@link HeartbeatAccount.lastError}.
81
+ *
82
+ * A refusal is the only outcome where the peer said why, and it clears none
83
+ * of the counts around it - so a silence after one leaves `refusals` standing
84
+ * while overwriting `lastError` with its own generic cause. A report reading
85
+ * only `lastError` would then attribute a timeout's message to the refusal.
86
+ * Cleared by an answered beat, which settles the whole account.
87
+ */
88
+ readonly lastRefusalError?: string;
79
89
  readonly lastReceivedAt?: number;
80
90
  readonly lastSuccessAt?: number;
81
91
  /**
@@ -411,6 +421,7 @@ export class SocketHeartbeat {
411
421
  this.state.lastSuccessAt = Date.now();
412
422
  this.lost = false;
413
423
  delete this.state.lastError;
424
+ delete this.state.lastRefusalError;
414
425
  return 'success';
415
426
  }
416
427
 
@@ -429,6 +440,10 @@ export class SocketHeartbeat {
429
440
  // silence that counter is the only thing left that can end one.
430
441
  this.state.refusals += 1;
431
442
  this.state.lastError = describe(error);
443
+ // Kept apart because the next unanswered beat overwrites `lastError`
444
+ // without clearing `refusals`, and the peer's own reason is the one thing
445
+ // in this account that was stated rather than inferred.
446
+ this.state.lastRefusalError = this.state.lastError;
432
447
  return 'refused';
433
448
  }
434
449
  // Saturating rather than growing: this is what the caller reports as the
@@ -7,6 +7,49 @@ import { ISocketWithClientAuth } from './socketWithClientAuth.js';
7
7
  const PING_TIMEOUT_CLOSE_REASON = 'Ping timeout';
8
8
 
9
9
  describe('ISocket', () => {
10
+ it('preserves safe fields from structured handler errors', async () => {
11
+ let messageHandler: ((event: { data: string }) => void) | undefined;
12
+ const ws = {
13
+ addEventListener: vi.fn((event: string, handler: (event: { data: string }) => void) => {
14
+ if (event === 'message') {
15
+ messageHandler = handler;
16
+ }
17
+ }),
18
+ close: vi.fn(),
19
+ send: vi.fn()
20
+ };
21
+ new ISocket(
22
+ ws as never,
23
+ {
24
+ editor: {
25
+ save: [
26
+ async () => {
27
+ throw { code: 2, status: 503, message: 'checkpoint unavailable', details: 'do not expose' };
28
+ }
29
+ ]
30
+ }
31
+ } as never,
32
+ [],
33
+ { onClose: vi.fn() }
34
+ );
35
+
36
+ messageHandler?.({
37
+ data: JSON.stringify({ request: { id: 1, method: 'editor.save', payload: {} } })
38
+ });
39
+
40
+ await vi.waitFor(() => {
41
+ expect(ws.send).toHaveBeenCalledWith(
42
+ JSON.stringify({
43
+ response: {
44
+ payload: null,
45
+ id: 1,
46
+ error: { code: 2, status: 503, message: 'checkpoint unavailable' }
47
+ }
48
+ })
49
+ );
50
+ });
51
+ });
52
+
10
53
  it('clears abort cleanup when a no-response timeout rejects the request', async () => {
11
54
  vi.useFakeTimers();
12
55
  try {
@@ -40,6 +40,7 @@ export interface SocketMessage<RequestPayload = unknown, ResponsePayload = unkno
40
40
  export interface SocketError {
41
41
  message: string;
42
42
  code: number;
43
+ status?: number;
43
44
  stack?: string;
44
45
  }
45
46
 
@@ -220,10 +221,18 @@ export class ISocket<ImplementedMethods, CallableMethods, RequestContext extends
220
221
  // call the first handler in the chain
221
222
  response = await generateNextFn(0)();
222
223
  } catch (error) {
224
+ const structuredError =
225
+ typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string' ? error : undefined;
223
226
  const socketError =
224
227
  error instanceof SocketErrorException
225
228
  ? { code: error.code, message: error.message, stack: error.stack }
226
- : { code: 3, message: error.toString(), stack: error instanceof Error ? error.stack : undefined };
229
+ : structuredError && !(structuredError instanceof Error)
230
+ ? {
231
+ code: 'code' in structuredError && typeof structuredError.code === 'number' ? structuredError.code : 3,
232
+ status: 'status' in structuredError && typeof structuredError.status === 'number' ? structuredError.status : undefined,
233
+ message: structuredError.message
234
+ }
235
+ : { code: 3, message: String(error), stack: error instanceof Error ? error.stack : undefined };
227
236
  this.logger.error(JSON.stringify(socketError));
228
237
  return this.respondError(message.request.id, socketError, error);
229
238
  }
@@ -5,6 +5,7 @@ import {
5
5
  buildAttachmentUploadMetadata,
6
6
  buildAttachmentUploadFormData,
7
7
  createUploadedAttachmentFromServer,
8
+ getAttachmentMimeTypeFromFileName,
8
9
  getTextAttachmentType,
9
10
  parseUserFacingUploadError,
10
11
  isOfficeXml,
@@ -12,6 +13,27 @@ import {
12
13
  toAttachmentUploadDescriptor
13
14
  } from './attachment-upload.js';
14
15
 
16
+ describe('getAttachmentMimeTypeFromFileName', () => {
17
+ test.each([
18
+ ['logo.png', 'image/png'],
19
+ ['logo.PNG', 'image/png'],
20
+ ['photo.jpg', 'image/jpeg'],
21
+ ['photo.jpeg', 'image/jpeg'],
22
+ ['animation.gif', 'image/gif'],
23
+ ['shot.webp', 'image/webp'],
24
+ ['report.pdf', 'application/pdf'],
25
+ ['server.log', 'text/plain'],
26
+ ['rows.csv', 'text/csv'],
27
+ ['config.json', 'application/json']
28
+ ])('reads %s as %s', (fileName, expected) => {
29
+ expect(getAttachmentMimeTypeFromFileName(fileName)).toBe(expected);
30
+ });
31
+
32
+ test.each(['app.exe', 'archive.dmg', 'noextension', ''])('has no type for %s', (fileName) => {
33
+ expect(getAttachmentMimeTypeFromFileName(fileName)).toBeUndefined();
34
+ });
35
+ });
36
+
15
37
  describe('toAttachmentUploadDescriptor', () => {
16
38
  test('preserves explicit names for non-File uploads', () => {
17
39
  const descriptor = toAttachmentUploadDescriptor({
@@ -291,6 +291,34 @@ function getTextLikeMimeTypeFromFileName(fileName: string | undefined): string |
291
291
  return textAttachmentType ? TEXT_ATTACHMENT_MIME_TYPES[textAttachmentType] : undefined;
292
292
  }
293
293
 
294
+ /**
295
+ * Names the media type for a file that arrives with nothing but a name, as it
296
+ * does when a caller points at a path instead of handing over the bytes.
297
+ *
298
+ * Derived from {@link MIME_TYPE_FILE_EXTENSIONS} rather than a general-purpose
299
+ * MIME database so the answer stays inside what attachments actually accept:
300
+ * a wider lookup would name a type for `.exe` that upload then refuses.
301
+ */
302
+ export function getAttachmentMimeTypeFromFileName(fileName?: string): string | undefined {
303
+ const normalizedFileName = normalizeNonEmptyString(fileName)?.toLowerCase();
304
+ if (!normalizedFileName) {
305
+ return undefined;
306
+ }
307
+
308
+ for (const [mimeType, extension] of Object.entries(MIME_TYPE_FILE_EXTENSIONS)) {
309
+ // '.jpeg' resolves through the text-like pass below only if listed there,
310
+ // so both spellings are matched here against the canonical extension.
311
+ if (normalizedFileName.endsWith(extension)) {
312
+ return mimeType === 'image/jpg' ? 'image/jpeg' : mimeType;
313
+ }
314
+ }
315
+ if (normalizedFileName.endsWith('.jpeg')) {
316
+ return 'image/jpeg';
317
+ }
318
+
319
+ return getTextLikeMimeTypeFromFileName(normalizedFileName);
320
+ }
321
+
294
322
  function isProviderSafeTextMimeType(mimeType: string): boolean {
295
323
  return mimeType.startsWith('text/') || mimeType === 'application/json';
296
324
  }