@proteinjs/user-server 1.9.1 → 1.10.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.
@@ -15,7 +15,8 @@ const isJpeg = (buffer: Buffer) => buffer[0] === 0xff && buffer[1] === 0xd8 && b
15
15
  const getFileRow = async (fileId: string) => await getScopedDbAsSystem<File>().get(fileTables.File, { id: fileId });
16
16
 
17
17
  /**
18
- * Avatars are photo OR emoji — exactly one active — stored as 512x512 JPEG, previous photo file
18
+ * Avatars are photo OR emoji — exactly one active — stored as a square JPEG capped at 512px
19
+ * (never enlarged past the source's honest pixels; see AvatarPhotoFidelity.test.ts), previous photo file
19
20
  * deleted on every change, and the server session cache refreshed in the same stroke (the same
20
21
  * helper that fixes the updateName rename-staleness wart). All assertions are outcomes: rows,
21
22
  * bytes, cache reads.
@@ -85,9 +86,11 @@ describe('UpdateUserInfo avatar mutations', () => {
85
86
  expect(await getFileRow(second.avatarFileId!)).toBeTruthy();
86
87
  });
87
88
 
88
- it('updateAvatarPhoto normalizes HEIC (default iPhone camera format) to JPEG', async () => {
89
+ it('updateAvatarPhoto normalizes HEIC (default iPhone camera format) to JPEG at its honest size', async () => {
89
90
  const user = await testEnv.createUser({ name: 'iPhone user', email: 'heic@test.local' });
90
91
  testEnv.actAs(user);
92
+ // The fixture is 96x64 — small on purpose: the stored master is its centered 64px square,
93
+ // NOT an enlarged 512 (enlargement invents pixels that read as permanent blur).
91
94
  const heic = fs.readFileSync(path.join(__dirname, 'fixtures', 'fixture.heic'));
92
95
 
93
96
  const updated = await service.updateAvatarPhoto(heic.toString('base64'), 'image/heic');
@@ -95,8 +98,8 @@ describe('UpdateUserInfo avatar mutations', () => {
95
98
  const bytes = Buffer.from(await new FileStorage().getFileData(updated.avatarFileId!), 'base64');
96
99
  expect(isJpeg(bytes)).toBe(true);
97
100
  const metadata = await sharp(bytes).metadata();
98
- expect(metadata.width).toBe(512);
99
- expect(metadata.height).toBe(512);
101
+ expect(metadata.width).toBe(64);
102
+ expect(metadata.height).toBe(64);
100
103
  });
101
104
 
102
105
  it('updateAvatarEmoji sets the emoji, nulls the photo, and deletes the previous photo file', async () => {
@@ -70,8 +70,10 @@ describe('GET /avatar/:userId', () => {
70
70
  expect(bytes[0]).toBe(0xff); // JPEG SOI
71
71
  expect(bytes[1]).toBe(0xd8);
72
72
  const metadata = await sharp(bytes).metadata();
73
- expect(metadata.width).toBe(512);
74
- expect(metadata.height).toBe(512);
73
+ // The 100px source stays its honest 100px square — masters are capped at 512, never
74
+ // enlarged to it (AvatarPhotoFidelity.test.ts owns the pipeline contract).
75
+ expect(metadata.width).toBe(100);
76
+ expect(metadata.height).toBe(100);
75
77
 
76
78
  // The client-side URL contract carries the cache-buster.
77
79
  expect(avatarRoute.path(owner.id, updated.avatarFileId!)).toBe(`/avatar/${owner.id}?v=${updated.avatarFileId}`);