jazz-tools 0.16.5 → 0.17.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 (170) hide show
  1. package/.svelte-kit/__package__/index.d.ts +1 -0
  2. package/.svelte-kit/__package__/index.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/index.js +1 -0
  4. package/.svelte-kit/__package__/media/image.svelte +131 -0
  5. package/.svelte-kit/__package__/media/image.svelte.d.ts +10 -0
  6. package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -0
  7. package/.svelte-kit/__package__/media/index.d.ts +2 -0
  8. package/.svelte-kit/__package__/media/index.d.ts.map +1 -0
  9. package/.svelte-kit/__package__/media/index.js +1 -0
  10. package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts +2 -0
  11. package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts.map +1 -0
  12. package/.svelte-kit/__package__/tests/media/image.svelte.test.js +430 -0
  13. package/.svelte-kit/__package__/tests/testUtils.d.ts +11 -0
  14. package/.svelte-kit/__package__/tests/testUtils.d.ts.map +1 -0
  15. package/.svelte-kit/__package__/tests/testUtils.js +17 -0
  16. package/.svelte-kit/__package__/tests/types.d.ts +3 -0
  17. package/.turbo/turbo-build.log +44 -48
  18. package/CHANGELOG.md +28 -0
  19. package/dist/{chunk-H3BIFFQG.js → chunk-2SH44VLX.js} +35 -40
  20. package/dist/chunk-2SH44VLX.js.map +1 -0
  21. package/dist/index.js +1 -3
  22. package/dist/index.js.map +1 -1
  23. package/dist/inspector/{custom-element-TUXKXSZU.js → custom-element-I7L56H6B.js} +3 -5
  24. package/dist/inspector/{custom-element-TUXKXSZU.js.map → custom-element-I7L56H6B.js.map} +1 -1
  25. package/dist/inspector/index.js +2 -4
  26. package/dist/inspector/index.js.map +1 -1
  27. package/dist/inspector/register-custom-element.js +1 -1
  28. package/dist/inspector/viewer/co-plain-text-view.d.ts +1 -1
  29. package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
  30. package/dist/inspector/viewer/role-display.d.ts.map +1 -1
  31. package/dist/media/chunk-BBSS3NEY.js +211 -0
  32. package/dist/media/chunk-BBSS3NEY.js.map +1 -0
  33. package/dist/media/create-image.d.ts +48 -0
  34. package/dist/media/create-image.d.ts.map +1 -0
  35. package/dist/media/create-image.test.d.ts +2 -0
  36. package/dist/media/create-image.test.d.ts.map +1 -0
  37. package/dist/media/index.browser.d.ts +15 -0
  38. package/dist/media/index.browser.d.ts.map +1 -0
  39. package/dist/media/index.browser.js +113 -0
  40. package/dist/media/index.browser.js.map +1 -0
  41. package/dist/media/index.d.ts +53 -0
  42. package/dist/media/index.d.ts.map +1 -0
  43. package/dist/media/index.js +13 -0
  44. package/dist/media/index.js.map +1 -0
  45. package/dist/media/index.native.d.ts +17 -0
  46. package/dist/media/index.native.d.ts.map +1 -0
  47. package/dist/media/index.native.js +126 -0
  48. package/dist/media/index.native.js.map +1 -0
  49. package/dist/media/utils.d.ts +17 -0
  50. package/dist/media/utils.d.ts.map +1 -0
  51. package/dist/media/utils.test.d.ts +2 -0
  52. package/dist/media/utils.test.d.ts.map +1 -0
  53. package/dist/react/index.d.ts +1 -2
  54. package/dist/react/index.d.ts.map +1 -1
  55. package/dist/react/index.js +176 -59
  56. package/dist/react/index.js.map +1 -1
  57. package/dist/react/media/image.d.ts +62 -0
  58. package/dist/react/media/image.d.ts.map +1 -0
  59. package/dist/react/ssr.d.ts.map +1 -1
  60. package/dist/react/ssr.js.map +1 -1
  61. package/dist/react/tests/media/image.test.d.ts +2 -0
  62. package/dist/react/tests/media/image.test.d.ts.map +1 -0
  63. package/dist/react/tests/testUtils.d.ts.map +1 -1
  64. package/dist/react-core/auth/PassphraseAuth.d.ts +1 -1
  65. package/dist/react-core/auth/PassphraseAuth.d.ts.map +1 -1
  66. package/dist/react-core/index.js +1 -3
  67. package/dist/react-core/index.js.map +1 -1
  68. package/dist/react-core/tests/testUtils.d.ts.map +1 -1
  69. package/dist/react-core/tests/useDemoAuth.test.d.ts +2 -0
  70. package/dist/react-core/tests/useDemoAuth.test.d.ts.map +1 -0
  71. package/dist/react-native-core/index.d.ts +1 -1
  72. package/dist/react-native-core/index.d.ts.map +1 -1
  73. package/dist/react-native-core/index.js +84 -66
  74. package/dist/react-native-core/index.js.map +1 -1
  75. package/dist/react-native-core/media/image.d.ts +93 -0
  76. package/dist/react-native-core/media/image.d.ts.map +1 -0
  77. package/dist/react-native-core/testing.d.ts +2 -0
  78. package/dist/react-native-core/testing.d.ts.map +1 -0
  79. package/dist/svelte/index.d.ts +1 -0
  80. package/dist/svelte/index.d.ts.map +1 -1
  81. package/dist/svelte/index.js +1 -0
  82. package/dist/svelte/media/image.svelte +131 -0
  83. package/dist/svelte/media/image.svelte.d.ts +10 -0
  84. package/dist/svelte/media/image.svelte.d.ts.map +1 -0
  85. package/dist/svelte/media/index.d.ts +2 -0
  86. package/dist/svelte/media/index.d.ts.map +1 -0
  87. package/dist/svelte/media/index.js +1 -0
  88. package/dist/svelte/tests/media/image.svelte.test.d.ts +2 -0
  89. package/dist/svelte/tests/media/image.svelte.test.d.ts.map +1 -0
  90. package/dist/svelte/tests/media/image.svelte.test.js +430 -0
  91. package/dist/svelte/tests/testUtils.d.ts +11 -0
  92. package/dist/svelte/tests/testUtils.d.ts.map +1 -0
  93. package/dist/svelte/tests/testUtils.js +17 -0
  94. package/dist/svelte/tests/types.d.ts +3 -0
  95. package/dist/testing.js +1 -1
  96. package/dist/testing.js.map +1 -1
  97. package/dist/tools/coValues/coFeed.d.ts +15 -0
  98. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  99. package/dist/tools/coValues/deepLoading.d.ts +10 -10
  100. package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
  101. package/dist/tools/coValues/extensions/imageDef.d.ts +3 -9
  102. package/dist/tools/coValues/extensions/imageDef.d.ts.map +1 -1
  103. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +1 -0
  104. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  105. package/dist/tools/index.d.ts +1 -1
  106. package/dist/tools/index.d.ts.map +1 -1
  107. package/dist/tools/testing.d.ts.map +1 -1
  108. package/package.json +12 -12
  109. package/src/inspector/viewer/co-plain-text-view.tsx +1 -5
  110. package/src/inspector/viewer/co-stream-view.tsx +1 -1
  111. package/src/inspector/viewer/role-display.tsx +4 -1
  112. package/src/{browser-media-images/index.test.browser.ts → media/create-image.test.ts} +146 -24
  113. package/src/media/create-image.ts +180 -0
  114. package/src/media/index.browser.ts +150 -0
  115. package/src/media/index.native.ts +153 -0
  116. package/src/media/index.ts +61 -0
  117. package/src/media/utils.test.ts +327 -0
  118. package/src/media/utils.ts +202 -0
  119. package/src/react/index.ts +1 -2
  120. package/src/react/media/image.tsx +210 -0
  121. package/src/react/ssr.ts +1 -3
  122. package/src/react/tests/media/image.test.tsx +588 -0
  123. package/src/react/tests/testUtils.tsx +2 -10
  124. package/src/react-core/auth/PassphraseAuth.tsx +1 -5
  125. package/src/react-core/tests/testUtils.tsx +2 -10
  126. package/src/react-native-core/index.ts +1 -1
  127. package/src/react-native-core/media/image.tsx +159 -0
  128. package/src/svelte/index.ts +1 -0
  129. package/src/svelte/media/image.svelte +131 -0
  130. package/src/svelte/media/index.ts +1 -0
  131. package/src/svelte/tests/media/image.svelte.test.ts +583 -0
  132. package/src/svelte/tests/testUtils.ts +33 -0
  133. package/src/svelte/tests/types.d.ts +3 -0
  134. package/src/tools/coValues/coFeed.ts +40 -7
  135. package/src/tools/coValues/deepLoading.ts +46 -32
  136. package/src/tools/coValues/extensions/imageDef.ts +3 -49
  137. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +6 -0
  138. package/src/tools/index.ts +0 -1
  139. package/src/tools/testing.ts +3 -1
  140. package/src/tools/tests/coList.test.ts +1 -1
  141. package/src/tools/tests/coMap.record.test-d.ts +105 -0
  142. package/src/tools/tests/coMap.record.test.ts +48 -2
  143. package/src/tools/tests/coMap.test-d.ts +50 -0
  144. package/src/tools/tests/coOptional.test.ts +3 -1
  145. package/tsconfig.json +1 -0
  146. package/tsup.config.ts +4 -9
  147. package/vitest.config.ts +14 -21
  148. package/dist/browser-media-images/index.d.ts +0 -9
  149. package/dist/browser-media-images/index.d.ts.map +0 -1
  150. package/dist/browser-media-images/index.js +0 -72
  151. package/dist/browser-media-images/index.js.map +0 -1
  152. package/dist/browser-media-images/index.test.browser.d.ts +0 -2
  153. package/dist/browser-media-images/index.test.browser.d.ts.map +0 -1
  154. package/dist/chunk-H3BIFFQG.js.map +0 -1
  155. package/dist/react/media.d.ts +0 -24
  156. package/dist/react/media.d.ts.map +0 -1
  157. package/dist/react-native-core/media.d.ts +0 -24
  158. package/dist/react-native-core/media.d.ts.map +0 -1
  159. package/dist/react-native-media-images/index.d.ts +0 -7
  160. package/dist/react-native-media-images/index.d.ts.map +0 -1
  161. package/dist/react-native-media-images/index.js +0 -177
  162. package/dist/react-native-media-images/index.js.map +0 -1
  163. package/dist/tools/tests/imageDef.test.d.ts +0 -2
  164. package/dist/tools/tests/imageDef.test.d.ts.map +0 -1
  165. package/src/browser-media-images/index.ts +0 -131
  166. package/src/react/media.tsx +0 -74
  167. package/src/react/scratch.tsx +0 -50
  168. package/src/react-native-core/media.tsx +0 -79
  169. package/src/react-native-media-images/index.ts +0 -238
  170. package/src/tools/tests/imageDef.test.ts +0 -278
@@ -97,6 +97,27 @@ type onErrorNullEnabled<Depth> = Depth extends { $onError: null }
97
97
  ? null
98
98
  : never;
99
99
 
100
+ type CoMapLikeLoaded<
101
+ V extends object,
102
+ Depth,
103
+ DepthLimit extends number,
104
+ CurrentDepth extends number[],
105
+ > = {
106
+ -readonly [Key in keyof Depth]-?: Key extends CoKeys<V>
107
+ ? NonNullable<V[Key]> extends CoValue
108
+ ?
109
+ | DeeplyLoaded<
110
+ NonNullable<V[Key]>,
111
+ Depth[Key],
112
+ DepthLimit,
113
+ [0, ...CurrentDepth]
114
+ >
115
+ | (undefined extends V[Key] ? undefined : never)
116
+ | onErrorNullEnabled<Depth[Key]>
117
+ : never
118
+ : never;
119
+ } & V;
120
+
100
121
  export type DeeplyLoaded<
101
122
  V,
102
123
  Depth,
@@ -126,38 +147,31 @@ export type DeeplyLoaded<
126
147
  : V
127
148
  : // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
128
149
  [V] extends [{ _type: "CoMap" | "Group" | "Account" }]
129
- ? ItemsSym extends keyof V
130
- ? Depth extends { $each: infer ItemDepth }
131
- ? // Deeply loaded Record-like CoMap
132
- {
133
- [key: string]:
134
- | DeeplyLoaded<
135
- NonNullable<V[ItemsSym]>,
136
- ItemDepth,
137
- DepthLimit,
138
- [0, ...CurrentDepth]
139
- >
140
- | onErrorNullEnabled<Depth["$each"]>;
141
- } & V // same reason as in CoList
142
- : never
143
- : keyof Depth extends never // Depth = {}
144
- ? V
145
- : // Deeply loaded CoMap
146
- {
147
- -readonly [Key in keyof Depth]-?: Key extends CoKeys<V>
148
- ? NonNullable<V[Key]> extends CoValue
149
- ?
150
- | DeeplyLoaded<
151
- NonNullable<V[Key]>,
152
- Depth[Key],
153
- DepthLimit,
154
- [0, ...CurrentDepth]
155
- >
156
- | (undefined extends V[Key] ? undefined : never)
157
- | onErrorNullEnabled<Depth[Key]>
158
- : never
159
- : never;
160
- } & V // same reason as in CoList
150
+ ? // If Depth = {} return V in any case
151
+ keyof Depth extends never
152
+ ? V
153
+ : // 1. Record-like CoMap
154
+ ItemsSym extends keyof V
155
+ ? // 1.1. Deeply loaded Record-like CoMap with { $each: true | {$onError: null} }
156
+ Depth extends { $each: infer ItemDepth }
157
+ ? {
158
+ [key: string]:
159
+ | DeeplyLoaded<
160
+ NonNullable<V[ItemsSym]>,
161
+ ItemDepth,
162
+ DepthLimit,
163
+ [0, ...CurrentDepth]
164
+ >
165
+ | onErrorNullEnabled<Depth["$each"]>;
166
+ } & V // same reason as in CoList
167
+ : // 1.2. Deeply loaded Record-like CoMap with { [key: string]: true }
168
+ string extends keyof Depth
169
+ ? // if at least one key is `string`, then we treat the resolve as it was empty
170
+ DeeplyLoaded<V, {}, DepthLimit, [0, ...CurrentDepth]> & V
171
+ : // 1.3 Deeply loaded Record-like CoMap with single keys
172
+ CoMapLikeLoaded<V, Depth, DepthLimit, CurrentDepth>
173
+ : // 2. Deeply loaded CoMap
174
+ CoMapLikeLoaded<V, Depth, DepthLimit, CurrentDepth>
161
175
  : [V] extends [
162
176
  {
163
177
  _type: "CoStream";
@@ -3,58 +3,12 @@ import { Loaded, coFileStreamDefiner, coMapDefiner } from "../../internal.js";
3
3
 
4
4
  // avoiding circularity by using the standalone definers instead of `co`
5
5
  const ImageDefinitionBase = coMapDefiner({
6
+ original: coFileStreamDefiner(),
6
7
  originalSize: z.tuple([z.number(), z.number()]),
7
8
  placeholderDataURL: z.string().optional(),
9
+ progressive: z.boolean(),
8
10
  }).catchall(coFileStreamDefiner());
9
11
 
10
12
  /** @category Media */
11
- export const ImageDefinition = Object.assign({}, ImageDefinitionBase, {
12
- highestResAvailable(
13
- imageDef: ImageDefinition,
14
- options?: {
15
- maxWidth?: number;
16
- targetWidth?: number;
17
- },
18
- ) {
19
- const resolutions = Object.keys(imageDef).filter((key) =>
20
- key.match(/^\d+x\d+$/),
21
- ) as `${number}x${number}`[];
22
-
23
- let maxWidth = options?.maxWidth;
24
-
25
- if (options?.targetWidth) {
26
- const targetWidth = options.targetWidth;
27
- const widths = resolutions.map((res) => Number(res.split("x")[0]));
28
-
29
- maxWidth = Math.min(...widths.filter((w) => w >= targetWidth));
30
- }
31
-
32
- const validResolutions = resolutions.filter(
33
- (key) => maxWidth === undefined || Number(key.split("x")[0]) <= maxWidth,
34
- ) as `${number}x${number}`[];
35
-
36
- // Sort the resolutions by width, smallest to largest
37
- validResolutions.sort((a, b) => {
38
- const aWidth = Number(a.split("x")[0]);
39
- const bWidth = Number(b.split("x")[0]);
40
- return aWidth - bWidth; // Sort smallest to largest
41
- });
42
-
43
- let highestAvailableResolution: `${number}x${number}` | undefined;
44
-
45
- for (const resolution of validResolutions) {
46
- if (imageDef[resolution] && imageDef[resolution]?.getChunks()) {
47
- highestAvailableResolution = resolution;
48
- }
49
- }
50
-
51
- // Return the highest complete resolution if we found one
52
- return (
53
- highestAvailableResolution && {
54
- res: highestAvailableResolution,
55
- stream: imageDef[highestAvailableResolution]!,
56
- }
57
- );
58
- },
59
- });
13
+ export const ImageDefinition = ImageDefinitionBase;
60
14
  export type ImageDefinition = Loaded<typeof ImageDefinition>;
@@ -42,6 +42,12 @@ export class FileStreamSchema implements CoreFileStreamSchema {
42
42
  return this.coValueClass.createFromBlob(blob, options);
43
43
  }
44
44
 
45
+ createFromArrayBuffer(
46
+ ...args: Parameters<typeof FileStream.createFromArrayBuffer>
47
+ ) {
48
+ return this.coValueClass.createFromArrayBuffer(...args);
49
+ }
50
+
45
51
  loadAsBlob(
46
52
  id: string,
47
53
  options?: {
@@ -1,7 +1,6 @@
1
1
  export * from "./exports.js";
2
2
 
3
3
  export {
4
- MAX_RECOMMENDED_TX_SIZE,
5
4
  cojsonInternals,
6
5
  logger,
7
6
  LogLevel,
@@ -305,7 +305,9 @@ export async function linkAccounts(
305
305
 
306
306
  export async function setupJazzTestSync({
307
307
  asyncPeers = false,
308
- }: { asyncPeers?: boolean } = {}) {
308
+ }: {
309
+ asyncPeers?: boolean;
310
+ } = {}) {
309
311
  if (syncServer.current) {
310
312
  syncServer.current.gracefulShutdown();
311
313
  }
@@ -1,5 +1,5 @@
1
1
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
- import { beforeEach, describe, expect, test, vi } from "vitest";
2
+ import { assert, beforeEach, describe, expect, test, vi } from "vitest";
3
3
  import { Account, Group, subscribeToCoValue, z } from "../index.js";
4
4
  import {
5
5
  Loaded,
@@ -173,6 +173,111 @@ describe("CoMap.Record", () => {
173
173
  matches(loadedPerson);
174
174
  });
175
175
 
176
+ test("loading a record with property resolve", async () => {
177
+ const Dog = co.map({
178
+ name: z.string(),
179
+ breed: z.string(),
180
+ });
181
+
182
+ const Person = co.record(z.string(), Dog);
183
+
184
+ const person = Person.create({
185
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
186
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
187
+ });
188
+
189
+ const loadedPerson = await Person.load(person.id, {
190
+ resolve: {
191
+ pet1: true,
192
+ },
193
+ });
194
+
195
+ type Expect = NonNullable<typeof loadedPerson> extends never
196
+ ? "error: is never"
197
+ : "ok";
198
+
199
+ expectTypeOf("ok" as const).toEqualTypeOf<Expect>();
200
+
201
+ expectTypeOf(loadedPerson?.pet1).toEqualTypeOf<
202
+ Loaded<typeof Dog> | undefined
203
+ >();
204
+ expectTypeOf(loadedPerson?.pet3).toEqualTypeOf<
205
+ Loaded<typeof Dog> | undefined | null
206
+ >();
207
+ });
208
+
209
+ test("loading a record with generic string resolve", async () => {
210
+ const Dog = co.map({
211
+ name: z.string(),
212
+ breed: z.string(),
213
+ });
214
+
215
+ const Person = co.record(z.string(), Dog);
216
+
217
+ const person = Person.create({
218
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
219
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
220
+ });
221
+
222
+ const userId: string = "pet1";
223
+ const userId2: string = "pet3";
224
+
225
+ const loadedPerson = await Person.load(person.id, {
226
+ resolve: {
227
+ [userId]: true,
228
+ pet2: true,
229
+ [userId2]: {
230
+ $onError: null,
231
+ },
232
+ },
233
+ });
234
+
235
+ type Expect = NonNullable<typeof loadedPerson> extends never
236
+ ? "error: is never"
237
+ : "ok";
238
+
239
+ expectTypeOf("ok" as const).toEqualTypeOf<Expect>();
240
+
241
+ expectTypeOf(loadedPerson?.pet1).toEqualTypeOf<
242
+ Loaded<typeof Dog> | undefined | null
243
+ >();
244
+ expectTypeOf(loadedPerson?.pet2).toEqualTypeOf<
245
+ Loaded<typeof Dog> | undefined | null
246
+ >();
247
+ expectTypeOf(loadedPerson?.pet3).toEqualTypeOf<
248
+ Loaded<typeof Dog> | undefined | null
249
+ >();
250
+ });
251
+
252
+ test("loading a record with empty resolve", async () => {
253
+ const Dog = co.map({
254
+ name: z.string(),
255
+ breed: z.string(),
256
+ });
257
+
258
+ const Person = co.record(z.string(), Dog);
259
+
260
+ const person = Person.create({
261
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
262
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
263
+ });
264
+
265
+ const loadedPerson = await Person.load(person.id);
266
+
267
+ type Expect = NonNullable<typeof loadedPerson> extends never
268
+ ? "error: is never"
269
+ : "ok";
270
+
271
+ expectTypeOf("ok" as const).toEqualTypeOf<Expect>();
272
+
273
+ expectTypeOf(loadedPerson?.pet1).toEqualTypeOf<
274
+ Loaded<typeof Dog> | undefined | null
275
+ >();
276
+ expectTypeOf(loadedPerson?.pet3).toEqualTypeOf<
277
+ Loaded<typeof Dog> | undefined | null
278
+ >();
279
+ });
280
+
176
281
  test("loading a record with $onError", async () => {
177
282
  const Dog = co.map({
178
283
  name: z.string(),
@@ -8,8 +8,7 @@ import {
8
8
  test,
9
9
  vi,
10
10
  } from "vitest";
11
- import { Group, co, z } from "../exports.js";
12
- import { InstanceOrPrimitiveOfSchema } from "../implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.js";
11
+ import { FileStream, Group, co, z } from "../exports.js";
13
12
  import { Loaded } from "../implementation/zodSchema/zodSchema.js";
14
13
  import { Account } from "../index.js";
15
14
  import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
@@ -213,6 +212,51 @@ describe("CoMap.Record", async () => {
213
212
  expect(loadedPerson.pet2?.name).toEqual("Fido");
214
213
  });
215
214
 
215
+ test("loading a locally available record with single resolve", async () => {
216
+ const Dog = co.map({
217
+ name: z.string(),
218
+ breed: z.string(),
219
+ });
220
+
221
+ const Person = co.record(z.string(), Dog);
222
+
223
+ const person = Person.create({
224
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
225
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
226
+ });
227
+
228
+ const loadedPerson = await Person.load(person.id, {
229
+ resolve: {
230
+ pet1: true,
231
+ },
232
+ });
233
+
234
+ assert(loadedPerson);
235
+ expect(loadedPerson.pet1?.name).toEqual("Rex");
236
+ });
237
+
238
+ test("loading a locally available record with unavailable single resolve", async () => {
239
+ const Dog = co.map({
240
+ name: z.string(),
241
+ breed: z.string(),
242
+ });
243
+
244
+ const Person = co.record(z.string(), Dog);
245
+
246
+ const person = Person.create({
247
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
248
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
249
+ });
250
+
251
+ const loadedPerson = await Person.load(person.id, {
252
+ resolve: {
253
+ pet3: true,
254
+ },
255
+ });
256
+
257
+ expect(loadedPerson).toEqual(null);
258
+ });
259
+
216
260
  test("loading a locally available record using autoload for the refs", async () => {
217
261
  const Dog = co.map({
218
262
  name: z.string(),
@@ -328,6 +372,8 @@ describe("CoMap.Record", async () => {
328
372
  type: "repro",
329
373
  name: "John",
330
374
  image: co.image().create({
375
+ original: FileStream.create(),
376
+ progressive: false,
331
377
  originalSize: [1920, 1080],
332
378
  }),
333
379
  });
@@ -542,6 +542,56 @@ describe("CoMap resolution", async () => {
542
542
  > | null>();
543
543
  });
544
544
 
545
+ test("partial loading a map with string resolve", async () => {
546
+ const Dog = co.map({
547
+ name: z.string(),
548
+ breed: z.string(),
549
+ });
550
+
551
+ const Person = co.map({
552
+ name: z.string(),
553
+ age: z.number(),
554
+ dog1: Dog,
555
+ dog2: Dog,
556
+ });
557
+
558
+ const person = Person.create({
559
+ name: "John",
560
+ age: 20,
561
+ dog1: Dog.create({ name: "Rex", breed: "Labrador" }),
562
+ dog2: Dog.create({ name: "Fido", breed: "Poodle" }),
563
+ });
564
+
565
+ const userId: string = "dog1";
566
+
567
+ const loadedPerson = await Person.load(person.id, {
568
+ resolve: {
569
+ [userId]: true,
570
+ },
571
+ });
572
+
573
+ type ExpectedType = {
574
+ name: string;
575
+ age: number;
576
+ dog1: Loaded<typeof Dog> | null;
577
+ dog2: Loaded<typeof Dog> | null;
578
+ } | null;
579
+
580
+ function matches(value: ExpectedType) {
581
+ return value;
582
+ }
583
+
584
+ matches(loadedPerson);
585
+
586
+ assert(loadedPerson);
587
+ expectTypeOf<typeof loadedPerson.dog1>().toEqualTypeOf<Loaded<
588
+ typeof Dog
589
+ > | null>();
590
+ expectTypeOf<typeof loadedPerson.dog2>().toEqualTypeOf<Loaded<
591
+ typeof Dog
592
+ > | null>();
593
+ });
594
+
545
595
  test("loading a map with deep resolve and $onError", async () => {
546
596
  const Dog = co.map({
547
597
  name: z.string(),
@@ -1,5 +1,5 @@
1
1
  import { beforeEach, describe, expect, test } from "vitest";
2
- import { CoPlainText, co, z } from "../exports.js";
2
+ import { CoPlainText, FileStream, co, z } from "../exports.js";
3
3
  import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
4
4
 
5
5
  describe("co.optional", () => {
@@ -69,6 +69,8 @@ describe("co.optional", () => {
69
69
  schema.fileStream = Schema.shape.fileStream.innerType.create();
70
70
  schema.image = Schema.shape.image.innerType.create({
71
71
  originalSize: [1920, 1080],
72
+ original: FileStream.create(),
73
+ progressive: false,
72
74
  });
73
75
  schema.record = Schema.shape.record.innerType.create({ field: "hello" });
74
76
  schema.map = Schema.shape.map.innerType.create({ field: "hello" });
package/tsconfig.json CHANGED
@@ -21,6 +21,7 @@
21
21
  "src/**/*.ts",
22
22
  "./src/**/*.js",
23
23
  "./src/**/*.ts",
24
+ "./src/**/*.tsx",
24
25
  "./src/**/*.svelte"
25
26
  ],
26
27
  "exclude": ["./node_modules"]
package/tsup.config.ts CHANGED
@@ -29,9 +29,11 @@ export default defineConfig([
29
29
  {
30
30
  ...cfg,
31
31
  entry: {
32
- index: "src/browser-media-images/index.ts",
32
+ index: "src/media/index.ts",
33
+ "index.browser": "src/media/index.browser.ts",
34
+ "index.native": "src/media/index.native.ts",
33
35
  },
34
- outDir: "dist/browser-media-images",
36
+ outDir: "dist/media",
35
37
  },
36
38
  {
37
39
  ...cfg,
@@ -121,13 +123,6 @@ export default defineConfig([
121
123
  },
122
124
  outDir: "dist/react-native-core",
123
125
  },
124
- {
125
- ...cfg,
126
- entry: {
127
- index: "src/react-native-media-images/index.ts",
128
- },
129
- outDir: "dist/react-native-media-images",
130
- },
131
126
  {
132
127
  ...cfg,
133
128
  entry: {
package/vitest.config.ts CHANGED
@@ -1,32 +1,25 @@
1
+ import { svelte } from "@sveltejs/vite-plugin-svelte";
2
+ import { svelteTesting } from "@testing-library/svelte/vite";
1
3
  import { defineProject } from "vitest/config";
2
4
 
3
5
  export default defineProject({
6
+ plugins: [
7
+ svelte(),
8
+ svelteTesting({
9
+ resolveBrowser: false,
10
+ }),
11
+ ],
12
+ resolve: {
13
+ // 'browser' for Svelte Testing Library
14
+ // 'node' for "msw/node"
15
+ conditions: ["browser", "node"],
16
+ },
4
17
  test: {
5
18
  name: "jazz-tools",
19
+ include: ["src/**/*.test.{js,ts,tsx,svelte}"],
6
20
  typecheck: {
7
21
  enabled: true,
8
22
  checker: "tsc",
9
23
  },
10
- projects: [
11
- {
12
- test: {
13
- include: ["src/**/*.test.browser.ts"],
14
- browser: {
15
- enabled: true,
16
- provider: "playwright",
17
- headless: true,
18
- screenshotFailures: false,
19
- instances: [{ browser: "chromium" }],
20
- },
21
- name: "browser",
22
- },
23
- },
24
- {
25
- test: {
26
- include: ["src/**/*.test.{js,ts,svelte}"],
27
- name: "unit",
28
- },
29
- },
30
- ],
31
24
  },
32
25
  });
@@ -1,9 +0,0 @@
1
- import { Account, Group, ImageDefinition, Loaded } from "jazz-tools";
2
- /** @category Image creation */
3
- export declare function createImage(imageBlobOrFile: Blob | File, options?: {
4
- owner?: Group | Account;
5
- maxSize?: 256 | 1024 | 2048;
6
- }): Promise<Loaded<typeof ImageDefinition, {
7
- $each: true;
8
- }>>;
9
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser-media-images/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EAEP,KAAK,EACL,eAAe,EACf,MAAM,EACP,MAAM,YAAY,CAAC;AAKpB,+BAA+B;AAC/B,wBAAsB,WAAW,CAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,EAC5B,OAAO,CAAC,EAAE;IACR,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;CAC7B,GACA,OAAO,CAAC,MAAM,CAAC,OAAO,eAAe,EAAE;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,CA8C1D"}
@@ -1,72 +0,0 @@
1
- // src/browser-media-images/index.ts
2
- import ImageBlobReduce from "image-blob-reduce";
3
- import {
4
- FileStream,
5
- ImageDefinition
6
- } from "jazz-tools";
7
- import Pica from "pica";
8
- var reducer;
9
- async function createImage(imageBlobOrFile, options) {
10
- const { width: originalWidth, height: originalHeight } = await getImageSize(imageBlobOrFile);
11
- const highestDimension = Math.max(originalWidth, originalHeight);
12
- const resizes = [256, 1024, 2048, highestDimension].filter((s) => s <= (options?.maxSize ?? highestDimension)).toSorted((a, b) => a - b);
13
- const { width: finalWidth, height: finalHeight } = getNewDimensions(
14
- originalWidth,
15
- originalHeight,
16
- resizes.at(-1)
17
- );
18
- const imageDefinition = ImageDefinition.create(
19
- { originalSize: [finalWidth, finalHeight] },
20
- options?.owner
21
- );
22
- const owner = imageDefinition._owner;
23
- imageDefinition.placeholderDataURL = await getPlaceholderBase64(imageBlobOrFile);
24
- for (let size of resizes) {
25
- const { width, height } = getNewDimensions(
26
- originalWidth,
27
- originalHeight,
28
- size
29
- );
30
- const image = await resize(imageBlobOrFile, width, height);
31
- const binaryStream = await FileStream.createFromBlob(image, owner);
32
- imageDefinition[`${width}x${height}`] = binaryStream;
33
- }
34
- return imageDefinition;
35
- }
36
- async function getImageSize(imageBlobOrFile) {
37
- const { width, height } = await new Promise((resolve, reject) => {
38
- const img = new Image();
39
- img.onload = () => {
40
- resolve({ width: img.width, height: img.height });
41
- URL.revokeObjectURL(img.src);
42
- };
43
- img.onerror = () => {
44
- reject(new Error("Failed to load image"));
45
- URL.revokeObjectURL(img.src);
46
- };
47
- img.src = URL.createObjectURL(imageBlobOrFile);
48
- });
49
- return { width, height };
50
- }
51
- async function getPlaceholderBase64(imageBlobOrFile) {
52
- if (!reducer) {
53
- reducer = new ImageBlobReduce({ pica: new Pica() });
54
- }
55
- const canvas = await reducer.toCanvas(imageBlobOrFile, { max: 8 });
56
- return canvas.toDataURL("image/png");
57
- }
58
- async function resize(imageBlobOrFile, width, height) {
59
- if (!reducer) {
60
- reducer = new ImageBlobReduce({ pica: new Pica() });
61
- }
62
- return reducer.toBlob(imageBlobOrFile, { max: Math.max(width, height) });
63
- }
64
- var getNewDimensions = (originalWidth, originalHeight, maxSize) => {
65
- const width = originalWidth > originalHeight ? maxSize : Math.round(maxSize * (originalWidth / originalHeight));
66
- const height = originalHeight > originalWidth ? maxSize : Math.round(maxSize * (originalHeight / originalWidth));
67
- return { width, height };
68
- };
69
- export {
70
- createImage
71
- };
72
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/browser-media-images/index.ts"],"sourcesContent":["import ImageBlobReduce from \"image-blob-reduce\";\nimport {\n Account,\n FileStream,\n Group,\n ImageDefinition,\n Loaded,\n} from \"jazz-tools\";\nimport Pica from \"pica\";\n\nlet reducer: ImageBlobReduce.ImageBlobReduce | undefined;\n\n/** @category Image creation */\nexport async function createImage(\n imageBlobOrFile: Blob | File,\n options?: {\n owner?: Group | Account;\n maxSize?: 256 | 1024 | 2048;\n },\n): Promise<Loaded<typeof ImageDefinition, { $each: true }>> {\n // Get the original size of the image\n const { width: originalWidth, height: originalHeight } =\n await getImageSize(imageBlobOrFile);\n\n const highestDimension = Math.max(originalWidth, originalHeight);\n\n // Calculate the sizes to resize the image to\n const resizes = [256, 1024, 2048, highestDimension]\n .filter((s) => s <= (options?.maxSize ?? highestDimension))\n .toSorted((a, b) => a - b);\n\n // Get the highest resolution to use as final original size\n // In case of options.maxSize, it's not the originalWidth/Height\n const { width: finalWidth, height: finalHeight } = getNewDimensions(\n originalWidth,\n originalHeight,\n resizes.at(-1)!,\n );\n\n const imageDefinition = ImageDefinition.create(\n { originalSize: [finalWidth, finalHeight] },\n options?.owner,\n );\n const owner = imageDefinition._owner;\n\n // Placeholder 8x8\n imageDefinition.placeholderDataURL =\n await getPlaceholderBase64(imageBlobOrFile);\n\n // Resizes for progressive loading\n for (let size of resizes) {\n // Calculate width and height respecting the aspect ratio\n const { width, height } = getNewDimensions(\n originalWidth,\n originalHeight,\n size,\n );\n\n const image = await resize(imageBlobOrFile, width, height);\n\n const binaryStream = await FileStream.createFromBlob(image, owner);\n imageDefinition[`${width}x${height}`] = binaryStream;\n }\n\n return imageDefinition;\n}\n\nasync function getImageSize(\n imageBlobOrFile: Blob | File,\n): Promise<{ width: number; height: number }> {\n const { width, height } = await new Promise<{\n width: number;\n height: number;\n }>((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.width, height: img.height });\n URL.revokeObjectURL(img.src);\n };\n img.onerror = () => {\n reject(new Error(\"Failed to load image\"));\n URL.revokeObjectURL(img.src);\n };\n img.src = URL.createObjectURL(imageBlobOrFile);\n });\n\n return { width, height };\n}\n\nasync function getPlaceholderBase64(\n imageBlobOrFile: Blob | File,\n): Promise<string> {\n // Inizialize Reducer here to not have module side effects\n if (!reducer) {\n reducer = new ImageBlobReduce({ pica: new Pica() });\n }\n\n const canvas = await reducer.toCanvas(imageBlobOrFile, { max: 8 });\n return canvas.toDataURL(\"image/png\");\n}\n\nasync function resize(\n imageBlobOrFile: Blob | File,\n width: number,\n height: number,\n): Promise<Blob> {\n // Inizialize Reducer here to not have module side effects\n if (!reducer) {\n reducer = new ImageBlobReduce({ pica: new Pica() });\n }\n\n return reducer.toBlob(imageBlobOrFile, { max: Math.max(width, height) });\n}\n\nconst getNewDimensions = (\n originalWidth: number,\n originalHeight: number,\n maxSize: number,\n) => {\n const width =\n originalWidth > originalHeight\n ? maxSize\n : Math.round(maxSize * (originalWidth / originalHeight));\n\n const height =\n originalHeight > originalWidth\n ? maxSize\n : Math.round(maxSize * (originalHeight / originalWidth));\n\n return { width, height };\n};\n"],"mappings":";AAAA,OAAO,qBAAqB;AAC5B;AAAA,EAEE;AAAA,EAEA;AAAA,OAEK;AACP,OAAO,UAAU;AAEjB,IAAI;AAGJ,eAAsB,YACpB,iBACA,SAI0D;AAE1D,QAAM,EAAE,OAAO,eAAe,QAAQ,eAAe,IACnD,MAAM,aAAa,eAAe;AAEpC,QAAM,mBAAmB,KAAK,IAAI,eAAe,cAAc;AAG/D,QAAM,UAAU,CAAC,KAAK,MAAM,MAAM,gBAAgB,EAC/C,OAAO,CAAC,MAAM,MAAM,SAAS,WAAW,iBAAiB,EACzD,SAAS,CAAC,GAAG,MAAM,IAAI,CAAC;AAI3B,QAAM,EAAE,OAAO,YAAY,QAAQ,YAAY,IAAI;AAAA,IACjD;AAAA,IACA;AAAA,IACA,QAAQ,GAAG,EAAE;AAAA,EACf;AAEA,QAAM,kBAAkB,gBAAgB;AAAA,IACtC,EAAE,cAAc,CAAC,YAAY,WAAW,EAAE;AAAA,IAC1C,SAAS;AAAA,EACX;AACA,QAAM,QAAQ,gBAAgB;AAG9B,kBAAgB,qBACd,MAAM,qBAAqB,eAAe;AAG5C,WAAS,QAAQ,SAAS;AAExB,UAAM,EAAE,OAAO,OAAO,IAAI;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,OAAO,iBAAiB,OAAO,MAAM;AAEzD,UAAM,eAAe,MAAM,WAAW,eAAe,OAAO,KAAK;AACjE,oBAAgB,GAAG,KAAK,IAAI,MAAM,EAAE,IAAI;AAAA,EAC1C;AAEA,SAAO;AACT;AAEA,eAAe,aACb,iBAC4C;AAC5C,QAAM,EAAE,OAAO,OAAO,IAAI,MAAM,IAAI,QAGjC,CAAC,SAAS,WAAW;AACtB,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,SAAS,MAAM;AACjB,cAAQ,EAAE,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO,CAAC;AAChD,UAAI,gBAAgB,IAAI,GAAG;AAAA,IAC7B;AACA,QAAI,UAAU,MAAM;AAClB,aAAO,IAAI,MAAM,sBAAsB,CAAC;AACxC,UAAI,gBAAgB,IAAI,GAAG;AAAA,IAC7B;AACA,QAAI,MAAM,IAAI,gBAAgB,eAAe;AAAA,EAC/C,CAAC;AAED,SAAO,EAAE,OAAO,OAAO;AACzB;AAEA,eAAe,qBACb,iBACiB;AAEjB,MAAI,CAAC,SAAS;AACZ,cAAU,IAAI,gBAAgB,EAAE,MAAM,IAAI,KAAK,EAAE,CAAC;AAAA,EACpD;AAEA,QAAM,SAAS,MAAM,QAAQ,SAAS,iBAAiB,EAAE,KAAK,EAAE,CAAC;AACjE,SAAO,OAAO,UAAU,WAAW;AACrC;AAEA,eAAe,OACb,iBACA,OACA,QACe;AAEf,MAAI,CAAC,SAAS;AACZ,cAAU,IAAI,gBAAgB,EAAE,MAAM,IAAI,KAAK,EAAE,CAAC;AAAA,EACpD;AAEA,SAAO,QAAQ,OAAO,iBAAiB,EAAE,KAAK,KAAK,IAAI,OAAO,MAAM,EAAE,CAAC;AACzE;AAEA,IAAM,mBAAmB,CACvB,eACA,gBACA,YACG;AACH,QAAM,QACJ,gBAAgB,iBACZ,UACA,KAAK,MAAM,WAAW,gBAAgB,eAAe;AAE3D,QAAM,SACJ,iBAAiB,gBACb,UACA,KAAK,MAAM,WAAW,iBAAiB,cAAc;AAE3D,SAAO,EAAE,OAAO,OAAO;AACzB;","names":[]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.browser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.browser.d.ts","sourceRoot":"","sources":["../../src/browser-media-images/index.test.browser.ts"],"names":[],"mappings":""}