jazz-tools 0.17.0 → 0.17.2

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.
@@ -1,7 +1,11 @@
1
- import { Account, FileStream, ImageDefinition } from "jazz-tools";
2
- import { highestResAvailable, loadImageBySize } from "jazz-tools/media";
3
- import { createJazzTestAccount, setupJazzTestSync } from "jazz-tools/testing";
1
+ import { Account, FileStream, Group, ImageDefinition } from "jazz-tools";
2
+ import {
3
+ createJazzTestAccount,
4
+ setActiveAccount,
5
+ setupJazzTestSync,
6
+ } from "jazz-tools/testing";
4
7
  import { beforeEach, describe, expect, it, vi } from "vitest";
8
+ import { highestResAvailable, loadImageBySize } from "./utils.js";
5
9
 
6
10
  const createFileStream = (account: any, blobSize?: number) => {
7
11
  return FileStream.createFromBlob(
@@ -209,23 +213,21 @@ describe("highestResAvailable", async () => {
209
213
  describe("loadImageBySize", async () => {
210
214
  let account: Account;
211
215
  beforeEach(async () => {
212
- account = await createJazzTestAccount({
213
- isCurrentActiveAccount: true,
214
- });
215
- vi.spyOn(Account, "getMe").mockReturnValue(account);
216
- await setupJazzTestSync();
216
+ account = await setupJazzTestSync();
217
+ setActiveAccount(account);
217
218
  });
218
219
 
219
220
  const createImageDef = async (
220
221
  sizes: Array<[number, number]>,
221
222
  progressive = true,
223
+ owner: Account | Group = account,
222
224
  ) => {
223
225
  if (sizes.length === 0) throw new Error("sizes array must not be empty");
224
226
 
225
227
  const originalSize = sizes[sizes.length - 1]!;
226
228
  sizes = sizes.slice(0, -1);
227
229
 
228
- const original = await createFileStream(account, 1);
230
+ const original = await createFileStream(owner, 1);
229
231
  // Ensure sizes array is not empty
230
232
  const imageDef = ImageDefinition.create(
231
233
  {
@@ -233,14 +235,14 @@ describe("loadImageBySize", async () => {
233
235
  progressive,
234
236
  original,
235
237
  },
236
- { owner: account },
238
+ { owner },
237
239
  );
238
240
  imageDef[`${originalSize[0]}x${originalSize[1]}`] = original;
239
241
 
240
242
  for (const size of sizes) {
241
243
  if (!size) continue;
242
244
  const [w, h] = size;
243
- imageDef[`${w}x${h}`] = await createFileStream(account, 1);
245
+ imageDef[`${w}x${h}`] = await createFileStream(owner, 1);
244
246
  }
245
247
  return imageDef;
246
248
  };
@@ -251,6 +253,24 @@ describe("loadImageBySize", async () => {
251
253
  expect(result?.image.id).toBe(imageDef["1920x1080"]!.id);
252
254
  });
253
255
 
256
+ it("returns the original image already loaded", async () => {
257
+ const account = await setupJazzTestSync({ asyncPeers: true });
258
+ const account2 = await createJazzTestAccount();
259
+
260
+ setActiveAccount(account);
261
+
262
+ const group = Group.create();
263
+ group.addMember("everyone", "reader");
264
+
265
+ const imageDef = await createImageDef([[1920, 1080]], false, group);
266
+ setActiveAccount(account2);
267
+
268
+ const result = await loadImageBySize(imageDef, 256, 256);
269
+ expect(result?.image.id).toBe(imageDef["1920x1080"]!.id);
270
+ expect(result?.image.isBinaryStreamEnded()).toBe(true);
271
+ expect(result?.image.asBase64()).toStrictEqual(expect.any(String));
272
+ });
273
+
254
274
  it("returns null if no sizes are available", async () => {
255
275
  const original = await createFileStream(account._owner, 1);
256
276
  const imageDef = ImageDefinition.create(
@@ -324,4 +344,30 @@ describe("loadImageBySize", async () => {
324
344
  expect(result?.width).toBe(1024);
325
345
  expect(result?.height).toBe(1024);
326
346
  });
347
+
348
+ it("returns the image already loaded", async () => {
349
+ const account = await setupJazzTestSync({ asyncPeers: true });
350
+ const account2 = await createJazzTestAccount();
351
+
352
+ setActiveAccount(account);
353
+
354
+ const group = Group.create();
355
+ group.addMember("everyone", "reader");
356
+
357
+ const imageDef = await createImageDef(
358
+ [
359
+ [512, 512],
360
+ [1024, 1024],
361
+ ],
362
+ undefined,
363
+ group,
364
+ );
365
+
366
+ setActiveAccount(account2);
367
+
368
+ const result = await loadImageBySize(imageDef, 1024, 1024);
369
+ expect(result?.image.id).toBe(imageDef["1024x1024"]!.id);
370
+ expect(result?.image.isBinaryStreamEnded()).toBe(true);
371
+ expect(result?.image.asBase64()).toStrictEqual(expect.any(String));
372
+ });
327
373
  });
@@ -126,21 +126,22 @@ export async function loadImage(
126
126
  };
127
127
  }
128
128
 
129
- await imageOrId.ensureLoaded({
130
- resolve: {
131
- original: true,
132
- },
133
- });
134
-
135
129
  if (!imageOrId.original) {
136
130
  console.warn("Unable to find the original image");
137
131
  return null;
138
132
  }
139
133
 
134
+ const loadedOriginal = await FileStream.load(imageOrId.original.id);
135
+
136
+ if (!loadedOriginal) {
137
+ console.warn("Unable to find the original image");
138
+ return null;
139
+ }
140
+
140
141
  return {
141
142
  width: imageOrId.originalSize[0],
142
143
  height: imageOrId.originalSize[1],
143
- image: imageOrId.original,
144
+ image: loadedOriginal,
144
145
  };
145
146
  }
146
147
 
@@ -149,7 +150,7 @@ export async function loadImageBySize(
149
150
  wantedWidth: number,
150
151
  wantedHeight: number,
151
152
  ): Promise<{ width: number; height: number; image: FileStream } | null> {
152
- const image =
153
+ const image: ImageDefinition | null =
153
154
  typeof imageOrId === "string"
154
155
  ? await ImageDefinition.load(imageOrId)
155
156
  : imageOrId;
@@ -184,19 +185,21 @@ export async function loadImageBySize(
184
185
  const bestTarget =
185
186
  sortedSizes.find((el) => el.match > 0.95) || sortedSizes.at(-1)!;
186
187
 
187
- const deepLoaded = await ImageDefinition.load(image.id, {
188
- resolve: {
189
- [bestTarget.size[2]]: true,
190
- },
191
- });
188
+ const file = image[bestTarget.size[2]];
189
+
190
+ if (!file) {
191
+ return null;
192
+ }
193
+
194
+ const loadedFile = await FileStream.load(file.id);
192
195
 
193
- if (deepLoaded === null || deepLoaded[bestTarget.size[2]] === undefined) {
196
+ if (!loadedFile) {
194
197
  return null;
195
198
  }
196
199
 
197
200
  return {
198
201
  width: bestTarget.size[0],
199
202
  height: bestTarget.size[1],
200
- image: deepLoaded[bestTarget.size[2]]!,
203
+ image: loadedFile,
201
204
  };
202
205
  }
@@ -92,7 +92,7 @@ export const Image = forwardRef<HTMLImageElement, ImageProps>(function Image(
92
92
  );
93
93
  const lazyPlaceholder = useMemo(
94
94
  () =>
95
- waitingLazyLoading ? URL.createObjectURL(emptyPixelBlob) : undefined,
95
+ waitingLazyLoading ? URL.createObjectURL(getEmptyPixelBlob()) : undefined,
96
96
  [waitingLazyLoading],
97
97
  );
98
98
 
@@ -197,14 +197,20 @@ function revokeObjectURL(url: string | undefined) {
197
197
  }
198
198
  }
199
199
 
200
- const emptyPixelBlob = new Blob(
201
- [
202
- Uint8Array.from(
203
- atob(
204
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
205
- ),
206
- (c) => c.charCodeAt(0),
207
- ),
208
- ],
209
- { type: "image/png" },
210
- );
200
+ let emptyPixelBlob: Blob | undefined;
201
+ function getEmptyPixelBlob() {
202
+ if (!emptyPixelBlob) {
203
+ emptyPixelBlob = new Blob(
204
+ [
205
+ Uint8Array.from(
206
+ atob(
207
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
208
+ ),
209
+ (c) => c.charCodeAt(0),
210
+ ),
211
+ ],
212
+ { type: "image/png" },
213
+ );
214
+ }
215
+ return emptyPixelBlob;
216
+ }
@@ -13,7 +13,7 @@ interface ImageProps extends Omit<HTMLImgAttributes, "width" | "height"> {
13
13
 
14
14
  const { imageId, width, height, ...rest }: ImageProps = $props();
15
15
 
16
- const imageState = new CoState(ImageDefinition, imageId);
16
+ const imageState = new CoState(ImageDefinition, () => imageId);
17
17
  let lastBestImage: [string, string] | null = null;
18
18
 
19
19
  /**
@@ -330,7 +330,7 @@ function parseSchemaAndResolve<
330
330
  };
331
331
  }
332
332
 
333
- class HttpRoute<
333
+ export class HttpRoute<
334
334
  RequestShape extends MessageShape = z.core.$ZodLooseShape,
335
335
  RequestResolve extends ResolveQuery<CoMapSchema<RequestShape>> = any,
336
336
  ResponseShape extends MessageShape = z.core.$ZodLooseShape,
@@ -113,4 +113,5 @@ export {
113
113
  experimental_defineRequest,
114
114
  JazzRequestError,
115
115
  isJazzRequestError,
116
+ type HttpRoute,
116
117
  } from "./coValues/request.js";