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.
- package/.svelte-kit/__package__/index.d.ts +1 -0
- package/.svelte-kit/__package__/index.d.ts.map +1 -1
- package/.svelte-kit/__package__/index.js +1 -0
- package/.svelte-kit/__package__/media/image.svelte +131 -0
- package/.svelte-kit/__package__/media/image.svelte.d.ts +10 -0
- package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -0
- package/.svelte-kit/__package__/media/index.d.ts +2 -0
- package/.svelte-kit/__package__/media/index.d.ts.map +1 -0
- package/.svelte-kit/__package__/media/index.js +1 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts +2 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.js +430 -0
- package/.svelte-kit/__package__/tests/testUtils.d.ts +11 -0
- package/.svelte-kit/__package__/tests/testUtils.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/testUtils.js +17 -0
- package/.svelte-kit/__package__/tests/types.d.ts +3 -0
- package/.turbo/turbo-build.log +44 -48
- package/CHANGELOG.md +28 -0
- package/dist/{chunk-H3BIFFQG.js → chunk-2SH44VLX.js} +35 -40
- package/dist/chunk-2SH44VLX.js.map +1 -0
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/inspector/{custom-element-TUXKXSZU.js → custom-element-I7L56H6B.js} +3 -5
- package/dist/inspector/{custom-element-TUXKXSZU.js.map → custom-element-I7L56H6B.js.map} +1 -1
- package/dist/inspector/index.js +2 -4
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/co-plain-text-view.d.ts +1 -1
- package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
- package/dist/inspector/viewer/role-display.d.ts.map +1 -1
- package/dist/media/chunk-BBSS3NEY.js +211 -0
- package/dist/media/chunk-BBSS3NEY.js.map +1 -0
- package/dist/media/create-image.d.ts +48 -0
- package/dist/media/create-image.d.ts.map +1 -0
- package/dist/media/create-image.test.d.ts +2 -0
- package/dist/media/create-image.test.d.ts.map +1 -0
- package/dist/media/index.browser.d.ts +15 -0
- package/dist/media/index.browser.d.ts.map +1 -0
- package/dist/media/index.browser.js +113 -0
- package/dist/media/index.browser.js.map +1 -0
- package/dist/media/index.d.ts +53 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +13 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/index.native.d.ts +17 -0
- package/dist/media/index.native.d.ts.map +1 -0
- package/dist/media/index.native.js +126 -0
- package/dist/media/index.native.js.map +1 -0
- package/dist/media/utils.d.ts +17 -0
- package/dist/media/utils.d.ts.map +1 -0
- package/dist/media/utils.test.d.ts +2 -0
- package/dist/media/utils.test.d.ts.map +1 -0
- package/dist/react/index.d.ts +1 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +176 -59
- package/dist/react/index.js.map +1 -1
- package/dist/react/media/image.d.ts +62 -0
- package/dist/react/media/image.d.ts.map +1 -0
- package/dist/react/ssr.d.ts.map +1 -1
- package/dist/react/ssr.js.map +1 -1
- package/dist/react/tests/media/image.test.d.ts +2 -0
- package/dist/react/tests/media/image.test.d.ts.map +1 -0
- package/dist/react/tests/testUtils.d.ts.map +1 -1
- package/dist/react-core/auth/PassphraseAuth.d.ts +1 -1
- package/dist/react-core/auth/PassphraseAuth.d.ts.map +1 -1
- package/dist/react-core/index.js +1 -3
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/tests/testUtils.d.ts.map +1 -1
- package/dist/react-core/tests/useDemoAuth.test.d.ts +2 -0
- package/dist/react-core/tests/useDemoAuth.test.d.ts.map +1 -0
- package/dist/react-native-core/index.d.ts +1 -1
- package/dist/react-native-core/index.d.ts.map +1 -1
- package/dist/react-native-core/index.js +84 -66
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/media/image.d.ts +93 -0
- package/dist/react-native-core/media/image.d.ts.map +1 -0
- package/dist/react-native-core/testing.d.ts +2 -0
- package/dist/react-native-core/testing.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/media/image.svelte +131 -0
- package/dist/svelte/media/image.svelte.d.ts +10 -0
- package/dist/svelte/media/image.svelte.d.ts.map +1 -0
- package/dist/svelte/media/index.d.ts +2 -0
- package/dist/svelte/media/index.d.ts.map +1 -0
- package/dist/svelte/media/index.js +1 -0
- package/dist/svelte/tests/media/image.svelte.test.d.ts +2 -0
- package/dist/svelte/tests/media/image.svelte.test.d.ts.map +1 -0
- package/dist/svelte/tests/media/image.svelte.test.js +430 -0
- package/dist/svelte/tests/testUtils.d.ts +11 -0
- package/dist/svelte/tests/testUtils.d.ts.map +1 -0
- package/dist/svelte/tests/testUtils.js +17 -0
- package/dist/svelte/tests/types.d.ts +3 -0
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/coFeed.d.ts +15 -0
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/deepLoading.d.ts +10 -10
- package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
- package/dist/tools/coValues/extensions/imageDef.d.ts +3 -9
- package/dist/tools/coValues/extensions/imageDef.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +1 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/inspector/viewer/co-plain-text-view.tsx +1 -5
- package/src/inspector/viewer/co-stream-view.tsx +1 -1
- package/src/inspector/viewer/role-display.tsx +4 -1
- package/src/{browser-media-images/index.test.browser.ts → media/create-image.test.ts} +146 -24
- package/src/media/create-image.ts +180 -0
- package/src/media/index.browser.ts +150 -0
- package/src/media/index.native.ts +153 -0
- package/src/media/index.ts +61 -0
- package/src/media/utils.test.ts +327 -0
- package/src/media/utils.ts +202 -0
- package/src/react/index.ts +1 -2
- package/src/react/media/image.tsx +210 -0
- package/src/react/ssr.ts +1 -3
- package/src/react/tests/media/image.test.tsx +588 -0
- package/src/react/tests/testUtils.tsx +2 -10
- package/src/react-core/auth/PassphraseAuth.tsx +1 -5
- package/src/react-core/tests/testUtils.tsx +2 -10
- package/src/react-native-core/index.ts +1 -1
- package/src/react-native-core/media/image.tsx +159 -0
- package/src/svelte/index.ts +1 -0
- package/src/svelte/media/image.svelte +131 -0
- package/src/svelte/media/index.ts +1 -0
- package/src/svelte/tests/media/image.svelte.test.ts +583 -0
- package/src/svelte/tests/testUtils.ts +33 -0
- package/src/svelte/tests/types.d.ts +3 -0
- package/src/tools/coValues/coFeed.ts +40 -7
- package/src/tools/coValues/deepLoading.ts +46 -32
- package/src/tools/coValues/extensions/imageDef.ts +3 -49
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +6 -0
- package/src/tools/index.ts +0 -1
- package/src/tools/testing.ts +3 -1
- package/src/tools/tests/coList.test.ts +1 -1
- package/src/tools/tests/coMap.record.test-d.ts +105 -0
- package/src/tools/tests/coMap.record.test.ts +48 -2
- package/src/tools/tests/coMap.test-d.ts +50 -0
- package/src/tools/tests/coOptional.test.ts +3 -1
- package/tsconfig.json +1 -0
- package/tsup.config.ts +4 -9
- package/vitest.config.ts +14 -21
- package/dist/browser-media-images/index.d.ts +0 -9
- package/dist/browser-media-images/index.d.ts.map +0 -1
- package/dist/browser-media-images/index.js +0 -72
- package/dist/browser-media-images/index.js.map +0 -1
- package/dist/browser-media-images/index.test.browser.d.ts +0 -2
- package/dist/browser-media-images/index.test.browser.d.ts.map +0 -1
- package/dist/chunk-H3BIFFQG.js.map +0 -1
- package/dist/react/media.d.ts +0 -24
- package/dist/react/media.d.ts.map +0 -1
- package/dist/react-native-core/media.d.ts +0 -24
- package/dist/react-native-core/media.d.ts.map +0 -1
- package/dist/react-native-media-images/index.d.ts +0 -7
- package/dist/react-native-media-images/index.d.ts.map +0 -1
- package/dist/react-native-media-images/index.js +0 -177
- package/dist/react-native-media-images/index.js.map +0 -1
- package/dist/tools/tests/imageDef.test.d.ts +0 -2
- package/dist/tools/tests/imageDef.test.d.ts.map +0 -1
- package/src/browser-media-images/index.ts +0 -131
- package/src/react/media.tsx +0 -74
- package/src/react/scratch.tsx +0 -50
- package/src/react-native-core/media.tsx +0 -79
- package/src/react-native-media-images/index.ts +0 -238
- 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
|
-
?
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
{
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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 =
|
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?: {
|
package/src/tools/index.ts
CHANGED
package/src/tools/testing.ts
CHANGED
@@ -305,7 +305,9 @@ export async function linkAccounts(
|
|
305
305
|
|
306
306
|
export async function setupJazzTestSync({
|
307
307
|
asyncPeers = false,
|
308
|
-
}: {
|
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
package/tsup.config.ts
CHANGED
@@ -29,9 +29,11 @@ export default defineConfig([
|
|
29
29
|
{
|
30
30
|
...cfg,
|
31
31
|
entry: {
|
32
|
-
index: "src/
|
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/
|
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 +0,0 @@
|
|
1
|
-
{"version":3,"file":"index.test.browser.d.ts","sourceRoot":"","sources":["../../src/browser-media-images/index.test.browser.ts"],"names":[],"mappings":""}
|