jazz-tools 0.15.15 → 0.16.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 (217) hide show
  1. package/.svelte-kit/__package__/jazz.class.svelte.d.ts +2 -2
  2. package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/jazz.class.svelte.js +5 -5
  4. package/.svelte-kit/__package__/jazz.svelte.d.ts +2 -2
  5. package/.svelte-kit/__package__/jazz.svelte.d.ts.map +1 -1
  6. package/.turbo/turbo-build.log +46 -50
  7. package/CHANGELOG.md +35 -0
  8. package/dist/browser/index.d.ts +2 -2
  9. package/dist/browser/index.d.ts.map +1 -1
  10. package/dist/browser/index.js.map +1 -1
  11. package/dist/browser-media-images/index.d.ts +3 -1
  12. package/dist/browser-media-images/index.d.ts.map +1 -1
  13. package/dist/browser-media-images/index.js.map +1 -1
  14. package/dist/{chunk-4CFNXQE7.js → chunk-MLCNE3TL.js} +791 -698
  15. package/dist/chunk-MLCNE3TL.js.map +1 -0
  16. package/dist/index.js +363 -11
  17. package/dist/index.js.map +1 -1
  18. package/dist/react/hooks.d.ts +2 -2
  19. package/dist/react/hooks.d.ts.map +1 -1
  20. package/dist/react/index.js +2 -0
  21. package/dist/react/index.js.map +1 -1
  22. package/dist/react/testing.js +3 -1
  23. package/dist/react/testing.js.map +1 -1
  24. package/dist/react-core/hooks.d.ts +2 -2
  25. package/dist/react-core/hooks.d.ts.map +1 -1
  26. package/dist/react-core/index.js +3 -3
  27. package/dist/react-core/index.js.map +1 -1
  28. package/dist/react-native-core/hooks.d.ts +2 -2
  29. package/dist/react-native-core/hooks.d.ts.map +1 -1
  30. package/dist/react-native-core/index.js.map +1 -1
  31. package/dist/svelte/jazz.class.svelte.d.ts +2 -2
  32. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  33. package/dist/svelte/jazz.class.svelte.js +5 -5
  34. package/dist/svelte/jazz.svelte.d.ts +2 -2
  35. package/dist/svelte/jazz.svelte.d.ts.map +1 -1
  36. package/dist/testing.js +3 -3
  37. package/dist/testing.js.map +1 -1
  38. package/dist/tools/coValues/CoValueBase.d.ts +3 -13
  39. package/dist/tools/coValues/CoValueBase.d.ts.map +1 -1
  40. package/dist/tools/coValues/account.d.ts +2 -2
  41. package/dist/tools/coValues/account.d.ts.map +1 -1
  42. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  43. package/dist/tools/coValues/coList.d.ts.map +1 -1
  44. package/dist/tools/coValues/coMap.d.ts +5 -18
  45. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  46. package/dist/tools/coValues/deepLoading.d.ts +4 -1
  47. package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
  48. package/dist/tools/coValues/extensions/imageDef.d.ts +4 -7
  49. package/dist/tools/coValues/extensions/imageDef.d.ts.map +1 -1
  50. package/dist/tools/coValues/group.d.ts +1 -0
  51. package/dist/tools/coValues/group.d.ts.map +1 -1
  52. package/dist/tools/coValues/inbox.d.ts +2 -2
  53. package/dist/tools/coValues/inbox.d.ts.map +1 -1
  54. package/dist/tools/coValues/interfaces.d.ts +58 -17
  55. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  56. package/dist/tools/coValues/request.d.ts +82 -0
  57. package/dist/tools/coValues/request.d.ts.map +1 -0
  58. package/dist/tools/coValues/schemaUnion.d.ts +5 -1
  59. package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
  60. package/dist/tools/exports.d.ts +4 -3
  61. package/dist/tools/exports.d.ts.map +1 -1
  62. package/dist/tools/implementation/createContext.d.ts +4 -4
  63. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  64. package/dist/tools/implementation/invites.d.ts +2 -2
  65. package/dist/tools/implementation/invites.d.ts.map +1 -1
  66. package/dist/tools/implementation/schemaUtils.d.ts +8 -0
  67. package/dist/tools/implementation/schemaUtils.d.ts.map +1 -0
  68. package/dist/tools/implementation/zodSchema/coExport.d.ts +11 -1
  69. package/dist/tools/implementation/zodSchema/coExport.d.ts.map +1 -1
  70. package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts +22 -0
  71. package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts.map +1 -0
  72. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +10 -0
  73. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -0
  74. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +11 -11
  75. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  76. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts +34 -24
  77. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts.map +1 -1
  78. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +16 -14
  79. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  80. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +23 -17
  81. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  82. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +24 -16
  83. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  84. package/dist/tools/implementation/zodSchema/schemaTypes/CoOptionalSchema.d.ts +20 -9
  85. package/dist/tools/implementation/zodSchema/schemaTypes/CoOptionalSchema.d.ts.map +1 -1
  86. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +18 -12
  87. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
  88. package/dist/tools/implementation/zodSchema/schemaTypes/CoValueSchema.d.ts +18 -0
  89. package/dist/tools/implementation/zodSchema/schemaTypes/CoValueSchema.d.ts.map +1 -0
  90. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +14 -9
  91. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  92. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +14 -9
  93. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
  94. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +14 -9
  95. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
  96. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts +15 -13
  97. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts.map +1 -1
  98. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts +12 -15
  99. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts.map +1 -1
  100. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts +17 -20
  101. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts.map +1 -1
  102. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts +17 -20
  103. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts.map +1 -1
  104. package/dist/tools/implementation/zodSchema/unionUtils.d.ts +3 -5
  105. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  106. package/dist/tools/implementation/zodSchema/zodCo.d.ts +10 -8
  107. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  108. package/dist/tools/implementation/zodSchema/zodReExport.d.ts +4 -8
  109. package/dist/tools/implementation/zodSchema/zodReExport.d.ts.map +1 -1
  110. package/dist/tools/implementation/zodSchema/zodSchema.d.ts +21 -30
  111. package/dist/tools/implementation/zodSchema/zodSchema.d.ts.map +1 -1
  112. package/dist/tools/internal.d.ts +3 -2
  113. package/dist/tools/internal.d.ts.map +1 -1
  114. package/dist/tools/lib/id.d.ts +2 -0
  115. package/dist/tools/lib/id.d.ts.map +1 -0
  116. package/dist/tools/lib/utilityTypes.d.ts +10 -0
  117. package/dist/tools/lib/utilityTypes.d.ts.map +1 -0
  118. package/dist/tools/subscribe/SubscriptionScope.d.ts +3 -2
  119. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  120. package/dist/tools/subscribe/utils.d.ts.map +1 -1
  121. package/dist/tools/testing.d.ts +2 -2
  122. package/dist/tools/testing.d.ts.map +1 -1
  123. package/dist/tools/tests/exportImport.test.d.ts +2 -0
  124. package/dist/tools/tests/exportImport.test.d.ts.map +1 -0
  125. package/dist/tools/tests/request.test.d.ts +2 -0
  126. package/dist/tools/tests/request.test.d.ts.map +1 -0
  127. package/dist/worker/index.d.ts.map +1 -1
  128. package/dist/worker/index.js +2 -2
  129. package/dist/worker/index.js.map +1 -1
  130. package/package.json +7 -6
  131. package/src/browser/index.ts +2 -4
  132. package/src/browser-media-images/index.ts +1 -1
  133. package/src/react/hooks.tsx +2 -2
  134. package/src/react-core/hooks.ts +6 -6
  135. package/src/react-core/tests/useAccount.test.ts +2 -2
  136. package/src/react-core/tests/useCoState.test.ts +3 -2
  137. package/src/react-native-core/hooks.tsx +2 -2
  138. package/src/svelte/jazz.class.svelte.ts +10 -7
  139. package/src/svelte/jazz.svelte.ts +2 -2
  140. package/src/tools/coValues/CoValueBase.ts +8 -20
  141. package/src/tools/coValues/account.ts +18 -14
  142. package/src/tools/coValues/coFeed.ts +0 -4
  143. package/src/tools/coValues/coList.ts +7 -9
  144. package/src/tools/coValues/coMap.ts +1 -6
  145. package/src/tools/coValues/coPlainText.ts +4 -4
  146. package/src/tools/coValues/deepLoading.ts +4 -1
  147. package/src/tools/coValues/extensions/imageDef.ts +3 -3
  148. package/src/tools/coValues/group.ts +1 -0
  149. package/src/tools/coValues/inbox.ts +8 -7
  150. package/src/tools/coValues/interfaces.ts +177 -69
  151. package/src/tools/coValues/request.ts +633 -0
  152. package/src/tools/coValues/schemaUnion.ts +8 -4
  153. package/src/tools/exports.ts +11 -14
  154. package/src/tools/implementation/createContext.ts +9 -9
  155. package/src/tools/implementation/invites.ts +2 -2
  156. package/src/tools/implementation/schemaUtils.ts +18 -0
  157. package/src/tools/implementation/zodSchema/coExport.ts +14 -0
  158. package/src/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.ts +156 -0
  159. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +133 -0
  160. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +36 -17
  161. package/src/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.ts +101 -52
  162. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +54 -50
  163. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +54 -46
  164. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +187 -137
  165. package/src/tools/implementation/zodSchema/schemaTypes/CoOptionalSchema.ts +29 -27
  166. package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +28 -18
  167. package/src/tools/implementation/zodSchema/schemaTypes/CoValueSchema.ts +18 -0
  168. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +45 -36
  169. package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +47 -35
  170. package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +43 -30
  171. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.ts +28 -23
  172. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.ts +28 -25
  173. package/src/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.ts +86 -78
  174. package/src/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.ts +91 -82
  175. package/src/tools/implementation/zodSchema/unionUtils.ts +47 -51
  176. package/src/tools/implementation/zodSchema/zodCo.ts +42 -75
  177. package/src/tools/implementation/zodSchema/zodReExport.ts +44 -24
  178. package/src/tools/implementation/zodSchema/zodSchema.ts +64 -102
  179. package/src/tools/internal.ts +3 -2
  180. package/src/tools/lib/id.ts +3 -0
  181. package/src/tools/lib/utilityTypes.ts +7 -0
  182. package/src/tools/subscribe/SubscriptionScope.ts +10 -1
  183. package/src/tools/subscribe/utils.ts +4 -2
  184. package/src/tools/testing.ts +4 -4
  185. package/src/tools/tests/ContextManager.test.ts +8 -9
  186. package/src/tools/tests/account.test.ts +65 -3
  187. package/src/tools/tests/coDiscriminatedUnion.test-d.ts +38 -0
  188. package/src/tools/tests/coDiscriminatedUnion.test.ts +219 -1
  189. package/src/tools/tests/coFeed.test-d.ts +4 -3
  190. package/src/tools/tests/coList.test-d.ts +32 -3
  191. package/src/tools/tests/coList.test.ts +20 -2
  192. package/src/tools/tests/coMap.record.test-d.ts +31 -3
  193. package/src/tools/tests/coMap.record.test.ts +9 -9
  194. package/src/tools/tests/coMap.test-d.ts +8 -8
  195. package/src/tools/tests/coMap.test.ts +19 -5
  196. package/src/tools/tests/coOptional.test.ts +63 -1
  197. package/src/tools/tests/createContext.test.ts +7 -9
  198. package/src/tools/tests/deepLoading.test.ts +4 -10
  199. package/src/tools/tests/exportImport.test.ts +526 -0
  200. package/src/tools/tests/groupsAndAccounts.test.ts +5 -4
  201. package/src/tools/tests/inbox.test.ts +3 -2
  202. package/src/tools/tests/load.test.ts +3 -29
  203. package/src/tools/tests/request.test.ts +951 -0
  204. package/src/tools/tests/schemaUnion.test.ts +2 -2
  205. package/src/tools/tests/subscribe.test.ts +22 -114
  206. package/src/tools/tests/testing.test.ts +6 -6
  207. package/src/tools/tests/zod.test-d.ts +27 -0
  208. package/src/tools/tests/zod.test.ts +50 -45
  209. package/src/worker/index.ts +0 -1
  210. package/tsup.config.ts +0 -2
  211. package/dist/chunk-4CFNXQE7.js.map +0 -1
  212. package/dist/tools/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.d.ts +0 -12
  213. package/dist/tools/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.d.ts.map +0 -1
  214. package/dist/tools/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.d.ts +0 -9
  215. package/dist/tools/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.d.ts.map +0 -1
  216. package/src/tools/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.ts +0 -172
  217. package/src/tools/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.ts +0 -218
@@ -0,0 +1,633 @@
1
+ import {
2
+ CoValueCore,
3
+ CojsonInternalTypes,
4
+ CryptoProvider,
5
+ RawAccount,
6
+ RawCoMap,
7
+ cojsonInternals,
8
+ } from "cojson";
9
+ import z from "zod/v4";
10
+ import {
11
+ AnyZodOrCoValueSchema,
12
+ CoMap,
13
+ CoMapInitZod,
14
+ CoMapSchema,
15
+ CoValueClass,
16
+ CoreCoMapSchema,
17
+ Group,
18
+ Loaded,
19
+ ResolveQuery,
20
+ ResolveQueryStrict,
21
+ Simplify,
22
+ coMapDefiner,
23
+ coValueClassFromCoValueClassOrSchema,
24
+ exportCoValue,
25
+ importContentPieces,
26
+ loadCoValue,
27
+ } from "../internal.js";
28
+ import { isCoValueId } from "../lib/id.js";
29
+ import { Account } from "./account.js";
30
+
31
+ type MessageShape = Record<string, AnyZodOrCoValueSchema>;
32
+
33
+ type RequestSchemaDefinition<
34
+ S extends MessageShape,
35
+ R extends ResolveQuery<CoMapSchema<S>> = true,
36
+ > =
37
+ | S
38
+ | {
39
+ schema: S;
40
+ resolve?: R;
41
+ };
42
+
43
+ /**
44
+ * Configuration options for defining HTTP request/response schemas in Jazz.
45
+ *
46
+ * This interface defines the structure for creating typed HTTP routes with
47
+ * request and response validation using CoMap schemas.
48
+ *
49
+ * @template RequestShape - The shape of the request message schema (must extend MessageShape)
50
+ * @template RequestResolve - The resolve query type for the request CoMap schema
51
+ * @template ResponseShape - The shape of the response message schema (must extend MessageShape)
52
+ * @template ResponseResolve - The resolve query type for the response CoMap schema
53
+ */
54
+ interface RequestOptions<
55
+ RequestShape extends MessageShape,
56
+ RequestResolve extends ResolveQuery<CoMapSchema<RequestShape>>,
57
+ ResponseShape extends MessageShape,
58
+ ResponseResolve extends ResolveQuery<CoMapSchema<ResponseShape>>,
59
+ > {
60
+ /**
61
+ * The URL endpoint for the HTTP route.
62
+ * This is used by the client to send requests to the server.
63
+ */
64
+ url: string;
65
+
66
+ /**
67
+ * The id of the worker Account or Group.
68
+ */
69
+ workerId: string;
70
+
71
+ /**
72
+ * Schema definition for the request payload.
73
+ * Can be either a direct schema object or an object with schema and optional resolve properties.
74
+ * The schema defines the structure and validation rules for incoming requests.
75
+ */
76
+ request: RequestSchemaDefinition<
77
+ RequestShape,
78
+ ResolveQueryStrict<CoMapSchema<RequestShape>, RequestResolve>
79
+ >;
80
+
81
+ /**
82
+ * Schema definition for the response payload.
83
+ * Can be either a direct schema object or an object with schema and optional resolve properties.
84
+ * The schema defines the structure and validation rules for outgoing responses.
85
+ */
86
+ response: RequestSchemaDefinition<
87
+ ResponseShape,
88
+ ResolveQueryStrict<CoMapSchema<ResponseShape>, ResponseResolve>
89
+ >;
90
+ }
91
+
92
+ type AsNullablePayload<T extends MessageShape> = T extends Record<string, never>
93
+ ? undefined
94
+ : never;
95
+ type MessageValuePayload<T extends MessageShape> =
96
+ | Simplify<CoMapInitZod<T>>
97
+ | AsNullablePayload<T>;
98
+
99
+ function createMessageEnvelope<S extends MessageShape>(
100
+ schema: CoreCoMapSchema,
101
+ value: MessageValuePayload<S>,
102
+ owner: Account,
103
+ sharedWith: Account | Group,
104
+ type: "request" | "response",
105
+ ): Loaded<CoMapSchema<S>> {
106
+ const group = Group.create({ owner });
107
+
108
+ if (type === "request") {
109
+ group.addMember(sharedWith, "writer");
110
+ } else {
111
+ group.addMember(sharedWith, "reader");
112
+ }
113
+
114
+ // @ts-expect-error - CoreCoMapSchema doesn't have static methods
115
+ return schema.create(value ?? {}, group);
116
+ }
117
+
118
+ /**
119
+ * Function that exports the input CoValue in a serializable format and prepares the information
120
+ * required for the other side to safely verify the identity of the sender.
121
+ */
122
+ async function serializeMessagePayload({
123
+ type,
124
+ schema,
125
+ resolve,
126
+ value,
127
+ owner,
128
+ target,
129
+ }: {
130
+ // Skipping type validation here to avoid excessive type complexity that affects the typecheck performance
131
+ type: "request" | "response";
132
+ schema: CoreCoMapSchema;
133
+ resolve: any;
134
+ value: any;
135
+ owner: Account;
136
+ target: Account | Group;
137
+ }) {
138
+ const me = owner ?? Account.getMe();
139
+ const node = me._raw.core.node;
140
+ const crypto = node.crypto;
141
+
142
+ const agent = node.getCurrentAgent();
143
+ const signerID = agent.currentSignerID();
144
+ const signerSecret = agent.currentSignerSecret();
145
+
146
+ const envelope = createMessageEnvelope(schema, value, me, target, type);
147
+
148
+ const contentPieces =
149
+ (await exportCoValue(schema, envelope.id, {
150
+ resolve,
151
+ loadAs: me,
152
+ bestEffortResolution: true,
153
+ })) ?? [];
154
+
155
+ const createdAt = Date.now();
156
+
157
+ const signPayload = crypto.secureHash({
158
+ contentPieces,
159
+ id: envelope.id,
160
+ createdAt,
161
+ signerID,
162
+ });
163
+
164
+ const authToken = crypto.sign(signerSecret, signPayload);
165
+
166
+ return {
167
+ contentPieces,
168
+ id: envelope.id,
169
+ createdAt,
170
+ authToken,
171
+ signerID,
172
+ };
173
+ }
174
+
175
+ const requestSchema = z.object({
176
+ contentPieces: z.array(z.json()),
177
+ id: z.custom<`co_z${string}`>(isCoValueId),
178
+ createdAt: z.number(),
179
+ authToken: z.custom<`signature_z${string}`>(
180
+ (value) => typeof value === "string" && value.startsWith("signature_z"),
181
+ ),
182
+ signerID: z.custom<`signer_z${string}`>(
183
+ (value) => typeof value === "string" && value.startsWith("signer_z"),
184
+ ),
185
+ });
186
+
187
+ /**
188
+ * Function that parses the message payload, verifies the identity of the sender and loads the data.
189
+ *
190
+ * @returns The data from the message.
191
+ */
192
+ async function handleMessagePayload({
193
+ type,
194
+ schema,
195
+ resolve,
196
+ request,
197
+ loadAs,
198
+ }: {
199
+ type: "request" | "response";
200
+ // Skipping type validation here to avoid excessive type complexity that affects the typecheck performance
201
+ schema: CoreCoMapSchema;
202
+ resolve: any;
203
+ request: unknown;
204
+ loadAs: Account;
205
+ }) {
206
+ const node = loadAs._raw.core.node;
207
+ const crypto = node.crypto;
208
+
209
+ const requestParsed = requestSchema.safeParse(request);
210
+
211
+ if (!requestParsed.success) {
212
+ throw new JazzRequestError(
213
+ "Request payload is not valid",
214
+ 400,
215
+ requestParsed.error,
216
+ );
217
+ }
218
+
219
+ const requestData = requestParsed.data;
220
+
221
+ if (type === "request") {
222
+ const core = await node.loadCoValueCore(requestData.id, undefined, true);
223
+
224
+ // Check if the message has already been handled to prevent replay attacks
225
+ if (core.isAvailable()) {
226
+ const content = core.getCurrentContent() as RawCoMap;
227
+
228
+ if (content.get("$handled") === loadAs.id) {
229
+ throw new JazzRequestError("Request payload is already handled", 400);
230
+ }
231
+ }
232
+
233
+ // Check if the message is expired as extra protection
234
+ if (requestData.createdAt + 1000 * 60 < Date.now()) {
235
+ throw new JazzRequestError("Authentication token is expired", 401);
236
+ }
237
+ }
238
+
239
+ // Verify the signature of the message to prevent tampering
240
+ const signPayload = crypto.secureHash({
241
+ contentPieces: requestData.contentPieces,
242
+ id: requestData.id,
243
+ createdAt: requestData.createdAt,
244
+ signerID: requestData.signerID,
245
+ });
246
+
247
+ if (
248
+ !safeVerifySignature(
249
+ crypto,
250
+ signPayload,
251
+ requestData.signerID,
252
+ requestData.authToken,
253
+ )
254
+ ) {
255
+ throw new JazzRequestError("Invalid signature", 401);
256
+ }
257
+
258
+ let contentPieces =
259
+ requestData.contentPieces as CojsonInternalTypes.NewContentMessage[];
260
+
261
+ if (type === "request") {
262
+ const coValueContent = contentPieces.find(
263
+ (piece) => piece.id === requestData.id,
264
+ );
265
+
266
+ if (coValueContent && coValueContent.header) {
267
+ const validValues = cojsonInternals.getDependedOnCoValues(
268
+ coValueContent.header,
269
+ coValueContent,
270
+ );
271
+ validValues.add(requestData.id);
272
+ contentPieces = contentPieces.filter((piece) =>
273
+ validValues.has(piece.id),
274
+ );
275
+ } else {
276
+ contentPieces = [];
277
+ }
278
+ }
279
+
280
+ importContentPieces(contentPieces, loadAs);
281
+
282
+ const coValue = await node.loadCoValueCore(requestData.id);
283
+ const accountId = getCoValueCreatorAccountId(coValue);
284
+
285
+ const madeBy = await Account.load(accountId, {
286
+ loadAs,
287
+ });
288
+
289
+ if (!madeBy) {
290
+ throw new JazzRequestError("Creator account not found", 400);
291
+ }
292
+
293
+ const coSchema = coValueClassFromCoValueClassOrSchema(
294
+ schema,
295
+ ) as CoValueClass<CoMap>;
296
+ const value = await loadCoValue<CoMap, true>(coSchema, requestData.id, {
297
+ resolve,
298
+ loadAs,
299
+ });
300
+
301
+ if (!value) {
302
+ throw new JazzRequestError("Value not found", 400);
303
+ }
304
+
305
+ if (type === "request") {
306
+ value._raw.set("$handled", loadAs.id);
307
+ }
308
+
309
+ return {
310
+ value: value as unknown,
311
+ madeBy,
312
+ };
313
+ }
314
+
315
+ function parseSchemaAndResolve<
316
+ S extends MessageShape,
317
+ R extends ResolveQuery<CoMapSchema<S>>,
318
+ >(options: RequestSchemaDefinition<S, R>) {
319
+ if ("schema" in options) {
320
+ return {
321
+ // Using a type cast to reduce the type complexity
322
+ schema: coMapDefiner(options.schema) as CoreCoMapSchema,
323
+ resolve: options.resolve as any,
324
+ };
325
+ }
326
+
327
+ return {
328
+ schema: coMapDefiner(options) as CoreCoMapSchema,
329
+ resolve: true as any,
330
+ };
331
+ }
332
+
333
+ class HttpRoute<
334
+ RequestShape extends MessageShape = z.core.$ZodLooseShape,
335
+ RequestResolve extends ResolveQuery<CoMapSchema<RequestShape>> = any,
336
+ ResponseShape extends MessageShape = z.core.$ZodLooseShape,
337
+ ResponseResolve extends ResolveQuery<CoMapSchema<ResponseShape>> = any,
338
+ > {
339
+ private requestDefinition: {
340
+ schema: CoreCoMapSchema;
341
+ resolve: any;
342
+ };
343
+ private responseDefinition: {
344
+ schema: CoreCoMapSchema;
345
+ resolve: any;
346
+ };
347
+ private url: string;
348
+ private workerId: string;
349
+
350
+ constructor(
351
+ params: RequestOptions<
352
+ RequestShape,
353
+ RequestResolve,
354
+ ResponseShape,
355
+ ResponseResolve
356
+ >,
357
+ ) {
358
+ this.requestDefinition = parseSchemaAndResolve(params.request);
359
+ this.responseDefinition = parseSchemaAndResolve(params.response);
360
+ this.url = params.url;
361
+ this.workerId = params.workerId;
362
+
363
+ if (params.workerId === undefined) {
364
+ throw new TypeError("Worker ID is required");
365
+ }
366
+ }
367
+
368
+ async send(
369
+ values: MessageValuePayload<RequestShape>,
370
+ options?: { owner?: Account },
371
+ ): Promise<Loaded<CoMapSchema<ResponseShape>, ResponseResolve>> {
372
+ const as = options?.owner ?? Account.getMe();
373
+
374
+ const target = await loadWorkerAccountOrGroup(this.workerId, as);
375
+ if (!target) {
376
+ throw new JazzRequestError("Worker account not found", 400);
377
+ }
378
+
379
+ const response = await fetch(this.url, {
380
+ method: "POST",
381
+ headers: {
382
+ "Content-Type": "application/json",
383
+ },
384
+ body: JSON.stringify(
385
+ await serializeMessagePayload({
386
+ type: "request",
387
+ schema: this.requestDefinition.schema,
388
+ resolve: true, // export only the envelope
389
+ value: values,
390
+ owner: as,
391
+ target,
392
+ }),
393
+ ),
394
+ });
395
+
396
+ if (!response.ok) {
397
+ if (response.headers.has("X-Jazz-Request-Error")) {
398
+ const error = await response.json();
399
+ throw new JazzRequestError(error.message, error.code, error.details);
400
+ }
401
+
402
+ throw new JazzRequestError("Request failed", response.status);
403
+ }
404
+
405
+ const responseBody = await response.json();
406
+
407
+ const responseParsed = z
408
+ .object({
409
+ type: z.literal("success"),
410
+ payload: z.any(),
411
+ })
412
+ .safeParse(responseBody);
413
+
414
+ if (!responseParsed.success) {
415
+ throw new JazzRequestError(
416
+ "Response payload is not valid",
417
+ 400,
418
+ responseParsed.error,
419
+ );
420
+ }
421
+
422
+ const data = await handleMessagePayload({
423
+ type: "response",
424
+ schema: this.responseDefinition.schema,
425
+ resolve: this.responseDefinition.resolve,
426
+ request: responseParsed.data.payload,
427
+ loadAs: as,
428
+ });
429
+
430
+ return data.value as Loaded<CoMapSchema<ResponseShape>, ResponseResolve>;
431
+ }
432
+
433
+ handle = async (
434
+ request: Request,
435
+ as: Account,
436
+ callback: (
437
+ value: Loaded<CoMapSchema<RequestShape>, RequestResolve>,
438
+ madeBy: Account,
439
+ ) =>
440
+ | Promise<MessageValuePayload<ResponseShape>>
441
+ | MessageValuePayload<ResponseShape>,
442
+ ): Promise<Response> => {
443
+ try {
444
+ const response = await this.executeHandleRequest(request, as, callback);
445
+ return response;
446
+ } catch (error) {
447
+ // Serialize the error to make it possible to handle it on the client side
448
+ if (isJazzRequestError(error)) {
449
+ return new Response(JSON.stringify(error.toJSON()), {
450
+ status: error.code,
451
+ headers: {
452
+ "Content-Type": "application/json",
453
+ "X-Jazz-Request-Error": "true",
454
+ },
455
+ });
456
+ }
457
+
458
+ throw error;
459
+ }
460
+ };
461
+
462
+ executeHandleRequest = async (
463
+ request: Request,
464
+ as: Account,
465
+ callback: (
466
+ value: Loaded<CoMapSchema<RequestShape>, RequestResolve>,
467
+ madeBy: Account,
468
+ ) =>
469
+ | Promise<MessageValuePayload<ResponseShape>>
470
+ | MessageValuePayload<ResponseShape>,
471
+ ): Promise<Response> => {
472
+ const node = as._raw.core.node;
473
+ const body = await request.json();
474
+ const data = await handleMessagePayload({
475
+ type: "request",
476
+ schema: this.requestDefinition.schema,
477
+ resolve: this.requestDefinition.resolve,
478
+ request: body,
479
+ loadAs: as,
480
+ });
481
+
482
+ const tracking = node.syncManager.trackDirtyCoValues();
483
+
484
+ const responseValue = await callback(
485
+ data.value as Loaded<CoMapSchema<RequestShape>, RequestResolve>,
486
+ data.madeBy,
487
+ );
488
+
489
+ const responsePayload = await serializeMessagePayload({
490
+ type: "response",
491
+ schema: this.responseDefinition.schema,
492
+ resolve: this.responseDefinition.resolve,
493
+ value: responseValue,
494
+ owner: as,
495
+ target: data.madeBy,
496
+ });
497
+
498
+ const responseBody = JSON.stringify({
499
+ type: "success",
500
+ payload: responsePayload,
501
+ });
502
+
503
+ // TODO: Detect the defer support from the environment
504
+ await Promise.all(
505
+ Array.from(tracking.done(), (id) => node.syncManager.waitForSync(id)),
506
+ );
507
+
508
+ return new Response(responseBody, {
509
+ status: 200,
510
+ headers: {
511
+ "Content-Type": "application/json",
512
+ },
513
+ });
514
+ };
515
+
516
+ get requestSchema(): CoMapSchema<RequestShape> {
517
+ return this.requestDefinition.schema as CoMapSchema<RequestShape>;
518
+ }
519
+
520
+ get responseSchema() {
521
+ return this.responseDefinition.schema as CoMapSchema<ResponseShape>;
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Define a request route.
527
+ *
528
+ * @param params - The parameters for the request route.
529
+ * @returns The request route.
530
+ *
531
+ * @see {@link https://jazz.tools/docs/react/server-side/http-requests}
532
+ */
533
+ export function experimental_defineRequest<
534
+ RequestShape extends MessageShape,
535
+ RequestResolve extends ResolveQuery<CoMapSchema<RequestShape>>,
536
+ ResponseShape extends MessageShape,
537
+ ResponseResolve extends ResolveQuery<CoMapSchema<ResponseShape>>,
538
+ >(
539
+ params: RequestOptions<
540
+ RequestShape,
541
+ RequestResolve,
542
+ ResponseShape,
543
+ ResponseResolve
544
+ >,
545
+ ) {
546
+ return new HttpRoute(params);
547
+ }
548
+
549
+ function getCoValueCreatorAccountId(coValue: CoValueCore) {
550
+ if (!coValue.isAvailable()) {
551
+ throw new Error("Unable to load the request payload");
552
+ }
553
+
554
+ const creatorSessionId = coValue.getValidSortedTransactions().at(0)
555
+ ?.txID.sessionID;
556
+
557
+ if (!creatorSessionId) {
558
+ throw new JazzRequestError(
559
+ "Request payload is not valid, creator session ID not found",
560
+ 400,
561
+ );
562
+ }
563
+
564
+ const accountId =
565
+ cojsonInternals.accountOrAgentIDfromSessionID(creatorSessionId);
566
+
567
+ if (!isCoValueId(accountId)) {
568
+ throw new JazzRequestError(
569
+ "Request payload is not valid, the creator is not a valid account",
570
+ 400,
571
+ );
572
+ }
573
+
574
+ return accountId;
575
+ }
576
+
577
+ export class JazzRequestError {
578
+ public readonly isJazzRequestError = true;
579
+
580
+ constructor(
581
+ public readonly message: string,
582
+ public readonly code: number,
583
+ public readonly details?: unknown,
584
+ ) {}
585
+
586
+ toJSON() {
587
+ return { message: this.message, code: this.code, details: this.details };
588
+ }
589
+ }
590
+
591
+ export function isJazzRequestError(error: unknown): error is JazzRequestError {
592
+ return (
593
+ error instanceof JazzRequestError ||
594
+ (typeof error === "object" &&
595
+ error !== null &&
596
+ "isJazzRequestError" in error &&
597
+ Boolean(error.isJazzRequestError))
598
+ );
599
+ }
600
+
601
+ function safeVerifySignature(
602
+ crypto: CryptoProvider,
603
+ signPayload: `hash_z${string}`,
604
+ signerID: `signer_z${string}`,
605
+ authToken: `signature_z${string}`,
606
+ ) {
607
+ try {
608
+ return crypto.verify(authToken, signPayload, signerID);
609
+ } catch (error) {
610
+ return false;
611
+ }
612
+ }
613
+
614
+ async function loadWorkerAccountOrGroup(id: string, loadAs: Account) {
615
+ const node = loadAs._raw.core.node;
616
+ const coValue = await node.loadCoValueCore(id as `co_z${string}`);
617
+
618
+ if (!coValue.isAvailable()) {
619
+ return null;
620
+ }
621
+
622
+ const content = coValue.getCurrentContent();
623
+
624
+ if (content instanceof RawAccount) {
625
+ return Account.load(content.id, {
626
+ loadAs,
627
+ });
628
+ }
629
+
630
+ return Group.load(content.id, {
631
+ loadAs,
632
+ });
633
+ }
@@ -6,8 +6,6 @@ import {
6
6
  CoValueClass,
7
7
  CoValueFromRaw,
8
8
  ID,
9
- RefsToResolve,
10
- RefsToResolveStrict,
11
9
  Resolved,
12
10
  SubscribeListenerOptions,
13
11
  SubscribeRestArgs,
@@ -16,6 +14,12 @@ import {
16
14
  subscribeToCoValueWithoutMe,
17
15
  } from "../internal.js";
18
16
 
17
+ /**
18
+ * Extends `SchemaUnion` with a non-abstract constructor.
19
+ */
20
+ export type SchemaUnionConcreteSubclass<V extends CoValue> =
21
+ typeof SchemaUnion & CoValueClass<V>;
22
+
19
23
  /**
20
24
  * SchemaUnion allows you to create union types of CoValues that can be discriminated at runtime.
21
25
  *
@@ -86,7 +90,7 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
86
90
  **/
87
91
  static Of<V extends CoValue>(
88
92
  discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
89
- ): CoValueClass<V> & typeof SchemaUnion {
93
+ ): SchemaUnionConcreteSubclass<V> {
90
94
  return class SchemaUnionClass extends SchemaUnion {
91
95
  static override fromRaw<T extends CoValue>(
92
96
  this: CoValueClass<T> & CoValueFromRaw<T>,
@@ -97,7 +101,7 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
97
101
  ) as unknown as CoValueClass<T> & CoValueFromRaw<T>;
98
102
  return ResolvedClass.fromRaw(raw);
99
103
  }
100
- } as unknown as CoValueClass<V> & typeof SchemaUnion;
104
+ } as unknown as SchemaUnionConcreteSubclass<V>;
101
105
  }
102
106
 
103
107
  /**
@@ -35,7 +35,6 @@ export type {
35
35
  TextPos,
36
36
  AccountClass,
37
37
  AccountCreationProps,
38
- WithHelpers,
39
38
  } from "./internal.js";
40
39
 
41
40
  export {
@@ -49,11 +48,12 @@ export {
49
48
  CoRichText,
50
49
  Account,
51
50
  isControlledAccount,
52
- createCoValueObservable,
53
51
  loadCoValue,
54
52
  subscribeToCoValue,
55
53
  ImageDefinition,
56
54
  SubscriptionScope,
55
+ exportCoValue,
56
+ importContentPieces,
57
57
  } from "./internal.js";
58
58
 
59
59
  export {
@@ -97,24 +97,21 @@ export {
97
97
  export type * from "./types.js";
98
98
 
99
99
  export {
100
- zodSchemaToCoSchema,
101
- anySchemaToCoSchema,
100
+ coValueClassFromCoValueClassOrSchema,
102
101
  type InstanceOfSchema,
103
102
  type InstanceOfSchemaCoValuesNullable,
104
- type CoValueOrZodSchema,
103
+ type CoValueClassOrSchema,
105
104
  type Loaded,
106
105
  type BaseAccountShape,
107
106
  type DefaultAccountShape,
108
- type AccountSchema,
109
- type AnyAccountSchema,
110
- type CoListSchema,
111
- type CoMapSchema,
112
- type CoFeedSchema,
113
- type PlainTextSchema,
114
- type FileStreamSchema,
107
+ type CoreAccountSchema as AnyAccountSchema,
115
108
  type ResolveQuery,
116
109
  type ResolveQueryStrict,
117
110
  type InitFor,
118
- type CoRecordSchema,
119
- type CoProfileSchema,
120
111
  } from "./internal.js";
112
+
113
+ export {
114
+ experimental_defineRequest,
115
+ JazzRequestError,
116
+ isJazzRequestError,
117
+ } from "./coValues/request.js";