jazz-tools 0.11.4 → 0.11.6

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.
@@ -9,6 +9,7 @@ export declare class ImageDefinition extends CoMap {
9
9
  [res: `${number}x${number}`]: co<FileStream | null>;
10
10
  highestResAvailable(options?: {
11
11
  maxWidth?: number;
12
+ targetWidth?: number;
12
13
  }): {
13
14
  res: `${number}x${number}`;
14
15
  stream: FileStream;
@@ -1 +1 @@
1
- {"version":3,"file":"imageDef.d.ts","sourceRoot":"","sources":["../../../src/coValues/extensions/imageDef.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAuB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,sBAAsB;AACtB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,uBAA+B;IAC3C,kBAAkB,CAAC,yBAAa;IAEhC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAsB;IAChC,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAEpD,mBAAmB,CAAC,OAAO,CAAC,EAAE;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG;QAAE,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS;CAyCnE"}
1
+ {"version":3,"file":"imageDef.d.ts","sourceRoot":"","sources":["../../../src/coValues/extensions/imageDef.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAuB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,sBAAsB;AACtB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,uBAA+B;IAC3C,kBAAkB,CAAC,yBAAa;IAEhC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAsB;IAChC,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAEpD,mBAAmB,CAAC,OAAO,CAAC,EAAE;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG;QAAE,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS;CA+CnE"}
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  parseInviteLink,
36
36
  randomSessionProvider,
37
37
  subscribeToCoValue
38
- } from "./chunk-IOK4K3XC.js";
38
+ } from "./chunk-2LO7CLFD.js";
39
39
 
40
40
  // src/index.ts
41
41
  import { MAX_RECOMMENDED_TX_SIZE, cojsonInternals } from "cojson";
package/dist/testing.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  createAnonymousJazzContext,
6
6
  createJazzContext,
7
7
  randomSessionProvider
8
- } from "./chunk-IOK4K3XC.js";
8
+ } from "./chunk-2LO7CLFD.js";
9
9
 
10
10
  // src/testing.ts
11
11
  import { LocalNode } from "cojson";
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=imageDef.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageDef.test.d.ts","sourceRoot":"","sources":["../../src/tests/imageDef.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -17,11 +17,11 @@
17
17
  },
18
18
  "type": "module",
19
19
  "license": "MIT",
20
- "version": "0.11.4",
20
+ "version": "0.11.6",
21
21
  "dependencies": {
22
22
  "@scure/bip39": "^1.3.0",
23
23
  "fast-myers-diff": "^3.2.0",
24
- "cojson": "0.11.4"
24
+ "cojson": "0.11.6"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsup": "8.3.5",
@@ -12,6 +12,7 @@ export class ImageDefinition extends CoMap {
12
12
 
13
13
  highestResAvailable(options?: {
14
14
  maxWidth?: number;
15
+ targetWidth?: number;
15
16
  }): { res: `${number}x${number}`; stream: FileStream } | undefined {
16
17
  if (!subscriptionsScopes.get(this)) {
17
18
  console.warn(
@@ -19,33 +20,39 @@ export class ImageDefinition extends CoMap {
19
20
  );
20
21
  }
21
22
 
22
- const resolutions = Object.keys(this).filter(
23
- (key) =>
24
- key.match(/^\d+x\d+$/) &&
25
- (options?.maxWidth === undefined ||
26
- Number(key.split("x")[0]) <= options.maxWidth),
23
+ const resolutions = Object.keys(this).filter((key) =>
24
+ key.match(/^\d+x\d+$/),
27
25
  ) as `${number}x${number}`[];
28
26
 
29
- resolutions.sort((a, b) => {
27
+ let maxWidth = options?.maxWidth;
28
+
29
+ if (options?.targetWidth) {
30
+ const targetWidth = options.targetWidth;
31
+ const widths = resolutions.map((res) => Number(res.split("x")[0]));
32
+
33
+ maxWidth = Math.min(...widths.filter((w) => w >= targetWidth));
34
+ }
35
+
36
+ const validResolutions = resolutions.filter(
37
+ (key) => maxWidth === undefined || Number(key.split("x")[0]) <= maxWidth,
38
+ ) as `${number}x${number}`[];
39
+
40
+ // Sort the resolutions by width, smallest to largest
41
+ validResolutions.sort((a, b) => {
30
42
  const aWidth = Number(a.split("x")[0]);
31
43
  const bWidth = Number(b.split("x")[0]);
32
- return aWidth - bWidth;
44
+ return aWidth - bWidth; // Sort smallest to largest
33
45
  });
34
46
 
35
47
  let highestAvailableResolution: `${number}x${number}` | undefined;
36
48
 
37
- for (const resolution of resolutions) {
49
+ for (const resolution of validResolutions) {
38
50
  if (this[resolution] && this[resolution]?.getChunks()) {
39
51
  highestAvailableResolution = resolution;
40
- } else {
41
- return (
42
- highestAvailableResolution && {
43
- res: highestAvailableResolution,
44
- stream: this[highestAvailableResolution]!,
45
- }
46
- );
47
52
  }
48
53
  }
54
+
55
+ // Return the highest complete resolution if we found one
49
56
  return (
50
57
  highestAvailableResolution && {
51
58
  res: highestAvailableResolution,
@@ -470,6 +470,38 @@ describe("FileStream.loadAsBlob", async () => {
470
470
  });
471
471
  });
472
472
 
473
+ describe("FileStream progress tracking", async () => {
474
+ test("createFromBlob should report upload progress correctly", async () => {
475
+ // Create 5MB test blob
476
+ const testData = new Uint8Array(5 * 1024 * 1024); // 5MB instead of 500KB
477
+ for (let i = 0; i < testData.length; i++) testData[i] = i % 256;
478
+ const testBlob = new Blob([testData]);
479
+
480
+ // Collect progress updates
481
+ const progressUpdates: number[] = [];
482
+ await FileStream.createFromBlob(testBlob, {
483
+ onProgress: (progress) => progressUpdates.push(progress),
484
+ });
485
+
486
+ // Verify progress reporting
487
+ expect(progressUpdates.length).toBeGreaterThan(1);
488
+
489
+ // Check values between 0-1, increasing, with final=1
490
+ progressUpdates.forEach((p) => {
491
+ expect(p).toBeGreaterThanOrEqual(0);
492
+ expect(p).toBeLessThanOrEqual(1);
493
+ });
494
+
495
+ for (let i = 1; i < progressUpdates.length; i++) {
496
+ expect(progressUpdates[i]!).toBeGreaterThanOrEqual(
497
+ progressUpdates[i - 1]!,
498
+ );
499
+ }
500
+
501
+ expect(progressUpdates[progressUpdates.length - 1]).toBe(1);
502
+ });
503
+ });
504
+
473
505
  describe("waitForSync", async () => {
474
506
  test("CoFeed: should resolve when the value is uploaded", async () => {
475
507
  class TestStream extends CoFeed.Of(co.string) {}
@@ -0,0 +1,269 @@
1
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
+ import { describe, expect, test } from "vitest";
3
+ import { Account, FileStream, ImageDefinition, co } from "../exports.js";
4
+
5
+ const Crypto = await WasmCrypto.create();
6
+
7
+ describe("ImageDefinition", async () => {
8
+ const me = await Account.create({
9
+ creationProps: { name: "Test User" },
10
+ crypto: Crypto,
11
+ });
12
+
13
+ test("Construction with basic properties", () => {
14
+ const imageDef = ImageDefinition.create(
15
+ {
16
+ originalSize: [1920, 1080],
17
+ placeholderDataURL: "data:image/jpeg;base64,...",
18
+ },
19
+ { owner: me },
20
+ );
21
+
22
+ expect(imageDef.originalSize).toEqual([1920, 1080]);
23
+ expect(imageDef.placeholderDataURL).toBe("data:image/jpeg;base64,...");
24
+ });
25
+
26
+ test("highestResAvailable with no resolutions", () => {
27
+ const imageDef = ImageDefinition.create(
28
+ {
29
+ originalSize: [1920, 1080],
30
+ },
31
+ { owner: me },
32
+ );
33
+
34
+ const result = imageDef.highestResAvailable();
35
+ expect(result).toBeUndefined();
36
+ });
37
+
38
+ test("highestResAvailable with single resolution", () => {
39
+ const imageDef = ImageDefinition.create(
40
+ {
41
+ originalSize: [1920, 1080],
42
+ },
43
+ { owner: me },
44
+ );
45
+
46
+ const stream = FileStream.create({ owner: me });
47
+ stream.start({ mimeType: "image/jpeg" });
48
+ stream.push(new Uint8Array([1, 2, 3]));
49
+ stream.end();
50
+
51
+ imageDef["1920x1080"] = stream;
52
+
53
+ const result = imageDef.highestResAvailable();
54
+ expect(result).toBeDefined();
55
+ expect(result?.res).toBe("1920x1080");
56
+ expect(result?.stream).toStrictEqual(stream);
57
+ });
58
+
59
+ test("highestResAvailable with multiple resolutions", () => {
60
+ const imageDef = ImageDefinition.create(
61
+ {
62
+ originalSize: [1920, 1080],
63
+ },
64
+ { owner: me },
65
+ );
66
+
67
+ const stream1 = FileStream.create({ owner: me });
68
+ stream1.start({ mimeType: "image/jpeg" });
69
+ stream1.push(new Uint8Array([1, 2, 3]));
70
+ stream1.end();
71
+
72
+ const stream2 = FileStream.create({ owner: me });
73
+ stream2.start({ mimeType: "image/jpeg" });
74
+ stream2.push(new Uint8Array([4, 5, 6]));
75
+ stream2.end();
76
+
77
+ imageDef["1920x1080"] = stream1;
78
+ imageDef["1280x720"] = stream2;
79
+
80
+ const result = imageDef.highestResAvailable();
81
+ expect(result).toBeDefined();
82
+ expect(result?.res).toBe("1920x1080");
83
+ expect(result?.stream).toStrictEqual(stream1);
84
+ });
85
+
86
+ test("highestResAvailable with maxWidth option", () => {
87
+ const imageDef = ImageDefinition.create(
88
+ {
89
+ originalSize: [1920, 1080],
90
+ },
91
+ { owner: me },
92
+ );
93
+
94
+ const stream1 = FileStream.create({ owner: me });
95
+ stream1.start({ mimeType: "image/jpeg" });
96
+ stream1.push(new Uint8Array([1, 2, 3]));
97
+ stream1.end();
98
+
99
+ const stream2 = FileStream.create({ owner: me });
100
+ stream2.start({ mimeType: "image/jpeg" });
101
+ stream2.push(new Uint8Array([4, 5, 6]));
102
+ stream2.end();
103
+
104
+ imageDef["1920x1080"] = stream1;
105
+ imageDef["1280x720"] = stream2;
106
+
107
+ const result = imageDef.highestResAvailable({ maxWidth: 1500 });
108
+ expect(result).toBeDefined();
109
+ expect(result?.res).toBe("1280x720");
110
+ expect(result?.stream).toStrictEqual(stream2);
111
+ });
112
+
113
+ test("highestResAvailable with missing chunks", () => {
114
+ const imageDef = ImageDefinition.create(
115
+ {
116
+ originalSize: [1920, 1080],
117
+ },
118
+ { owner: me },
119
+ );
120
+
121
+ const stream1 = FileStream.create({ owner: me });
122
+ stream1.start({ mimeType: "image/jpeg" });
123
+ stream1.push(new Uint8Array([1, 2, 3]));
124
+ stream1.end();
125
+
126
+ const stream2 = FileStream.create({ owner: me });
127
+ stream2.start({ mimeType: "image/jpeg" });
128
+ // Don't end stream2, so it has no chunks
129
+
130
+ imageDef["1920x1080"] = stream1;
131
+ imageDef["1280x720"] = stream2;
132
+
133
+ const result = imageDef.highestResAvailable();
134
+ expect(result).toBeDefined();
135
+ expect(result?.res).toBe("1920x1080");
136
+ expect(result?.stream).toStrictEqual(stream1);
137
+ });
138
+
139
+ test("highestResAvailable with missing chunks in middle stream", () => {
140
+ const imageDef = ImageDefinition.create(
141
+ {
142
+ originalSize: [1920, 1080],
143
+ },
144
+ { owner: me },
145
+ );
146
+
147
+ const stream1 = FileStream.create({ owner: me });
148
+ stream1.start({ mimeType: "image/jpeg" });
149
+ stream1.push(new Uint8Array([1, 2, 3]));
150
+ stream1.end();
151
+
152
+ const stream2 = FileStream.create({ owner: me });
153
+ stream2.start({ mimeType: "image/jpeg" });
154
+ // Don't end stream2, so it has no chunks
155
+
156
+ const stream3 = FileStream.create({ owner: me });
157
+ stream3.start({ mimeType: "image/jpeg" });
158
+ stream3.push(new Uint8Array([7, 8, 9]));
159
+ stream3.end();
160
+
161
+ imageDef["1920x1080"] = stream1;
162
+ imageDef["1280x720"] = stream2;
163
+ imageDef["1024x576"] = stream3;
164
+
165
+ const result = imageDef.highestResAvailable();
166
+ expect(result).toBeDefined();
167
+ expect(result?.res).toBe("1920x1080");
168
+ expect(result?.stream).toStrictEqual(stream1);
169
+ });
170
+
171
+ test("highestResAvailable with non-resolution keys", () => {
172
+ const imageDef = ImageDefinition.create(
173
+ {
174
+ originalSize: [1920, 1080],
175
+ },
176
+ { owner: me },
177
+ );
178
+
179
+ const stream = FileStream.create({ owner: me });
180
+ stream.start({ mimeType: "image/jpeg" });
181
+ stream.push(new Uint8Array([1, 2, 3]));
182
+ stream.end();
183
+
184
+ // @ts-expect-error - Testing invalid key
185
+ imageDef["invalid-key"] = stream;
186
+
187
+ const result = imageDef.highestResAvailable();
188
+ expect(result).toBeUndefined();
189
+ });
190
+
191
+ test("highestResAvailable with targetWidth option", () => {
192
+ const imageDef = ImageDefinition.create(
193
+ {
194
+ originalSize: [1920, 1080],
195
+ },
196
+ { owner: me },
197
+ );
198
+
199
+ const stream1 = FileStream.create({ owner: me });
200
+ stream1.start({ mimeType: "image/jpeg" });
201
+ stream1.push(new Uint8Array([1, 2, 3]));
202
+ stream1.end();
203
+
204
+ const stream2 = FileStream.create({ owner: me });
205
+ stream2.start({ mimeType: "image/jpeg" });
206
+ stream2.push(new Uint8Array([4, 5, 6]));
207
+ stream2.end();
208
+
209
+ const stream3 = FileStream.create({ owner: me });
210
+ stream3.start({ mimeType: "image/jpeg" });
211
+ stream3.push(new Uint8Array([7, 8, 9]));
212
+ stream3.end();
213
+
214
+ imageDef["1920x1080"] = stream1;
215
+ imageDef["1280x720"] = stream2;
216
+ imageDef["800x450"] = stream3;
217
+
218
+ // Should return 1280x720 as it's the smallest resolution >= 1000px
219
+ const result1 = imageDef.highestResAvailable({ targetWidth: 1000 });
220
+ expect(result1).toBeDefined();
221
+ expect(result1?.res).toBe("1280x720");
222
+ expect(result1?.stream).toStrictEqual(stream2);
223
+
224
+ // Should return 800x450 as it's the smallest resolution >= 700px
225
+ const result2 = imageDef.highestResAvailable({ targetWidth: 700 });
226
+ expect(result2).toBeDefined();
227
+ expect(result2?.res).toBe("800x450");
228
+ expect(result2?.stream).toStrictEqual(stream3);
229
+
230
+ // Should return 1920x1080 as it's the smallest resolution >= 1500px
231
+ const result3 = imageDef.highestResAvailable({ targetWidth: 1500 });
232
+ expect(result3).toBeDefined();
233
+ expect(result3?.res).toBe("1920x1080");
234
+ expect(result3?.stream).toStrictEqual(stream1);
235
+ });
236
+
237
+ test("highestResAvailable with targetWidth and incomplete streams", () => {
238
+ const imageDef = ImageDefinition.create(
239
+ {
240
+ originalSize: [1920, 1080],
241
+ },
242
+ { owner: me },
243
+ );
244
+
245
+ const stream1 = FileStream.create({ owner: me });
246
+ stream1.start({ mimeType: "image/jpeg" });
247
+ stream1.push(new Uint8Array([1, 2, 3]));
248
+ stream1.end();
249
+
250
+ const stream2 = FileStream.create({ owner: me });
251
+ stream2.start({ mimeType: "image/jpeg" });
252
+ // Don't end stream2, so it has no chunks
253
+
254
+ const stream3 = FileStream.create({ owner: me });
255
+ stream3.start({ mimeType: "image/jpeg" });
256
+ stream3.push(new Uint8Array([7, 8, 9]));
257
+ stream3.end();
258
+
259
+ imageDef["1920x1080"] = stream1;
260
+ imageDef["1280x720"] = stream2;
261
+ imageDef["800x450"] = stream3;
262
+
263
+ // Should skip 1280x720 as it's incomplete and return 1920x1080
264
+ const result = imageDef.highestResAvailable({ targetWidth: 1000 });
265
+ expect(result).toBeDefined();
266
+ expect(result?.res).toBe("800x450");
267
+ expect(result?.stream).toStrictEqual(stream1);
268
+ });
269
+ });