jazz-tools 0.19.11 → 0.19.13

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 (151) hide show
  1. package/.turbo/turbo-build.log +51 -51
  2. package/CHANGELOG.md +22 -0
  3. package/dist/browser/createBrowserContext.d.ts +1 -5
  4. package/dist/browser/createBrowserContext.d.ts.map +1 -1
  5. package/dist/browser/index.js +124 -47
  6. package/dist/browser/index.js.map +1 -1
  7. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts +12 -0
  8. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts.map +1 -0
  9. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts +2 -0
  10. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts.map +1 -0
  11. package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts +6 -0
  12. package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts.map +1 -0
  13. package/dist/browser/provideBrowserLockSession/index.d.ts +4 -0
  14. package/dist/browser/provideBrowserLockSession/index.d.ts.map +1 -0
  15. package/dist/{chunk-HX5S6W5E.js → chunk-GAPMDNJY.js} +492 -108
  16. package/dist/chunk-GAPMDNJY.js.map +1 -0
  17. package/dist/index.js +5 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/inspector/{chunk-C6BJPHBQ.js → chunk-YQNK5Y7B.js} +47 -35
  20. package/dist/inspector/chunk-YQNK5Y7B.js.map +1 -0
  21. package/dist/inspector/{custom-element-GJVBPZES.js → custom-element-KYV64IOC.js} +47 -35
  22. package/dist/inspector/{custom-element-GJVBPZES.js.map → custom-element-KYV64IOC.js.map} +1 -1
  23. package/dist/inspector/index.js +1 -1
  24. package/dist/inspector/register-custom-element.js +1 -1
  25. package/dist/inspector/standalone.js +1 -1
  26. package/dist/inspector/tests/utils/transactions-changes.test.d.ts +2 -0
  27. package/dist/inspector/tests/utils/transactions-changes.test.d.ts.map +1 -0
  28. package/dist/inspector/utils/transactions-changes.d.ts +13 -13
  29. package/dist/inspector/utils/transactions-changes.d.ts.map +1 -1
  30. package/dist/react/index.js +4 -1
  31. package/dist/react/index.js.map +1 -1
  32. package/dist/react/provider.d.ts.map +1 -1
  33. package/dist/react-core/index.js +2 -2
  34. package/dist/react-core/index.js.map +1 -1
  35. package/dist/react-native/index.js +45 -13
  36. package/dist/react-native/index.js.map +1 -1
  37. package/dist/react-native-core/ReactNativeSessionProvider.d.ts +11 -0
  38. package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -0
  39. package/dist/react-native-core/index.js +45 -13
  40. package/dist/react-native-core/index.js.map +1 -1
  41. package/dist/react-native-core/platform.d.ts +2 -8
  42. package/dist/react-native-core/platform.d.ts.map +1 -1
  43. package/dist/react-native-core/provider.d.ts.map +1 -1
  44. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts +2 -0
  45. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts.map +1 -0
  46. package/dist/testing.js +4 -3
  47. package/dist/testing.js.map +1 -1
  48. package/dist/tools/coValues/account.d.ts.map +1 -1
  49. package/dist/tools/coValues/coFeed.d.ts +2 -2
  50. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  51. package/dist/tools/coValues/coList.d.ts +1 -2
  52. package/dist/tools/coValues/coList.d.ts.map +1 -1
  53. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  54. package/dist/tools/coValues/coVector.d.ts.map +1 -1
  55. package/dist/tools/coValues/group.d.ts +5 -1
  56. package/dist/tools/coValues/group.d.ts.map +1 -1
  57. package/dist/tools/coValues/interfaces.d.ts +2 -1
  58. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  59. package/dist/tools/exports.d.ts +2 -2
  60. package/dist/tools/exports.d.ts.map +1 -1
  61. package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
  62. package/dist/tools/implementation/createContext.d.ts +21 -11
  63. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  64. package/dist/tools/implementation/schema.d.ts +14 -6
  65. package/dist/tools/implementation/schema.d.ts.map +1 -1
  66. package/dist/tools/implementation/schemaUtils.d.ts +1 -1
  67. package/dist/tools/implementation/schemaUtils.d.ts.map +1 -1
  68. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  69. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts +99 -0
  70. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts.map +1 -0
  71. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +11 -0
  72. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  73. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +11 -0
  74. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  75. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +15 -1
  76. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  77. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +10 -0
  78. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
  79. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +9 -0
  80. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -1
  81. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +13 -1
  82. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  83. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +10 -0
  84. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
  85. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -0
  86. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
  87. package/dist/tools/implementation/zodSchema/unionUtils.d.ts +12 -1
  88. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  89. package/dist/tools/internal.d.ts +1 -0
  90. package/dist/tools/internal.d.ts.map +1 -1
  91. package/dist/tools/subscribe/SubscriptionScope.d.ts +3 -6
  92. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  93. package/dist/tools/testing.d.ts.map +1 -1
  94. package/dist/tools/tests/schema.withPermissions.test.d.ts +2 -0
  95. package/dist/tools/tests/schema.withPermissions.test.d.ts.map +1 -0
  96. package/dist/worker/index.js +2 -2
  97. package/dist/worker/index.js.map +1 -1
  98. package/package.json +4 -4
  99. package/src/browser/createBrowserContext.ts +3 -62
  100. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +406 -0
  101. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +132 -0
  102. package/src/browser/provideBrowserLockSession/SessionIDStorage.ts +33 -0
  103. package/src/browser/provideBrowserLockSession/index.ts +11 -0
  104. package/src/inspector/tests/utils/transactions-changes.test.ts +102 -0
  105. package/src/inspector/ui/icons/add-icon.tsx +3 -3
  106. package/src/inspector/utils/history.ts +6 -6
  107. package/src/inspector/utils/transactions-changes.ts +37 -3
  108. package/src/inspector/viewer/history-view.tsx +13 -13
  109. package/src/react/provider.tsx +6 -1
  110. package/src/react-core/hooks.ts +2 -2
  111. package/src/react-core/tests/useSuspenseCoState.test.tsx +47 -0
  112. package/src/react-native-core/ReactNativeSessionProvider.ts +52 -0
  113. package/src/react-native-core/platform.ts +5 -30
  114. package/src/react-native-core/provider.tsx +6 -1
  115. package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +124 -0
  116. package/src/tools/coValues/account.ts +4 -0
  117. package/src/tools/coValues/coFeed.ts +8 -3
  118. package/src/tools/coValues/coList.ts +6 -3
  119. package/src/tools/coValues/coMap.ts +10 -0
  120. package/src/tools/coValues/coVector.ts +2 -1
  121. package/src/tools/coValues/group.ts +6 -4
  122. package/src/tools/coValues/interfaces.ts +19 -7
  123. package/src/tools/exports.ts +3 -1
  124. package/src/tools/implementation/ContextManager.ts +10 -0
  125. package/src/tools/implementation/createContext.ts +43 -15
  126. package/src/tools/implementation/schema.ts +23 -13
  127. package/src/tools/implementation/schemaUtils.ts +1 -1
  128. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +105 -4
  129. package/src/tools/implementation/zodSchema/schemaPermissions.ts +188 -0
  130. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +46 -3
  131. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +46 -3
  132. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +50 -13
  133. package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +14 -0
  134. package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +24 -1
  135. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +51 -4
  136. package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +25 -1
  137. package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +21 -1
  138. package/src/tools/implementation/zodSchema/unionUtils.ts +72 -20
  139. package/src/tools/internal.ts +1 -0
  140. package/src/tools/subscribe/SubscriptionScope.ts +61 -39
  141. package/src/tools/testing.ts +3 -1
  142. package/src/tools/tests/ContextManager.test.ts +2 -1
  143. package/src/tools/tests/coPlainText.test.ts +2 -2
  144. package/src/tools/tests/createContext.test.ts +79 -1
  145. package/src/tools/tests/deepLoading.test.ts +25 -2
  146. package/src/tools/tests/schema.resolved.test.ts +10 -0
  147. package/src/tools/tests/schema.withPermissions.test.ts +859 -0
  148. package/src/tools/tests/utils.ts +2 -2
  149. package/src/worker/index.ts +2 -2
  150. package/dist/chunk-HX5S6W5E.js.map +0 -1
  151. package/dist/inspector/chunk-C6BJPHBQ.js.map +0 -1
@@ -0,0 +1,859 @@
1
+ import {
2
+ afterEach,
3
+ assert,
4
+ beforeAll,
5
+ describe,
6
+ expect,
7
+ test,
8
+ vi,
9
+ } from "vitest";
10
+ import {
11
+ Account,
12
+ co,
13
+ CoValueLoadingState,
14
+ Group,
15
+ setDefaultSchemaPermissions,
16
+ z,
17
+ } from "../exports";
18
+ import {
19
+ assertLoaded,
20
+ createJazzTestAccount,
21
+ setupJazzTestSync,
22
+ } from "../testing";
23
+
24
+ beforeAll(async () => {
25
+ await setupJazzTestSync();
26
+ await createJazzTestAccount({
27
+ isCurrentActiveAccount: true,
28
+ creationProps: { name: "Hermes Puggington" },
29
+ });
30
+ });
31
+
32
+ describe("Schema.withPermissions()", () => {
33
+ let me: Account;
34
+ let anotherAccount: Account;
35
+
36
+ beforeAll(async () => {
37
+ me = Account.getMe();
38
+ anotherAccount = await createJazzTestAccount();
39
+
40
+ // Load anotherAccount to make `addMember` work without needing manual loading
41
+ await Account.load(anotherAccount.$jazz.id, {
42
+ loadAs: me,
43
+ });
44
+ });
45
+
46
+ test("can define permissions on all CoValue schemas for concrete CoValue types", () => {
47
+ const AllSchemas = [
48
+ co.plainText(),
49
+ co.richText(),
50
+ co.fileStream(),
51
+ co.vector(1),
52
+ co.list(co.plainText()),
53
+ co.feed(co.plainText()),
54
+ co.map({ text: co.plainText() }),
55
+ co.record(z.string(), co.plainText()),
56
+ ];
57
+
58
+ for (const Schema of AllSchemas) {
59
+ const SchemaWithPermissions = Schema.withPermissions({
60
+ onInlineCreate: "extendsContainer",
61
+ });
62
+ expect(SchemaWithPermissions.permissions).toEqual({
63
+ onInlineCreate: "extendsContainer",
64
+ });
65
+ }
66
+ });
67
+
68
+ test("cannot define permissions on co.account()", () => {
69
+ expect(() =>
70
+ co
71
+ .account()
72
+ // @ts-expect-error: Accounts do not have permissions
73
+ .withPermissions({ onInlineCreate: "extendsContainer" }),
74
+ ).toThrow();
75
+ });
76
+
77
+ test("cannot define permissions on co.group()", () => {
78
+ expect(() =>
79
+ co
80
+ .group()
81
+ // @ts-expect-error: Groups do not have permissions
82
+ .withPermissions({ onInlineCreate: "extendsContainer" }),
83
+ ).toThrow();
84
+ });
85
+
86
+ test("cannot define permissions on co.optional()", () => {
87
+ expect(() =>
88
+ co
89
+ .optional(co.plainText())
90
+ // @ts-expect-error: cannot create CoValues with co.optional schema
91
+ .withPermissions({ onInlineCreate: "extendsContainer" }),
92
+ ).toThrow();
93
+ });
94
+
95
+ test("cannot define permissions on co.discriminatedUnion()", () => {
96
+ expect(() =>
97
+ co
98
+ .discriminatedUnion("type", [
99
+ co.map({ type: z.literal("a") }),
100
+ co.map({ type: z.literal("b") }),
101
+ ])
102
+ // @ts-expect-error: cannot create CoValues with co.discriminatedUnion schema
103
+ .withPermissions({ onInlineCreate: "extendsContainer" }),
104
+ ).toThrow();
105
+ });
106
+
107
+ describe("default", () => {
108
+ describe("defines a default owner to be used when no explicit owner is passed to .create()", () => {
109
+ test("for CoMap", async () => {
110
+ const TestMap = co.map({ name: co.plainText() }).withPermissions({
111
+ default: () => Group.create().makePublic(),
112
+ });
113
+
114
+ const map = TestMap.create({ name: "Hi!" });
115
+
116
+ const loadedMap = await TestMap.load(map.$jazz.id, {
117
+ resolve: { name: true },
118
+ loadAs: anotherAccount,
119
+ });
120
+ assertLoaded(loadedMap);
121
+ expect(loadedMap.name.toString()).toEqual("Hi!");
122
+ });
123
+
124
+ test("for CoList", async () => {
125
+ const TestList = co.list(z.string()).withPermissions({
126
+ default: () => Group.create().makePublic(),
127
+ });
128
+
129
+ const list = TestList.create(["a", "b", "c"]);
130
+
131
+ const loadedList = await TestList.load(list.$jazz.id, {
132
+ loadAs: anotherAccount,
133
+ });
134
+ assertLoaded(loadedList);
135
+ expect(loadedList).toEqual(["a", "b", "c"]);
136
+ });
137
+
138
+ test("for CoFeed", async () => {
139
+ const TestFeed = co.feed(z.string()).withPermissions({
140
+ default: () => Group.create().makePublic(),
141
+ });
142
+
143
+ const feed = TestFeed.create(["a", "b", "c"]);
144
+
145
+ const loadedFeed = await TestFeed.load(feed.$jazz.id, {
146
+ loadAs: anotherAccount,
147
+ });
148
+ assertLoaded(loadedFeed);
149
+ expect(loadedFeed.perAccount[me.$jazz.id]?.value).toEqual("c");
150
+ });
151
+
152
+ test("for CoPlainText", async () => {
153
+ const TestPlainText = co.plainText().withPermissions({
154
+ default: () => Group.create().makePublic(),
155
+ });
156
+
157
+ const plainText = TestPlainText.create("Hello");
158
+
159
+ const loadedPlainText = await TestPlainText.load(plainText.$jazz.id, {
160
+ loadAs: anotherAccount,
161
+ });
162
+ assertLoaded(loadedPlainText);
163
+ expect(loadedPlainText.toString()).toEqual("Hello");
164
+ });
165
+
166
+ test("for CoRichText", async () => {
167
+ const TestRichText = co.richText().withPermissions({
168
+ default: () => Group.create().makePublic(),
169
+ });
170
+
171
+ const richText = TestRichText.create("Hello");
172
+
173
+ const loadedRichText = await TestRichText.load(richText.$jazz.id, {
174
+ loadAs: anotherAccount,
175
+ });
176
+ assertLoaded(loadedRichText);
177
+ expect(loadedRichText.toString()).toEqual("Hello");
178
+ });
179
+
180
+ describe("for FileStream", async () => {
181
+ test(".create()", async () => {
182
+ const TestFileStream = co.fileStream().withPermissions({
183
+ default: () => Group.create().makePublic(),
184
+ });
185
+
186
+ const fileStream = TestFileStream.create();
187
+ fileStream.start({ mimeType: "text/plain" });
188
+ fileStream.end();
189
+
190
+ const loadedFileStream = await TestFileStream.load(
191
+ fileStream.$jazz.id,
192
+ {
193
+ loadAs: anotherAccount,
194
+ },
195
+ );
196
+ assertLoaded(loadedFileStream);
197
+ expect(loadedFileStream.getMetadata()).toEqual({
198
+ mimeType: "text/plain",
199
+ });
200
+ });
201
+
202
+ test(".createFromBlob()", async () => {
203
+ const TestFileStream = co.fileStream().withPermissions({
204
+ default: () => Group.create().makePublic(),
205
+ });
206
+
207
+ const blob = new Blob(["test"], { type: "text/plain" });
208
+ const fileStream = await TestFileStream.createFromBlob(blob);
209
+
210
+ const loadedFileStream = await TestFileStream.load(
211
+ fileStream.$jazz.id,
212
+ {
213
+ loadAs: anotherAccount,
214
+ },
215
+ );
216
+ assertLoaded(loadedFileStream);
217
+ expect(loadedFileStream.getMetadata()).toEqual({
218
+ mimeType: "text/plain",
219
+ totalSizeBytes: 4,
220
+ });
221
+ });
222
+
223
+ test(".createFromArrayBuffer()", async () => {
224
+ const TestFileStream = co.fileStream().withPermissions({
225
+ default: () => Group.create().makePublic(),
226
+ });
227
+
228
+ const arrayBuffer = new TextEncoder().encode("test").buffer;
229
+ const fileStream = await TestFileStream.createFromArrayBuffer(
230
+ arrayBuffer,
231
+ "text/plain",
232
+ "filename",
233
+ );
234
+
235
+ const loadedFileStream = await TestFileStream.load(
236
+ fileStream.$jazz.id,
237
+ {
238
+ loadAs: anotherAccount,
239
+ },
240
+ );
241
+ assertLoaded(loadedFileStream);
242
+ expect(loadedFileStream.getMetadata()).toEqual({
243
+ mimeType: "text/plain",
244
+ totalSizeBytes: 4,
245
+ fileName: "filename",
246
+ });
247
+ });
248
+ });
249
+
250
+ test("for CoVector", async () => {
251
+ const TestVector = co.vector(1).withPermissions({
252
+ default: () => Group.create().makePublic(),
253
+ });
254
+
255
+ const vector = TestVector.create([1]);
256
+
257
+ const loadedVector = await TestVector.load(vector.$jazz.id, {
258
+ loadAs: anotherAccount,
259
+ });
260
+ assertLoaded(loadedVector);
261
+ expect(loadedVector.toJSON()).toEqual([1]);
262
+ });
263
+ });
264
+
265
+ test("the default owner is not used when providing an explicit owner", async () => {
266
+ let spy = vi.fn();
267
+ const TestMap = co.map({ name: co.plainText() }).withPermissions({
268
+ default: () => {
269
+ spy();
270
+ return Group.create().makePublic();
271
+ },
272
+ });
273
+ const map = TestMap.create({ name: "Hi!" }, { owner: Group.create() });
274
+
275
+ const loadedMap = await TestMap.load(map.$jazz.id, {
276
+ resolve: { name: true },
277
+ loadAs: anotherAccount,
278
+ });
279
+ expect(spy).not.toHaveBeenCalled();
280
+ expect(loadedMap.$isLoaded).toBe(false);
281
+ expect(loadedMap.$jazz.loadingState).toBe(
282
+ CoValueLoadingState.UNAUTHORIZED,
283
+ );
284
+ });
285
+ });
286
+
287
+ describe("onInlineCreate", () => {
288
+ describe("defines how the owner is obtained when creating CoValues from JSON", () => {
289
+ describe("extendsContainer", () => {
290
+ test("creates a new group that includes the container CoValue's owner as a member", async () => {
291
+ const TestMap = co.map({
292
+ name: co
293
+ .plainText()
294
+ .withPermissions({ onInlineCreate: "extendsContainer" }),
295
+ });
296
+
297
+ const parentOwner = Group.create({ owner: me });
298
+ parentOwner.addMember(anotherAccount, "writer");
299
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
300
+
301
+ const childOwner = map.name.$jazz.owner;
302
+
303
+ expect(
304
+ childOwner.getParentGroups().map((group) => group.$jazz.id),
305
+ ).toContain(parentOwner.$jazz.id);
306
+ expect(childOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
307
+ "writer",
308
+ );
309
+ });
310
+
311
+ test("allows overriding the role of the container CoValue's owner", async () => {
312
+ const TestMap = co.map({
313
+ name: co.plainText().withPermissions({
314
+ onInlineCreate: { extendsContainer: "reader" },
315
+ }),
316
+ });
317
+
318
+ const parentOwner = Group.create({ owner: me });
319
+ parentOwner.addMember(anotherAccount, "writer");
320
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
321
+
322
+ const childOwner = map.name.$jazz.owner;
323
+ expect(
324
+ childOwner.getParentGroups().map((group) => group.$jazz.id),
325
+ ).toContain(parentOwner.$jazz.id);
326
+ expect(parentOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
327
+ "writer",
328
+ );
329
+ expect(childOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
330
+ "reader",
331
+ );
332
+ });
333
+ });
334
+
335
+ describe("newGroup", () => {
336
+ test("creates a new group with the active account as the admin", async () => {
337
+ const TestMap = co.map({
338
+ name: co
339
+ .plainText()
340
+ .withPermissions({ onInlineCreate: "newGroup" }),
341
+ });
342
+
343
+ const parentOwner = Group.create({ owner: me });
344
+ parentOwner.addMember(anotherAccount, "writer");
345
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
346
+
347
+ const childOwner = map.name.$jazz.owner;
348
+ expect(parentOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
349
+ "writer",
350
+ );
351
+ expect(
352
+ childOwner.getRoleOf(anotherAccount.$jazz.id),
353
+ ).not.toBeDefined();
354
+ expect(childOwner.members.map((member) => member.account)).toEqual([
355
+ co.account().getMe(),
356
+ ]);
357
+ });
358
+ });
359
+
360
+ describe("sameAsContainer", () => {
361
+ test("uses the container CoValue's owner as the new CoValue's owner", async () => {
362
+ const TestMap = co.map({
363
+ name: co
364
+ .plainText()
365
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
366
+ });
367
+
368
+ const parentOwner = Group.create({ owner: me });
369
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
370
+ const childOwner = map.name.$jazz.owner;
371
+ expect(childOwner.$jazz.id).toEqual(parentOwner.$jazz.id);
372
+ });
373
+ });
374
+
375
+ describe("group configuration callback", () => {
376
+ test("creates a new group and configures it according to the callback", async () => {
377
+ const TestMap = co.map({
378
+ name: co.plainText().withPermissions({
379
+ onInlineCreate(newGroup) {
380
+ newGroup.addMember(anotherAccount, "writer");
381
+ },
382
+ }),
383
+ });
384
+
385
+ const parentOwner = Group.create({ owner: me });
386
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
387
+
388
+ const childOwner = map.name.$jazz.owner;
389
+ expect(childOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
390
+ "writer",
391
+ );
392
+ expect(
393
+ parentOwner.getRoleOf(anotherAccount.$jazz.id),
394
+ ).not.toBeDefined();
395
+ });
396
+
397
+ test("can access the container's owner inside the callback", async () => {
398
+ const TestMap = co.map({
399
+ name: co.plainText().withPermissions({
400
+ onInlineCreate(newGroup, { containerOwner }) {
401
+ containerOwner.addMember(anotherAccount, "writer");
402
+ newGroup.addMember(containerOwner);
403
+ },
404
+ }),
405
+ });
406
+
407
+ const parentOwner = Group.create({ owner: me });
408
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
409
+
410
+ const childOwner = map.name.$jazz.owner;
411
+ expect(childOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
412
+ "writer",
413
+ );
414
+ expect(parentOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
415
+ "writer",
416
+ );
417
+ });
418
+ });
419
+ });
420
+
421
+ test("defaults to 'extendsContainer'", () => {
422
+ const Schema = co.map({ name: co.plainText() });
423
+ expect(Schema.permissions.onInlineCreate).toEqual("extendsContainer");
424
+ });
425
+
426
+ test("is used when setting new properties on a CoMap", async () => {
427
+ const TestMap = co.map({
428
+ name: co
429
+ .plainText()
430
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
431
+ });
432
+
433
+ const parentOwner = Group.create({ owner: me });
434
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
435
+ map.$jazz.set("name", "Hi!");
436
+
437
+ const childOwner = map.name.$jazz.owner;
438
+ expect(childOwner.$jazz.id).toEqual(parentOwner.$jazz.id);
439
+ });
440
+
441
+ test("works on CoList container", async () => {
442
+ const TestList = co.list(
443
+ co.plainText().withPermissions({
444
+ onInlineCreate: "sameAsContainer",
445
+ }),
446
+ );
447
+
448
+ const parentOwner = Group.create({ owner: me });
449
+ parentOwner.addMember(anotherAccount, "writer");
450
+ const list = TestList.create(["Hello"], {
451
+ owner: parentOwner,
452
+ });
453
+
454
+ const childOwner = list[0]?.$jazz.owner;
455
+ expect(childOwner?.$jazz.id).toEqual(parentOwner.$jazz.id);
456
+ });
457
+
458
+ test("works on CoFeed container", async () => {
459
+ const TestFeed = co.feed(
460
+ co.plainText().withPermissions({
461
+ onInlineCreate: "sameAsContainer",
462
+ }),
463
+ );
464
+
465
+ const parentOwner = Group.create({ owner: me });
466
+ parentOwner.addMember(anotherAccount, "writer");
467
+ const feed = TestFeed.create(["Hello"], { owner: parentOwner });
468
+
469
+ const childCoValue = feed.inCurrentSession?.value;
470
+ assert(childCoValue);
471
+ assertLoaded(childCoValue);
472
+ const childOwner = childCoValue.$jazz.owner;
473
+ expect(childOwner?.$jazz.id).toEqual(parentOwner.$jazz.id);
474
+ });
475
+
476
+ test("works on Account container", async () => {
477
+ const TestAccount = co.account({
478
+ root: co
479
+ .map({
480
+ field: z.string(),
481
+ })
482
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
483
+ profile: co
484
+ .profile()
485
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
486
+ });
487
+ const account = await createJazzTestAccount({
488
+ AccountSchema: TestAccount,
489
+ });
490
+
491
+ account.$jazz.set("root", { field: "Test" });
492
+ account.$jazz.set("profile", { name: "Hermes Puggington" });
493
+
494
+ const rootOwner = account.root.$jazz.owner;
495
+ expect(rootOwner.$jazz.id).toEqual(account.$jazz.id);
496
+ const profileOwner = account.profile.$jazz.owner;
497
+ expect(profileOwner.$jazz.id).toEqual(account.$jazz.id);
498
+ });
499
+
500
+ describe("cannot be used in schemas that do not support inline creation", () => {
501
+ test("FileStream", () => {
502
+ co.fileStream().withPermissions({
503
+ // @ts-expect-error: FileStream does not support `onInlineCreate`
504
+ onInlineCreate: "sameAsContainer",
505
+ });
506
+ });
507
+ });
508
+
509
+ test("works on multiple nested inline CoValues", async () => {
510
+ const Dog = co
511
+ .map({
512
+ type: z.literal("dog"),
513
+ name: co
514
+ .plainText()
515
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
516
+ })
517
+ .withPermissions({ onInlineCreate: "sameAsContainer" });
518
+ const Person = co.map({
519
+ pets: co
520
+ .list(Dog)
521
+ .withPermissions({ onInlineCreate: "sameAsContainer" }),
522
+ });
523
+
524
+ const parentOwner = Group.create({ owner: me });
525
+ const person = Person.create(
526
+ {
527
+ pets: [{ type: "dog", name: "Rex" }],
528
+ },
529
+ { owner: parentOwner },
530
+ );
531
+
532
+ const petsOwner = person.pets.$jazz.owner;
533
+ expect(petsOwner.$jazz.id).toEqual(parentOwner.$jazz.id);
534
+ const dogOwner = person.pets[0]?.$jazz.owner;
535
+ expect(dogOwner?.$jazz.id).toEqual(parentOwner.$jazz.id);
536
+ const dogNameOwner = person.pets[0]?.name.$jazz.owner;
537
+ expect(dogNameOwner?.$jazz.id).toEqual(parentOwner.$jazz.id);
538
+ });
539
+
540
+ test("works when the field is optional", async () => {
541
+ const TestMap = co.map({
542
+ name: co
543
+ .plainText()
544
+ .withPermissions({ onInlineCreate: "sameAsContainer" })
545
+ .optional(),
546
+ });
547
+
548
+ const parentOwner = Group.create({ owner: me });
549
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
550
+
551
+ const childOwner = map.name?.$jazz.owner;
552
+ expect(childOwner?.$jazz.id).toEqual(parentOwner.$jazz.id);
553
+ });
554
+
555
+ test("works when the field is a discriminated union", async () => {
556
+ const Dog = co
557
+ .map({
558
+ type: z.literal("dog"),
559
+ name: z.string(),
560
+ })
561
+ .withPermissions({ onInlineCreate: "sameAsContainer" });
562
+ const Cat = co
563
+ .map({
564
+ type: z.literal("cat"),
565
+ name: z.string(),
566
+ })
567
+ .withPermissions({ onInlineCreate: "extendsContainer" });
568
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
569
+ const Person = co.map({
570
+ pet: Pet,
571
+ });
572
+
573
+ const parentOwner = Group.create({ owner: me });
574
+ const person = Person.create(
575
+ {
576
+ pet: { type: "dog", name: "Rex" },
577
+ },
578
+ { owner: parentOwner },
579
+ );
580
+
581
+ const dogOwner = person.pet.$jazz.owner;
582
+ expect(dogOwner.$jazz.id).toEqual(parentOwner.$jazz.id);
583
+
584
+ person.$jazz.set("pet", { type: "cat", name: "Whiskers" });
585
+ const catOwner = person.pet.$jazz.owner;
586
+ expect(
587
+ catOwner.getParentGroups().map((group) => group.$jazz.id),
588
+ ).toContain(parentOwner.$jazz.id);
589
+ });
590
+
591
+ test("works when the field is a nested discriminated union", async () => {
592
+ const Dog = co
593
+ .map({
594
+ type: z.literal("dog"),
595
+ name: z.string(),
596
+ })
597
+ .withPermissions({ onInlineCreate: "sameAsContainer" });
598
+ const Cat = co
599
+ .map({
600
+ type: z.literal("cat"),
601
+ name: z.string(),
602
+ })
603
+ .withPermissions({ onInlineCreate: "extendsContainer" });
604
+ const Bird = co
605
+ .map({
606
+ type: z.literal("bird"),
607
+ name: z.string(),
608
+ })
609
+ .withPermissions({ onInlineCreate: "newGroup" });
610
+ const Pet = co.discriminatedUnion("type", [
611
+ Dog,
612
+ co.discriminatedUnion("type", [Cat, Bird]),
613
+ ]);
614
+ const Person = co.map({
615
+ pet: Pet,
616
+ });
617
+
618
+ const parentOwner = Group.create({ owner: me });
619
+ const person = Person.create(
620
+ { pet: { type: "dog", name: "Rex" } },
621
+ { owner: parentOwner },
622
+ );
623
+
624
+ const dogOwner = person.pet.$jazz.owner;
625
+ expect(dogOwner.$jazz.id).toEqual(parentOwner.$jazz.id);
626
+
627
+ person.$jazz.set("pet", { type: "cat", name: "Whiskers" });
628
+ const catOwner = person.pet.$jazz.owner;
629
+ expect(
630
+ catOwner.getParentGroups().map((group) => group.$jazz.id),
631
+ ).toContain(parentOwner.$jazz.id);
632
+
633
+ person.$jazz.set("pet", { type: "bird", name: "Tweety" });
634
+ const birdOwner = person.pet.$jazz.owner;
635
+ expect(birdOwner.$jazz.id).not.toEqual(parentOwner.$jazz.id);
636
+ expect(birdOwner.members.map((member) => member.account)).toEqual([
637
+ co.account().getMe(),
638
+ ]);
639
+ });
640
+ });
641
+
642
+ describe("onCreate", () => {
643
+ test("is called when creating a CoValue with .create() without explicit owner", async () => {
644
+ let onCreateGroup: Group | undefined;
645
+
646
+ const TestMap = co.map({ name: co.plainText() }).withPermissions({
647
+ onCreate(newGroup) {
648
+ onCreateGroup = newGroup;
649
+ newGroup.addMember(anotherAccount, "writer");
650
+ },
651
+ });
652
+
653
+ const map = TestMap.create({ name: "Hello" });
654
+
655
+ expect(onCreateGroup).toBeDefined();
656
+ expect(onCreateGroup?.$jazz.id).toEqual(map.$jazz.owner.$jazz.id);
657
+ expect(map.$jazz.owner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
658
+ "writer",
659
+ );
660
+ });
661
+
662
+ test("is called when creating a CoValue with .create() with explicit owner", async () => {
663
+ let onCreateCalled = false;
664
+
665
+ const TestMap = co.map({ name: co.plainText() }).withPermissions({
666
+ onCreate() {
667
+ onCreateCalled = true;
668
+ },
669
+ });
670
+
671
+ const explicitOwner = Group.create();
672
+ const map = TestMap.create({ name: "Hello" }, { owner: explicitOwner });
673
+
674
+ expect(onCreateCalled).toBe(true);
675
+ expect(map.$jazz.owner.$jazz.id).toEqual(explicitOwner.$jazz.id);
676
+ });
677
+
678
+ test("is called when creating a CoValue inline", async () => {
679
+ let onCreateGroup: Group | undefined;
680
+
681
+ const TestMap = co.map({
682
+ name: co.plainText().withPermissions({
683
+ onCreate(newGroup) {
684
+ onCreateGroup = newGroup;
685
+ newGroup.addMember(anotherAccount, "reader");
686
+ },
687
+ }),
688
+ });
689
+
690
+ const parentOwner = Group.create({ owner: me });
691
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
692
+
693
+ expect(onCreateGroup).toBeDefined();
694
+ expect(onCreateGroup?.$jazz.id).toEqual(map.name.$jazz.owner.$jazz.id);
695
+ expect(map.name.$jazz.owner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
696
+ "reader",
697
+ );
698
+ });
699
+
700
+ test("works with onInlineCreate", async () => {
701
+ let onCreateCalled = false;
702
+
703
+ const TestMap = co.map({
704
+ name: co.plainText().withPermissions({
705
+ onCreate(newGroup) {
706
+ onCreateCalled = true;
707
+ newGroup.addMember(anotherAccount, "reader");
708
+ },
709
+ onInlineCreate: "extendsContainer",
710
+ }),
711
+ });
712
+
713
+ const parentOwner = Group.create({ owner: me });
714
+ const map = TestMap.create({ name: "Hello" }, { owner: parentOwner });
715
+
716
+ expect(onCreateCalled).toBe(true);
717
+ expect(map.name.$jazz.owner.getRoleOf(anotherAccount.$jazz.id)).toEqual(
718
+ "reader",
719
+ );
720
+ });
721
+
722
+ test("works when the field is optional", async () => {
723
+ let onCreateGroup: Group | undefined;
724
+ const TestMap = co.map({
725
+ name: co
726
+ .plainText()
727
+ .withPermissions({
728
+ onCreate(newGroup) {
729
+ onCreateGroup = newGroup;
730
+ },
731
+ })
732
+ .optional(),
733
+ });
734
+ const map = TestMap.create({ name: "Hello" });
735
+ expect(onCreateGroup?.$jazz.id).toEqual(map.name?.$jazz.owner.$jazz.id);
736
+ });
737
+
738
+ test("works when the field is a discriminated union", async () => {
739
+ let onCreateCalledOn = "";
740
+ const Dog = co
741
+ .map({
742
+ type: z.literal("dog"),
743
+ name: z.string(),
744
+ })
745
+ .withPermissions({
746
+ onCreate() {
747
+ onCreateCalledOn = "dog";
748
+ },
749
+ });
750
+ const Cat = co
751
+ .map({
752
+ type: z.literal("cat"),
753
+ name: z.string(),
754
+ })
755
+ .withPermissions({
756
+ onCreate() {
757
+ onCreateCalledOn = "cat";
758
+ },
759
+ });
760
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
761
+ const Person = co.map({
762
+ pet: Pet,
763
+ });
764
+
765
+ const person = Person.create({
766
+ pet: { type: "dog", name: "Rex" },
767
+ });
768
+ expect(onCreateCalledOn).toEqual("dog");
769
+
770
+ person.$jazz.set("pet", { type: "cat", name: "Whiskers" });
771
+ expect(onCreateCalledOn).toEqual("cat");
772
+ });
773
+ });
774
+
775
+ test("withPermissions() can be used with recursive schemas", () => {
776
+ const Person = co.map({
777
+ name: z.string(),
778
+ get friend(): co.List<typeof Person> {
779
+ return Friends;
780
+ },
781
+ });
782
+ const Friends = co.list(Person).withPermissions({
783
+ onInlineCreate: "sameAsContainer",
784
+ });
785
+ const person = Person.create({ name: "John", friend: [] });
786
+
787
+ expect(person.friend.$jazz.owner).toEqual(person.$jazz.owner);
788
+ });
789
+
790
+ test("withPermissions() does not override previous schema configuration", () => {
791
+ const TestMap = co.map({ name: co.plainText() }).resolved({ name: true });
792
+ const TestMapWithName = TestMap.withPermissions({
793
+ onInlineCreate: "extendsContainer",
794
+ });
795
+ expect(TestMapWithName.permissions).toEqual({
796
+ onInlineCreate: "extendsContainer",
797
+ });
798
+ });
799
+ });
800
+
801
+ describe("setDefaultSchemaPermissions", () => {
802
+ afterEach(() => {
803
+ setDefaultSchemaPermissions({
804
+ default: () => Group.create(),
805
+ onInlineCreate: "extendsContainer",
806
+ onCreate: undefined,
807
+ });
808
+ });
809
+
810
+ test("can set the default permissions for all schemas", () => {
811
+ setDefaultSchemaPermissions({
812
+ onInlineCreate: "sameAsContainer",
813
+ });
814
+
815
+ const TestMap = co.map({
816
+ name: co.plainText(),
817
+ });
818
+
819
+ const map = TestMap.create({ name: "Hello" });
820
+ expect(map.name.$jazz.owner.$jazz.id).toEqual(map.$jazz.owner.$jazz.id);
821
+ });
822
+
823
+ test("only overrides the provided options", async () => {
824
+ const anotherAccount = await createJazzTestAccount();
825
+ setDefaultSchemaPermissions({
826
+ onInlineCreate: "sameAsContainer",
827
+ });
828
+ setDefaultSchemaPermissions({
829
+ onCreate: (newGroup) => {
830
+ newGroup.addMember(anotherAccount, "reader");
831
+ },
832
+ });
833
+
834
+ const TestMap = co.map({
835
+ name: co.plainText(),
836
+ });
837
+ const map = TestMap.create({ name: "Hello" });
838
+ await map.$jazz.waitForSync();
839
+
840
+ const parentOwner = map.$jazz.owner;
841
+ const childOwner = map.name.$jazz.owner;
842
+ expect(parentOwner.$jazz.id).toEqual(childOwner.$jazz.id);
843
+ expect(childOwner.getRoleOf(anotherAccount.$jazz.id)).toEqual("reader");
844
+ });
845
+
846
+ test("does not modify permissions for existing schemas", () => {
847
+ const ExistingMap = co.map({
848
+ name: co.plainText(),
849
+ });
850
+ setDefaultSchemaPermissions({
851
+ onInlineCreate: "sameAsContainer",
852
+ });
853
+
854
+ const map = ExistingMap.create({ name: "Hello" });
855
+ expect(
856
+ map.name.$jazz.owner.getParentGroups().map((group) => group.$jazz.id),
857
+ ).toContain(map.$jazz.owner.$jazz.id);
858
+ });
859
+ });