jazz-tools 0.18.26 → 0.18.28

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 (68) hide show
  1. package/.turbo/turbo-build.log +61 -61
  2. package/CHANGELOG.md +24 -0
  3. package/dist/{chunk-ZIAN4UY5.js → chunk-YOL3XDDW.js} +158 -120
  4. package/dist/chunk-YOL3XDDW.js.map +1 -0
  5. package/dist/index.js +97 -5
  6. package/dist/index.js.map +1 -1
  7. package/dist/media/{chunk-W3S526L3.js → chunk-K6GCHLQU.js} +1 -1
  8. package/dist/media/chunk-K6GCHLQU.js.map +1 -0
  9. package/dist/media/create-image/browser.d.ts +1 -1
  10. package/dist/media/create-image/react-native.d.ts +1 -1
  11. package/dist/media/create-image/react-native.d.ts.map +1 -1
  12. package/dist/media/create-image/server.d.ts +1 -1
  13. package/dist/media/create-image-factory.d.ts +5 -2
  14. package/dist/media/create-image-factory.d.ts.map +1 -1
  15. package/dist/media/index.browser.js +1 -1
  16. package/dist/media/index.d.ts +3 -4
  17. package/dist/media/index.d.ts.map +1 -1
  18. package/dist/media/index.js +1 -1
  19. package/dist/media/index.native.js +63 -28
  20. package/dist/media/index.native.js.map +1 -1
  21. package/dist/media/index.server.js +1 -1
  22. package/dist/react/index.js.map +1 -1
  23. package/dist/react-core/hooks.d.ts +4 -0
  24. package/dist/react-core/hooks.d.ts.map +1 -1
  25. package/dist/react-core/index.js +5 -0
  26. package/dist/react-core/index.js.map +1 -1
  27. package/dist/testing.js +1 -1
  28. package/dist/tools/coValues/coList.d.ts +11 -3
  29. package/dist/tools/coValues/coList.d.ts.map +1 -1
  30. package/dist/tools/coValues/coMap.d.ts +21 -5
  31. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  32. package/dist/tools/coValues/group.d.ts +2 -2
  33. package/dist/tools/coValues/group.d.ts.map +1 -1
  34. package/dist/tools/coValues/inbox.d.ts.map +1 -1
  35. package/dist/tools/coValues/interfaces.d.ts +9 -0
  36. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  37. package/dist/tools/coValues/request.d.ts +70 -0
  38. package/dist/tools/coValues/request.d.ts.map +1 -1
  39. package/dist/tools/exports.d.ts +2 -2
  40. package/dist/tools/exports.d.ts.map +1 -1
  41. package/dist/tools/tests/authenticate-request.test.d.ts +2 -0
  42. package/dist/tools/tests/authenticate-request.test.d.ts.map +1 -0
  43. package/dist/tools/tests/coList.unique.test.d.ts +2 -0
  44. package/dist/tools/tests/coList.unique.test.d.ts.map +1 -0
  45. package/dist/tools/tests/coMap.unique.test.d.ts +2 -0
  46. package/dist/tools/tests/coMap.unique.test.d.ts.map +1 -0
  47. package/package.json +8 -4
  48. package/src/media/create-image/react-native.ts +75 -30
  49. package/src/media/create-image-factory.test.ts +18 -0
  50. package/src/media/create-image-factory.ts +6 -1
  51. package/src/media/index.ts +7 -4
  52. package/src/react-core/hooks.ts +8 -0
  53. package/src/react-core/tests/useAccount.test.ts +61 -1
  54. package/src/react-core/tests/usePassPhraseAuth.test.ts +74 -2
  55. package/src/tools/coValues/coList.ts +38 -35
  56. package/src/tools/coValues/coMap.ts +38 -38
  57. package/src/tools/coValues/group.ts +5 -1
  58. package/src/tools/coValues/inbox.ts +4 -3
  59. package/src/tools/coValues/interfaces.ts +88 -0
  60. package/src/tools/coValues/request.ts +188 -4
  61. package/src/tools/exports.ts +4 -0
  62. package/src/tools/tests/authenticate-request.test.ts +194 -0
  63. package/src/tools/tests/coList.test.ts +0 -190
  64. package/src/tools/tests/coList.unique.test.ts +244 -0
  65. package/src/tools/tests/coMap.test.ts +0 -433
  66. package/src/tools/tests/coMap.unique.test.ts +474 -0
  67. package/dist/chunk-ZIAN4UY5.js.map +0 -1
  68. package/dist/media/chunk-W3S526L3.js.map +0 -1
@@ -8,7 +8,7 @@ export { CoValueBase } from "./internal.js";
8
8
  export { Profile } from "./internal.js";
9
9
  export { SchemaUnion } from "./internal.js";
10
10
  export { co } from "./internal.js";
11
- export type { CoValueClass, CoValueFromRaw, DeeplyLoaded, Resolved, RefsToResolve, RefsToResolveStrict, CoMapInit_DEPRECATED as CoMapInit, CoFeedEntry, TextPos, AccountClass, AccountCreationProps, BaseProfileShape, } from "./internal.js";
11
+ export type { CoValueClass, CoValueFromRaw, DeeplyLoaded, Resolved, RefsToResolve, RefsToResolveStrict, CoMapInit_DEPRECATED as CoMapInit, CoFeedEntry, TextPos, AccountClass, AccountCreationProps, BaseProfileShape, unstable_loadUnique, } from "./internal.js";
12
12
  export { CoMap, CoList, BinaryCoStream, CoFeed, CoStream, FileStream, CoPlainText, CoRichText, Account, isControlledAccount, loadCoValue, subscribeToCoValue, ImageDefinition, SubscriptionScope, exportCoValue, importContentPieces, Ref, } from "./internal.js";
13
13
  export { JazzContextManager, type JazzContextManagerAuthProps, } from "./internal.js";
14
14
  export { AuthSecretStorage, type AuthSetPayload, } from "./auth/AuthSecretStorage.js";
@@ -21,6 +21,6 @@ export { createInviteLink, parseInviteLink, consumeInviteLink, } from "./impleme
21
21
  export { AnonymousJazzAgent, createAnonymousJazzContext, createJazzContextFromExistingCredentials, createJazzContextForNewAccount, createJazzContext, randomSessionProvider, type AuthResult, type Credentials, type JazzContextWithAccount, type BranchDefinition, } from "./internal.js";
22
22
  export type * from "./types.js";
23
23
  export { coValueClassFromCoValueClassOrSchema, type InstanceOfSchema, type InstanceOfSchemaCoValuesNullable, type CoValueClassOrSchema, type Loaded, type BaseAccountShape, type DefaultAccountShape, type CoreAccountSchema as AnyAccountSchema, type ResolveQuery, type ResolveQueryStrict, } from "./internal.js";
24
- export { experimental_defineRequest, JazzRequestError, isJazzRequestError, type HttpRoute, } from "./coValues/request.js";
24
+ export { experimental_defineRequest, JazzRequestError, isJazzRequestError, authenticateRequest, generateAuthToken, parseAuthToken, type HttpRoute, } from "./coValues/request.js";
25
25
  export * from "./ssr/index.js";
26
26
  //# sourceMappingURL=exports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../src/tools/exports.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,KAAK,CAAC,MAAM,2CAA2C,CAAC;AAE/D,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAEnC,YAAY,EACV,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,oBAAoB,IAAI,SAAS,EACjC,WAAW,EACX,OAAO,EACP,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,EACL,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EACR,UAAU,EACV,WAAW,EACX,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,GAAG,GACJ,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,KAAK,2BAA2B,GACjC,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,wCAAwC,EACxC,8BAA8B,EAC9B,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EACL,oCAAoC,EACpC,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,EACrC,KAAK,oBAAoB,EACzB,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,IAAI,gBAAgB,EAC1C,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,SAAS,GACf,MAAM,uBAAuB,CAAC;AAE/B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../src/tools/exports.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,KAAK,CAAC,MAAM,2CAA2C,CAAC;AAE/D,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAEnC,YAAY,EACV,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,oBAAoB,IAAI,SAAS,EACjC,WAAW,EACX,OAAO,EACP,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,EACL,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EACR,UAAU,EACV,WAAW,EACX,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,GAAG,GACJ,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,KAAK,2BAA2B,GACjC,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,wCAAwC,EACxC,8BAA8B,EAC9B,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EACL,oCAAoC,EACpC,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,EACrC,KAAK,oBAAoB,EACzB,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,IAAI,gBAAgB,EAC1C,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,KAAK,SAAS,GACf,MAAM,uBAAuB,CAAC;AAE/B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=authenticate-request.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticate-request.test.d.ts","sourceRoot":"","sources":["../../../src/tools/tests/authenticate-request.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coList.unique.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coList.unique.test.d.ts","sourceRoot":"","sources":["../../../src/tools/tests/coList.unique.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coMap.unique.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coMap.unique.test.d.ts","sourceRoot":"","sources":["../../../src/tools/tests/coMap.unique.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -187,7 +187,7 @@
187
187
  },
188
188
  "type": "module",
189
189
  "license": "MIT",
190
- "version": "0.18.26",
190
+ "version": "0.18.28",
191
191
  "dependencies": {
192
192
  "@manuscripts/prosemirror-recreate-steps": "^0.1.4",
193
193
  "@scure/base": "1.2.1",
@@ -204,9 +204,9 @@
204
204
  "prosemirror-transform": "^1.9.0",
205
205
  "use-sync-external-store": "^1.5.0",
206
206
  "zod": "4.1.11",
207
- "cojson": "0.18.26",
208
- "cojson-storage-indexeddb": "0.18.26",
209
- "cojson-transport-ws": "0.18.26"
207
+ "cojson": "0.18.28",
208
+ "cojson-storage-indexeddb": "0.18.28",
209
+ "cojson-transport-ws": "0.18.28"
210
210
  },
211
211
  "devDependencies": {
212
212
  "@scure/bip39": "^1.3.0",
@@ -234,6 +234,7 @@
234
234
  "@op-engineering/op-sqlite": "*",
235
235
  "@react-native-community/netinfo": "*",
236
236
  "better-auth": "^1.3.2",
237
+ "expo-image-manipulator": "*",
237
238
  "expo-file-system": "*",
238
239
  "expo-secure-store": "*",
239
240
  "expo-sqlite": "*",
@@ -260,6 +261,9 @@
260
261
  "better-auth": {
261
262
  "optional": true
262
263
  },
264
+ "expo-image-manipulator": {
265
+ "optional": true
266
+ },
263
267
  "expo-file-system": {
264
268
  "optional": true
265
269
  },
@@ -1,11 +1,11 @@
1
+ import { NativeModules } from "react-native";
1
2
  import type ImageResizerType from "@bam.tech/react-native-image-resizer";
3
+ import type ImageManipulatorType from "expo-image-manipulator";
2
4
  import type { Account, Group } from "jazz-tools";
3
5
  import { FileStream } from "jazz-tools";
4
6
  import { Image } from "react-native";
5
7
  import { createImageFactory } from "../create-image-factory";
6
8
 
7
- let ImageResizer: typeof ImageResizerType | undefined;
8
-
9
9
  /**
10
10
  * Creates an ImageDefinition from an image file path with built-in UX features.
11
11
  *
@@ -45,19 +45,27 @@ export const createImage = createImageFactory(
45
45
  },
46
46
  );
47
47
 
48
- async function getResizer(): Promise<typeof ImageResizerType> {
49
- if (!ImageResizer) {
50
- try {
51
- ImageResizer = (await import("@bam.tech/react-native-image-resizer"))
52
- .default;
53
- } catch (e) {
54
- throw new Error(
55
- "ImageResizer is not installed, please run `npm install @bam.tech/react-native-image-resizer`",
56
- );
48
+ async function getResizer(): Promise<
49
+ typeof ImageResizerType | typeof ImageManipulatorType
50
+ > {
51
+ try {
52
+ const rnImageResizer = await import("@bam.tech/react-native-image-resizer");
53
+
54
+ if (rnImageResizer.default !== undefined) {
55
+ return rnImageResizer.default;
57
56
  }
58
- }
57
+ } catch (e) {}
58
+
59
+ try {
60
+ const expoImageManipulator = await import("expo-image-manipulator");
61
+ if (expoImageManipulator.ImageManipulator !== undefined) {
62
+ return expoImageManipulator;
63
+ }
64
+ } catch (e) {}
59
65
 
60
- return ImageResizer;
66
+ throw new Error(
67
+ "No resizer lib found. Please install `@bam.tech/react-native-image-resizer` or `expo-image-manipulator`",
68
+ );
61
69
  }
62
70
 
63
71
  async function getImageSize(
@@ -71,15 +79,37 @@ async function getImageSize(
71
79
  async function getPlaceholderBase64(filePath: string): Promise<string> {
72
80
  const ImageResizer = await getResizer();
73
81
 
74
- const { uri } = await ImageResizer.createResizedImage(
75
- filePath,
76
- 8,
77
- 8,
78
- "PNG",
79
- 100,
80
- );
82
+ if ("createResizedImage" in ImageResizer) {
83
+ const { uri } = await ImageResizer.createResizedImage(
84
+ filePath,
85
+ 8,
86
+ 8,
87
+ "PNG",
88
+ 100,
89
+ );
90
+
91
+ return imageUrlToBase64(uri);
92
+ } else {
93
+ const ctx = ImageResizer.ImageManipulator.manipulate(filePath);
81
94
 
82
- return imageUrlToBase64(uri);
95
+ ctx.resize({ width: 8, height: 8 });
96
+
97
+ const im = await ctx.renderAsync();
98
+ const result = await im.saveAsync({
99
+ base64: true,
100
+ format: ImageResizer.SaveFormat.PNG,
101
+ });
102
+
103
+ const base64 = result.base64;
104
+
105
+ if (!base64) {
106
+ throw new Error(
107
+ "Failed to get generate placeholder using expo-image-manipulator",
108
+ );
109
+ }
110
+
111
+ return base64;
112
+ }
83
113
  }
84
114
 
85
115
  async function resize(
@@ -91,15 +121,30 @@ async function resize(
91
121
 
92
122
  const mimeType = await getMimeType(filePath);
93
123
 
94
- const { uri } = await ImageResizer.createResizedImage(
95
- filePath,
96
- width,
97
- height,
98
- contentTypeToFormat(mimeType),
99
- 80,
100
- );
101
-
102
- return uri;
124
+ if ("createResizedImage" in ImageResizer) {
125
+ const { uri } = await ImageResizer.createResizedImage(
126
+ filePath,
127
+ width,
128
+ height,
129
+ contentTypeToFormat(mimeType),
130
+ 80,
131
+ );
132
+
133
+ return uri;
134
+ } else {
135
+ const ctx = ImageResizer.ImageManipulator.manipulate(filePath);
136
+ ctx.resize({ width: width, height: height });
137
+
138
+ const mime = contentTypeToFormat(mimeType);
139
+
140
+ const im = await ctx.renderAsync();
141
+ const result = await im.saveAsync({
142
+ format: ImageResizer.SaveFormat[mime],
143
+ compress: 0.8,
144
+ });
145
+
146
+ return result.uri;
147
+ }
103
148
  }
104
149
 
105
150
  function getMimeType(filePath: string): Promise<string> {
@@ -24,6 +24,24 @@ describe("createImage", async () => {
24
24
  vi.clearAllMocks();
25
25
  });
26
26
 
27
+ it("should return a loaded image definition", async () => {
28
+ const imageBlob = new Blob(
29
+ [Uint8Array.from(OnePixel, (c) => c.charCodeAt(0))],
30
+ { type: "image/png" },
31
+ );
32
+
33
+ getImageSize.mockResolvedValue({ width: 1, height: 1 });
34
+
35
+ const image = await createImage(imageBlob, { owner: account });
36
+
37
+ // ensure `original` is loaded
38
+ function typeMatch(value: { original: FileStream }) {
39
+ return value;
40
+ }
41
+
42
+ typeMatch(image);
43
+ });
44
+
27
45
  it("should create a single original image if all settings are off", async () => {
28
46
  const imageBlob = new Blob(
29
47
  [Uint8Array.from(OnePixel, (c) => c.charCodeAt(0))],
@@ -37,6 +37,11 @@ export type CreateImageOptions = {
37
37
  progressive?: boolean;
38
38
  };
39
39
 
40
+ export type CreateImageReturnType = Loaded<
41
+ typeof ImageDefinition,
42
+ { original: true }
43
+ >;
44
+
40
45
  export type CreateImageImpl<
41
46
  TSourceType = SourceType,
42
47
  TResizeOutput = ResizeOutput,
@@ -70,7 +75,7 @@ async function createImage<TSourceType, TResizeOutput>(
70
75
  imageBlobOrFile: TSourceType,
71
76
  options: CreateImageOptions,
72
77
  impl: CreateImageImpl<TSourceType, TResizeOutput>,
73
- ): Promise<Loaded<typeof ImageDefinition, { $each: true }>> {
78
+ ): Promise<CreateImageReturnType> {
74
79
  // Get the original size of the image
75
80
  const { width: originalWidth, height: originalHeight } =
76
81
  await impl.getImageSize(imageBlobOrFile);
@@ -1,5 +1,8 @@
1
- import type { ImageDefinition } from "jazz-tools";
2
- import { CreateImageOptions } from "./create-image-factory";
1
+ import type { ImageDefinition, Loaded } from "jazz-tools";
2
+ import type {
3
+ CreateImageOptions,
4
+ CreateImageReturnType,
5
+ } from "./create-image-factory";
3
6
 
4
7
  export * from "./exports";
5
8
 
@@ -38,7 +41,7 @@ export * from "./exports";
38
41
  export declare function createImage(
39
42
  imageBlobOrFile: Blob | File,
40
43
  options?: CreateImageOptions,
41
- ): Promise<ImageDefinition>;
44
+ ): Promise<CreateImageReturnType>;
42
45
 
43
46
  /**
44
47
  * Creates an ImageDefinition from an image file path with built-in UX features.
@@ -66,4 +69,4 @@ export declare function createImage(
66
69
  export declare function createImage(
67
70
  filePath: string,
68
71
  options?: CreateImageOptions,
69
- ): Promise<ImageDefinition>;
72
+ ): Promise<CreateImageReturnType>;
@@ -792,6 +792,14 @@ export function useAccountWithSelector<
792
792
  );
793
793
  }
794
794
 
795
+ /**
796
+ * Returns a function for logging out of the current account.
797
+ */
798
+ export function useLogOut(): () => void {
799
+ const contextManager = useJazzContextManager();
800
+ return contextManager.logOut;
801
+ }
802
+
795
803
  export function experimental_useInboxSender<
796
804
  I extends CoValue,
797
805
  O extends CoValue | undefined,
@@ -64,7 +64,67 @@ describe("useAccount", () => {
64
64
  expect(result.current?.me?.root?.value).toBe("123");
65
65
  });
66
66
 
67
- it("should be in sync with useIsAuthenticated when logOut is called", async () => {
67
+ it("should be in sync with useIsAuthenticated when logOut (from useAccount.logOut) is called", async () => {
68
+ const account = await createJazzTestAccount({});
69
+
70
+ const accounts: string[] = [];
71
+ const updates: { isAuthenticated: boolean; accountIndex: number }[] = [];
72
+
73
+ const { result } = renderHook(
74
+ () => {
75
+ const isAuthenticated = useIsAuthenticated();
76
+ const account = useAccount();
77
+
78
+ if (account.me) {
79
+ if (!accounts.includes(account.me.$jazz.id)) {
80
+ accounts.push(account.me.$jazz.id);
81
+ }
82
+
83
+ updates.push({
84
+ isAuthenticated,
85
+ accountIndex: accounts.indexOf(account.me.$jazz.id),
86
+ });
87
+ }
88
+
89
+ return { isAuthenticated, account };
90
+ },
91
+ {
92
+ account,
93
+ isAuthenticated: true,
94
+ },
95
+ );
96
+
97
+ expect(result.current?.isAuthenticated).toBe(true);
98
+ expect(result.current?.account?.me).toBeDefined();
99
+
100
+ const id = result.current?.account?.me?.$jazz.id;
101
+
102
+ await act(async () => {
103
+ await result.current?.account?.logOut();
104
+ });
105
+
106
+ expect(result.current?.isAuthenticated).toBe(false);
107
+ expect(result.current?.account?.me?.$jazz.id).not.toBe(id);
108
+
109
+ expect(updates).toMatchInlineSnapshot(`
110
+ [
111
+ {
112
+ "accountIndex": 0,
113
+ "isAuthenticated": true,
114
+ },
115
+ {
116
+ "accountIndex": 0,
117
+ "isAuthenticated": false,
118
+ },
119
+ {
120
+ "accountIndex": 1,
121
+ "isAuthenticated": false,
122
+ },
123
+ ]
124
+ `);
125
+ });
126
+
127
+ it("should be in sync with useIsAuthenticated when logOut (from useLogOut) is called", async () => {
68
128
  const account = await createJazzTestAccount({});
69
129
 
70
130
  const accounts: string[] = [];
@@ -4,7 +4,7 @@ import { mnemonicToEntropy } from "@scure/bip39";
4
4
  import { AuthSecretStorage, KvStoreContext } from "jazz-tools";
5
5
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
6
  import { usePassphraseAuth } from "../auth/PassphraseAuth";
7
- import { useAccount } from "../hooks";
7
+ import { useAccount, useLogOut } from "../hooks";
8
8
  import {
9
9
  createJazzTestAccount,
10
10
  createJazzTestGuest,
@@ -107,7 +107,7 @@ describe("usePassphraseAuth", () => {
107
107
  expect(await result.current.signUp()).toBe(passphrase);
108
108
  });
109
109
 
110
- it("should be able to logout after sign up", async () => {
110
+ it("should be able to logout after sign up using useAccount.logOut", async () => {
111
111
  const account = await createJazzTestAccount({});
112
112
 
113
113
  const accounts: string[] = [];
@@ -177,4 +177,76 @@ describe("usePassphraseAuth", () => {
177
177
  ]
178
178
  `);
179
179
  });
180
+
181
+ it("should be able to logout after sign up using useLogout", async () => {
182
+ const account = await createJazzTestAccount({});
183
+
184
+ const accounts: string[] = [];
185
+ const updates: { state: string; accountIndex: number }[] = [];
186
+
187
+ const { result } = renderHook(
188
+ () => {
189
+ const passphraseAuth = usePassphraseAuth({ wordlist: testWordlist });
190
+ const account = useAccount();
191
+ const logOut = useLogOut();
192
+
193
+ if (account.me) {
194
+ if (!accounts.includes(account.me.$jazz.id)) {
195
+ accounts.push(account.me.$jazz.id);
196
+ }
197
+
198
+ updates.push({
199
+ state: passphraseAuth.state,
200
+ accountIndex: accounts.indexOf(account.me.$jazz.id),
201
+ });
202
+ }
203
+
204
+ return { passphraseAuth, account, logOut };
205
+ },
206
+ {
207
+ account,
208
+ isAuthenticated: false,
209
+ },
210
+ );
211
+
212
+ expect(result.current?.passphraseAuth.state).toBe("anonymous");
213
+ expect(result.current?.account?.me).toBeDefined();
214
+
215
+ const id = result.current?.account?.me?.$jazz.id;
216
+
217
+ await act(async () => {
218
+ await result.current?.passphraseAuth.signUp();
219
+ });
220
+
221
+ expect(result.current?.passphraseAuth.state).toBe("signedIn");
222
+ expect(result.current?.account?.me?.$jazz.id).toBe(id);
223
+
224
+ await act(async () => {
225
+ await result.current?.logOut();
226
+ });
227
+
228
+ expect(result.current?.passphraseAuth.state).toBe("anonymous");
229
+ expect(result.current?.account?.me?.$jazz.id).not.toBe(id);
230
+
231
+ expect(updates).toMatchInlineSnapshot(`
232
+ [
233
+ {
234
+ "accountIndex": 0,
235
+ "state": "anonymous",
236
+ },
237
+ {
238
+ "accountIndex": 0,
239
+ "state": "signedIn",
240
+ },
241
+ {
242
+ "accountIndex": 0,
243
+ "state": "anonymous",
244
+ },
245
+ {
246
+ "accountIndex": 1,
247
+ "state": "anonymous",
248
+ },
249
+ ]
250
+ `);
251
+ });
180
252
  });
@@ -21,6 +21,8 @@ import {
21
21
  SubscribeRestArgs,
22
22
  TypeSym,
23
23
  BranchDefinition,
24
+ getIdFromHeader,
25
+ unstable_loadUnique,
24
26
  } from "../internal.js";
25
27
  import {
26
28
  AnonymousJazzAgent,
@@ -320,19 +322,17 @@ export class CoList<out Item = any>
320
322
  ownerID: ID<Account> | ID<Group>,
321
323
  as?: Account | Group | AnonymousJazzAgent,
322
324
  ) {
323
- return CoList._findUnique(unique, ownerID, as);
325
+ const header = CoList._getUniqueHeader(unique, ownerID);
326
+
327
+ return getIdFromHeader(header, as);
324
328
  }
325
329
 
326
330
  /** @internal */
327
- static _findUnique<L extends CoList>(
328
- this: CoValueClass<L>,
331
+ static _getUniqueHeader(
329
332
  unique: CoValueUniqueness["uniqueness"],
330
333
  ownerID: ID<Account> | ID<Group>,
331
- as?: Account | Group | AnonymousJazzAgent,
332
334
  ) {
333
- as ||= activeAccountContext.get();
334
-
335
- const header = {
335
+ return {
336
336
  type: "colist" as const,
337
337
  ruleset: {
338
338
  type: "ownedByGroup" as const,
@@ -341,9 +341,6 @@ export class CoList<out Item = any>
341
341
  meta: null,
342
342
  uniqueness: unique,
343
343
  };
344
- const crypto =
345
- as[TypeSym] === "Anonymous" ? as.node.crypto : as.$jazz.localNode.crypto;
346
- return cojsonInternals.idforHeader(header, crypto) as ID<L>;
347
344
  }
348
345
 
349
346
  /**
@@ -378,29 +375,24 @@ export class CoList<out Item = any>
378
375
  resolve?: RefsToResolveStrict<L, R>;
379
376
  },
380
377
  ): Promise<Resolved<L, R> | null> {
381
- const listId = CoList._findUnique(
378
+ const header = CoList._getUniqueHeader(
382
379
  options.unique,
383
380
  options.owner.$jazz.id,
384
- options.owner.$jazz.loadedAs,
385
381
  );
386
- let list: Resolved<L, R> | null = await loadCoValueWithoutMe(this, listId, {
387
- ...options,
388
- loadAs: options.owner.$jazz.loadedAs,
389
- skipRetry: true,
390
- });
391
- if (!list) {
392
- list = (this as any).create(options.value, {
393
- owner: options.owner,
394
- unique: options.unique,
395
- }) as Resolved<L, R>;
396
- } else {
397
- (list as L).$jazz.applyDiff(options.value);
398
- }
399
382
 
400
- return await loadCoValueWithoutMe(this, listId, {
401
- ...options,
402
- loadAs: options.owner.$jazz.loadedAs,
403
- skipRetry: true,
383
+ return unstable_loadUnique(this, {
384
+ header,
385
+ owner: options.owner,
386
+ resolve: options.resolve,
387
+ onCreateWhenMissing: () => {
388
+ (this as any).create(options.value, {
389
+ owner: options.owner,
390
+ unique: options.unique,
391
+ });
392
+ },
393
+ onUpdateWhenFound(value) {
394
+ value.$jazz.applyDiff(options.value);
395
+ },
404
396
  });
405
397
  }
406
398
 
@@ -411,7 +403,10 @@ export class CoList<out Item = any>
411
403
  * @param options Additional options for loading the CoList.
412
404
  * @returns The loaded CoList, or null if unavailable.
413
405
  */
414
- static loadUnique<L extends CoList, const R extends RefsToResolve<L> = true>(
406
+ static async loadUnique<
407
+ L extends CoList,
408
+ const R extends RefsToResolve<L> = true,
409
+ >(
415
410
  this: CoValueClass<L>,
416
411
  unique: CoValueUniqueness["uniqueness"],
417
412
  ownerID: ID<Account> | ID<Group>,
@@ -420,11 +415,19 @@ export class CoList<out Item = any>
420
415
  loadAs?: Account | AnonymousJazzAgent;
421
416
  },
422
417
  ): Promise<Resolved<L, R> | null> {
423
- return loadCoValueWithoutMe(
424
- this,
425
- CoList._findUnique(unique, ownerID, options?.loadAs),
426
- { ...options, skipRetry: true },
427
- );
418
+ const header = CoList._getUniqueHeader(unique, ownerID);
419
+
420
+ const owner = await Group.load(ownerID, {
421
+ loadAs: options?.loadAs,
422
+ });
423
+
424
+ if (!owner) return owner;
425
+
426
+ return unstable_loadUnique(this, {
427
+ header,
428
+ owner,
429
+ resolve: options?.resolve,
430
+ });
428
431
  }
429
432
 
430
433
  // Override mutation methods defined on Array, as CoLists aren't meant to be mutated directly