jazz-tools 0.19.12 → 0.19.14

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 (119) hide show
  1. package/.turbo/turbo-build.log +44 -44
  2. package/CHANGELOG.md +18 -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-AGF4HEDH.js → chunk-GAPMDNJY.js} +437 -82
  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/react-native/index.js +41 -12
  20. package/dist/react-native/index.js.map +1 -1
  21. package/dist/react-native-core/ReactNativeSessionProvider.d.ts +11 -0
  22. package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -0
  23. package/dist/react-native-core/index.js +41 -12
  24. package/dist/react-native-core/index.js.map +1 -1
  25. package/dist/react-native-core/platform.d.ts +2 -8
  26. package/dist/react-native-core/platform.d.ts.map +1 -1
  27. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts +2 -0
  28. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts.map +1 -0
  29. package/dist/testing.js +4 -3
  30. package/dist/testing.js.map +1 -1
  31. package/dist/tools/coValues/account.d.ts.map +1 -1
  32. package/dist/tools/coValues/coFeed.d.ts +2 -2
  33. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  34. package/dist/tools/coValues/coList.d.ts +1 -2
  35. package/dist/tools/coValues/coList.d.ts.map +1 -1
  36. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  37. package/dist/tools/coValues/coVector.d.ts.map +1 -1
  38. package/dist/tools/coValues/group.d.ts +5 -1
  39. package/dist/tools/coValues/group.d.ts.map +1 -1
  40. package/dist/tools/coValues/interfaces.d.ts +2 -1
  41. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  42. package/dist/tools/exports.d.ts +2 -2
  43. package/dist/tools/exports.d.ts.map +1 -1
  44. package/dist/tools/implementation/createContext.d.ts +21 -11
  45. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  46. package/dist/tools/implementation/schema.d.ts +14 -6
  47. package/dist/tools/implementation/schema.d.ts.map +1 -1
  48. package/dist/tools/implementation/schemaUtils.d.ts +1 -1
  49. package/dist/tools/implementation/schemaUtils.d.ts.map +1 -1
  50. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  51. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts +99 -0
  52. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts.map +1 -0
  53. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +11 -0
  54. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  55. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +11 -0
  56. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  57. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +15 -1
  58. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  59. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +10 -0
  60. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
  61. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +9 -0
  62. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -1
  63. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +13 -1
  64. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  65. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +10 -0
  66. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
  67. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -0
  68. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
  69. package/dist/tools/implementation/zodSchema/unionUtils.d.ts +12 -1
  70. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  71. package/dist/tools/internal.d.ts +1 -0
  72. package/dist/tools/internal.d.ts.map +1 -1
  73. package/dist/tools/testing.d.ts.map +1 -1
  74. package/dist/tools/tests/schema.withPermissions.test.d.ts +2 -0
  75. package/dist/tools/tests/schema.withPermissions.test.d.ts.map +1 -0
  76. package/dist/worker/index.js +2 -2
  77. package/dist/worker/index.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/browser/createBrowserContext.ts +3 -62
  80. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +406 -0
  81. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +132 -0
  82. package/src/browser/provideBrowserLockSession/SessionIDStorage.ts +33 -0
  83. package/src/browser/provideBrowserLockSession/index.ts +11 -0
  84. package/src/react-native-core/ReactNativeSessionProvider.ts +52 -0
  85. package/src/react-native-core/platform.ts +5 -30
  86. package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +124 -0
  87. package/src/tools/coValues/account.ts +4 -0
  88. package/src/tools/coValues/coFeed.ts +8 -3
  89. package/src/tools/coValues/coList.ts +6 -3
  90. package/src/tools/coValues/coMap.ts +10 -0
  91. package/src/tools/coValues/coVector.ts +2 -1
  92. package/src/tools/coValues/group.ts +6 -4
  93. package/src/tools/coValues/interfaces.ts +19 -7
  94. package/src/tools/exports.ts +3 -1
  95. package/src/tools/implementation/createContext.ts +43 -15
  96. package/src/tools/implementation/schema.ts +23 -13
  97. package/src/tools/implementation/schemaUtils.ts +1 -1
  98. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +105 -4
  99. package/src/tools/implementation/zodSchema/schemaPermissions.ts +188 -0
  100. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +46 -3
  101. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +46 -3
  102. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +50 -13
  103. package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +14 -0
  104. package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +24 -1
  105. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +51 -4
  106. package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +25 -1
  107. package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +21 -1
  108. package/src/tools/implementation/zodSchema/unionUtils.ts +72 -20
  109. package/src/tools/internal.ts +1 -0
  110. package/src/tools/testing.ts +3 -1
  111. package/src/tools/tests/ContextManager.test.ts +2 -1
  112. package/src/tools/tests/coPlainText.test.ts +2 -2
  113. package/src/tools/tests/createContext.test.ts +79 -1
  114. package/src/tools/tests/deepLoading.test.ts +25 -2
  115. package/src/tools/tests/schema.resolved.test.ts +10 -0
  116. package/src/tools/tests/schema.withPermissions.test.ts +859 -0
  117. package/src/tools/tests/utils.ts +2 -2
  118. package/src/worker/index.ts +2 -2
  119. package/dist/chunk-AGF4HEDH.js.map +0 -1
@@ -28,10 +28,39 @@ export type Credentials = {
28
28
  secret: AgentSecret;
29
29
  };
30
30
 
31
- type SessionProvider = (
32
- accountID: ID<Account>,
33
- crypto: CryptoProvider,
34
- ) => Promise<{ sessionID: SessionID; sessionDone: () => void }>;
31
+ export interface SessionProvider {
32
+ acquireSession: (
33
+ accountID: ID<Account>,
34
+ crypto: CryptoProvider,
35
+ ) => Promise<{ sessionID: SessionID; sessionDone: () => void }>;
36
+ persistSession: (
37
+ accountID: ID<Account>,
38
+ sessionID: SessionID,
39
+ ) => Promise<{ sessionDone: () => void }>;
40
+ }
41
+
42
+ export class MockSessionProvider implements SessionProvider {
43
+ async acquireSession(
44
+ accountID: ID<Account>,
45
+ crypto: CryptoProvider,
46
+ ): Promise<{ sessionID: SessionID; sessionDone: () => void }> {
47
+ return {
48
+ sessionID: crypto.newRandomSessionID(
49
+ accountID as unknown as RawAccountID,
50
+ ),
51
+ sessionDone: () => {},
52
+ };
53
+ }
54
+
55
+ async persistSession(
56
+ _accountID: ID<Account>,
57
+ _sessionID: SessionID,
58
+ ): Promise<{ sessionDone: () => void }> {
59
+ return {
60
+ sessionDone: () => {},
61
+ };
62
+ }
63
+ }
35
64
 
36
65
  export type AuthResult =
37
66
  | {
@@ -57,16 +86,6 @@ export type AuthResult =
57
86
  logOut: () => Promise<void>;
58
87
  };
59
88
 
60
- export async function randomSessionProvider(
61
- accountID: ID<Account>,
62
- crypto: CryptoProvider,
63
- ) {
64
- return {
65
- sessionID: crypto.newRandomSessionID(accountID as unknown as RawAccountID),
66
- sessionDone: () => {},
67
- };
68
- }
69
-
70
89
  export type JazzContextWithAccount<Acc extends Account> = {
71
90
  node: LocalNode;
72
91
  account: Acc;
@@ -107,7 +126,7 @@ export async function createJazzContextFromExistingCredentials<
107
126
  storage?: StorageAPI;
108
127
  asActiveAccount: boolean;
109
128
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
110
- const { sessionID, sessionDone } = await sessionProvider(
129
+ const { sessionID, sessionDone } = await sessionProvider.acquireSession(
111
130
  credentials.accountID,
112
131
  crypto,
113
132
  );
@@ -167,6 +186,7 @@ export async function createJazzContextForNewAccount<
167
186
  AccountSchema: PropsAccountSchema,
168
187
  onLogOut,
169
188
  storage,
189
+ sessionProvider,
170
190
  }: {
171
191
  creationProps: { name: string };
172
192
  initialAgentSecret?: AgentSecret;
@@ -175,6 +195,7 @@ export async function createJazzContextForNewAccount<
175
195
  AccountSchema?: S;
176
196
  onLogOut?: () => Promise<void>;
177
197
  storage?: StorageAPI;
198
+ sessionProvider: SessionProvider;
178
199
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
179
200
  const CurrentAccountSchema =
180
201
  PropsAccountSchema ?? (RegisteredSchemas["Account"] as unknown as S);
@@ -199,11 +220,17 @@ export async function createJazzContextForNewAccount<
199
220
  const account = AccountClass.fromNode(node);
200
221
  activeAccountContext.set(account);
201
222
 
223
+ const { sessionDone } = await sessionProvider.persistSession(
224
+ account.$jazz.id,
225
+ node.currentSessionID,
226
+ );
227
+
202
228
  return {
203
229
  node,
204
230
  account: account as InstanceOfSchema<S>,
205
231
  done: () => {
206
232
  node.gracefulShutdown();
233
+ sessionDone();
207
234
  },
208
235
  logOut: async () => {
209
236
  node.gracefulShutdown();
@@ -270,6 +297,7 @@ export async function createJazzContext<
270
297
  peers: options.peers,
271
298
  crypto,
272
299
  AccountSchema: options.AccountSchema,
300
+ sessionProvider: options.sessionProvider,
273
301
  onLogOut: async () => {
274
302
  await authSecretStorage.clearWithoutNotify();
275
303
  },
@@ -5,9 +5,14 @@ import {
5
5
  type CoValue,
6
6
  type CoValueClass,
7
7
  CoValueFromRaw,
8
+ extendContainerOwner,
8
9
  Group,
10
+ type GroupRole,
9
11
  ItemsSym,
10
12
  LoadedAndRequired,
13
+ type NewInlineOwnerStrategy,
14
+ type RefOnCreateCallback,
15
+ type RefPermissions,
11
16
  SchemaInit,
12
17
  isCoValueClass,
13
18
  } from "../internal.js";
@@ -94,31 +99,33 @@ export const coField = {
94
99
 
95
100
  function optionalRef<C extends CoValueClass>(
96
101
  arg: C | ((raw: InstanceType<C>["$jazz"]["raw"]) => C),
102
+ options: { permissions: RefPermissions },
97
103
  ): InstanceType<C> | null | undefined {
98
- return ref(arg, { optional: true });
104
+ return ref(arg, { optional: true, permissions: options.permissions });
99
105
  }
100
106
 
101
107
  function ref<C extends CoValueClass>(
102
108
  arg: C | ((raw: InstanceType<C>["$jazz"]["raw"]) => C),
103
- options?: never,
109
+ options: { permissions?: RefPermissions },
104
110
  ): InstanceType<C> | null;
105
111
  function ref<C extends CoValueClass>(
106
112
  arg: C | ((raw: InstanceType<C>["$jazz"]["raw"]) => C),
107
- options: { optional: true },
113
+ options: { optional: true; permissions?: RefPermissions },
108
114
  ): InstanceType<C> | null | undefined;
109
115
  function ref<
110
116
  C extends CoValueClass,
111
- Options extends { optional?: boolean } | undefined,
117
+ Options extends { optional?: boolean; permissions?: RefPermissions },
112
118
  >(
113
119
  arg: C | ((raw: InstanceType<C>["$jazz"]["raw"]) => C),
114
- options?: Options,
120
+ options: Options,
115
121
  ): Options extends { optional: true }
116
122
  ? InstanceType<C> | null | undefined
117
123
  : InstanceType<C> | null {
118
124
  return {
119
125
  [SchemaInit]: {
120
126
  ref: arg,
121
- optional: options?.optional || false,
127
+ optional: options.optional || false,
128
+ permissions: options.permissions,
122
129
  } satisfies Schema,
123
130
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
131
  } as any;
@@ -129,6 +136,7 @@ export type EncodedAs<V> = { encoded: Encoder<V> | OptionalEncoder<V> };
129
136
  export type RefEncoded<V extends CoValue> = {
130
137
  ref: CoValueClass<V> | ((raw: RawCoValue) => CoValueClass<V>);
131
138
  optional: boolean;
139
+ permissions?: RefPermissions;
132
140
  };
133
141
 
134
142
  export function isRefEncoded<V extends CoValue>(
@@ -158,24 +166,26 @@ export function instantiateRefEncodedFromRaw<V extends CoValue>(
158
166
  *
159
167
  * @param schema - The schema of the CoValue to create.
160
168
  * @param init - The init values to use to create the CoValue.
161
- * @param parentOwner - The owner of the referencing CoValue. Will be used
162
- * as the parent group of the created CoValue's group
169
+ * @param containerOwner - The owner of the referencing CoValue. Will be used
170
+ * to determine the owner of the new CoValue
171
+ * @param newOwnerStrategy - The strategy to use to determine the owner of the new CoValue
172
+ * @param onCreate - The callback to call when the new CoValue is created
163
173
  * @returns The created CoValue.
164
174
  */
165
175
  export function instantiateRefEncodedWithInit<V extends CoValue>(
166
176
  schema: RefEncoded<V>,
167
177
  init: any,
168
- parentOwner: Group,
178
+ containerOwner: Group,
179
+ newOwnerStrategy: NewInlineOwnerStrategy = extendContainerOwner,
180
+ onCreate?: RefOnCreateCallback,
169
181
  ): V {
170
182
  if (!isCoValueClass<V>(schema.ref)) {
171
183
  throw Error(
172
184
  `Cannot automatically create CoValue from value: ${JSON.stringify(init)}. Use the CoValue schema's create() method instead.`,
173
185
  );
174
186
  }
175
- const node = parentOwner.$jazz.localNode;
176
- const rawGroup = node.createGroup();
177
- const owner = new Group({ fromRaw: rawGroup });
178
- owner.addMember(parentOwner);
187
+ const owner = newOwnerStrategy(() => Group.create(), containerOwner, init);
188
+ onCreate?.(owner, init);
179
189
  // @ts-expect-error - create is a static method in all CoValue classes
180
190
  return schema.ref.create(init, owner);
181
191
  }
@@ -1,4 +1,4 @@
1
- import { CoreResolveQuery } from "./zodSchema/schemaTypes/CoValueSchema";
1
+ import { CoreResolveQuery } from "./zodSchema/schemaTypes/CoValueSchema.js";
2
2
 
3
3
  /**
4
4
  * Remove getters from an object
@@ -1,8 +1,24 @@
1
1
  import type { JsonValue } from "cojson";
2
- import { CoValueClass, isCoValueClass } from "../../../internal.js";
2
+ import {
3
+ CoValueClass,
4
+ isCoValueClass,
5
+ schemaToRefPermissions,
6
+ getDefaultRefPermissions,
7
+ SchemaPermissions,
8
+ RefPermissions,
9
+ type NewInlineOwnerStrategy,
10
+ type CoreCoDiscriminatedUnionSchema,
11
+ type DiscriminableCoValueSchemas,
12
+ type RefOnCreateCallback,
13
+ } from "../../../internal.js";
3
14
  import { coField } from "../../schema.js";
4
15
  import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
5
- import { isUnionOfPrimitivesDeeply } from "../unionUtils.js";
16
+ import {
17
+ isUnionOfPrimitivesDeeply,
18
+ getFlattenedUnionOptions,
19
+ getDiscriminatorValuesForOption,
20
+ resolveDiscriminantValue,
21
+ } from "../unionUtils.js";
6
22
  import {
7
23
  ZodCatch,
8
24
  ZodDefault,
@@ -76,17 +92,28 @@ export function schemaFieldToCoFieldDef(schema: SchemaField): CoFieldDef {
76
92
  }
77
93
 
78
94
  if (isCoValueClass(schema)) {
79
- return cacheSchemaField(schema, coField.ref(schema));
95
+ return cacheSchemaField(
96
+ schema,
97
+ coField.ref(schema, {
98
+ permissions: getDefaultRefPermissions(),
99
+ }),
100
+ );
80
101
  } else if (isCoValueSchema(schema)) {
81
102
  if (schema.builtin === "CoOptional") {
82
103
  return cacheSchemaField(
83
104
  schema,
84
105
  coField.ref(schema.getCoValueClass(), {
85
106
  optional: true,
107
+ permissions: schemaFieldPermissions(schema),
86
108
  }),
87
109
  );
88
110
  }
89
- return cacheSchemaField(schema, coField.ref(schema.getCoValueClass()));
111
+ return cacheSchemaField(
112
+ schema,
113
+ coField.ref(schema.getCoValueClass(), {
114
+ permissions: schemaFieldPermissions(schema),
115
+ }),
116
+ );
90
117
  } else {
91
118
  if ("_zod" in schema) {
92
119
  const zodSchemaDef = schema._zod.def;
@@ -228,3 +255,77 @@ export function schemaFieldToCoFieldDef(schema: SchemaField): CoFieldDef {
228
255
  }
229
256
  }
230
257
  }
258
+
259
+ function schemaFieldPermissions(schema: CoreCoValueSchema): RefPermissions {
260
+ if (schema.builtin === "CoOptional") {
261
+ return schemaFieldPermissions((schema as any).innerType);
262
+ }
263
+ if (schema.builtin === "CoDiscriminatedUnion") {
264
+ return discriminatedUnionFieldPermissions(
265
+ schema as CoreCoDiscriminatedUnionSchema<DiscriminableCoValueSchemas>,
266
+ );
267
+ }
268
+ return "permissions" in schema
269
+ ? schemaToRefPermissions(schema.permissions as SchemaPermissions)
270
+ : getDefaultRefPermissions();
271
+ }
272
+
273
+ function discriminatedUnionFieldPermissions(
274
+ schema: CoreCoDiscriminatedUnionSchema<DiscriminableCoValueSchemas>,
275
+ ): RefPermissions {
276
+ const discriminatorKey = schema.getDefinition().discriminator;
277
+ const allOptions = getFlattenedUnionOptions(schema);
278
+
279
+ const valueToStrategy = new Map<unknown, RefPermissions>();
280
+ for (const option of allOptions) {
281
+ const optionPermissions = schemaFieldPermissions(option);
282
+ const discriminatorValues = getDiscriminatorValuesForOption(
283
+ option,
284
+ discriminatorKey,
285
+ );
286
+
287
+ if (!discriminatorValues) {
288
+ continue;
289
+ }
290
+
291
+ for (const value of discriminatorValues) {
292
+ if (!valueToStrategy.has(value)) {
293
+ valueToStrategy.set(value, optionPermissions);
294
+ }
295
+ }
296
+ }
297
+
298
+ const fallbackStrategy = getDefaultRefPermissions();
299
+
300
+ const newInlineOwnerStrategy: NewInlineOwnerStrategy = (
301
+ createNewGroup,
302
+ containerOwner,
303
+ init,
304
+ ) => {
305
+ const discriminantValue = resolveDiscriminantValue(init, discriminatorKey);
306
+ const strategy =
307
+ discriminantValue !== undefined
308
+ ? valueToStrategy.get(discriminantValue)
309
+ : undefined;
310
+
311
+ const effectiveStrategy = strategy ?? fallbackStrategy;
312
+ return effectiveStrategy.newInlineOwnerStrategy(
313
+ createNewGroup,
314
+ containerOwner,
315
+ init,
316
+ );
317
+ };
318
+
319
+ const onCreate: RefOnCreateCallback = (newGroup, init) => {
320
+ const discriminantValue = resolveDiscriminantValue(init, discriminatorKey);
321
+ const strategy =
322
+ discriminantValue !== undefined
323
+ ? valueToStrategy.get(discriminantValue)
324
+ : undefined;
325
+
326
+ const effectiveStrategy = strategy ?? fallbackStrategy;
327
+ effectiveStrategy.onCreate?.(newGroup, init);
328
+ };
329
+
330
+ return { newInlineOwnerStrategy, onCreate };
331
+ }
@@ -0,0 +1,188 @@
1
+ import { Account, Group, TypeSym, type GroupRole } from "../../internal.js";
2
+
3
+ /**
4
+ * Defines how a nested CoValue’s owner is obtained when creating CoValues from JSON.
5
+ *
6
+ * This configuration is not used when using an explicit .create() for nested CoValues.
7
+ * In that case, {@link SchemaPermissions.default} is used.
8
+ */
9
+ export type OnInlineCreateOptions =
10
+ /**
11
+ * Always create a new group for CoValues created inline
12
+ */
13
+ | "newGroup"
14
+ /**
15
+ * Use the same owner as the container CoValue
16
+ */
17
+ | "sameAsContainer"
18
+ /**
19
+ * Create a new group that includes the container CoValue's owner as a member (effectively inheriting
20
+ * all permissions from the container)
21
+ */
22
+ | "extendsContainer"
23
+ /**
24
+ * Similar to "extendsContainer", but allows overriding the role of the container CoValue's owner
25
+ */
26
+ | { extendsContainer: GroupRole }
27
+ /**
28
+ * Create a new group and configure it as needed
29
+ */
30
+ | InlineGroupConfigurationCallback;
31
+
32
+ export type InlineGroupConfigurationCallback = (
33
+ newGroup: Group,
34
+ context: { containerOwner: Group },
35
+ ) => void;
36
+
37
+ export type OnCreateCallback = (newGroup: Group) => void;
38
+
39
+ /**
40
+ * Internal callback type used by RefPermissions that includes init for discriminated union support.
41
+ * @internal
42
+ */
43
+ export type RefOnCreateCallback = (newGroup: Group, init?: unknown) => void;
44
+
45
+ /**
46
+ * Permissions to be used when creating or composing CoValues
47
+ * @param default - default owner to be used when creating a CoValue without providing an explicit owner.
48
+ * @param onInlineCreate - defines how a nested CoValue's owner is obtained when creating CoValues from JSON.
49
+ * @param onCreate - callback that runs every time a CoValue is created. Can be used to configure the CoValue's owner.
50
+ * Runs both when creating CoValues with `.create()` and when creating CoValues from JSON.
51
+ * @default { default: () => Group.create(), onInlineCreate: "extendsContainer" }
52
+ */
53
+ export type SchemaPermissions = {
54
+ /**
55
+ * default owner to be used when creating a CoValue without providing an explicit owner.
56
+ */
57
+ default?: () => Group;
58
+ /**
59
+ * Defines how a nested CoValue's owner is obtained when creating CoValues from JSON.
60
+ */
61
+ onInlineCreate?: OnInlineCreateOptions;
62
+ /**
63
+ * callback that runs every time a CoValue is created. Can be used to configure the CoValue's owner.
64
+ * Runs both when creating CoValues with `.create()` and when creating CoValues from JSON.
65
+ */
66
+ onCreate?: OnCreateCallback;
67
+ };
68
+
69
+ export let DEFAULT_SCHEMA_PERMISSIONS: SchemaPermissions = {
70
+ default: () => Group.create(),
71
+ onInlineCreate: "extendsContainer",
72
+ };
73
+
74
+ /**
75
+ * Update the default schema permissions for all new CoValue schemas.
76
+ * Schemas created before calling this function will not be affected.
77
+ */
78
+ export function setDefaultSchemaPermissions(permissions: SchemaPermissions) {
79
+ DEFAULT_SCHEMA_PERMISSIONS = {
80
+ ...DEFAULT_SCHEMA_PERMISSIONS,
81
+ ...permissions,
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Parsed {@link SchemaPermissions}, used by CoValue classes to set up permissions for referenced CoValues.
87
+ */
88
+ export type RefPermissions = {
89
+ newInlineOwnerStrategy: NewInlineOwnerStrategy;
90
+ onCreate?: RefOnCreateCallback;
91
+ };
92
+
93
+ /**
94
+ * A function that creates a new owner for a new CoValue created inline.
95
+ * @param createNewGroup - A function that creates a new group.
96
+ * @param containerOwner - The owner of the container CoValue.
97
+ * @param init - The value used to create the new CoValue. Necessary to determine the concrete
98
+ * strategy to use in discriminated unions.
99
+ * @returns The new owner.
100
+ */
101
+ export type NewInlineOwnerStrategy = (
102
+ createNewGroup: () => Group,
103
+ containerOwner: Group,
104
+ init?: unknown,
105
+ ) => Group;
106
+
107
+ export const extendContainerOwnerFactory =
108
+ (roleOverride?: GroupRole): NewInlineOwnerStrategy =>
109
+ (createNewGroup: () => Group, containerOwner: Group): Group => {
110
+ const node = containerOwner.$jazz.localNode;
111
+ const rawGroup = node.createGroup();
112
+ const owner = new Group({ fromRaw: rawGroup });
113
+ owner.addMember(containerOwner, roleOverride);
114
+ return owner;
115
+ };
116
+
117
+ /**
118
+ * A function that creates a new owner for a new CoValue by extending the container CoValue's owner
119
+ * (without overriding its role)
120
+ */
121
+ export const extendContainerOwner = extendContainerOwnerFactory();
122
+
123
+ export function schemaToRefPermissions(
124
+ permissions: SchemaPermissions,
125
+ ): RefPermissions {
126
+ const newInlineOwnerStrategy = parseOnInlineCreate(
127
+ permissions.onInlineCreate,
128
+ );
129
+ const onCreate: RefOnCreateCallback | undefined = permissions.onCreate
130
+ ? (newGroup, _init) => permissions.onCreate?.(newGroup)
131
+ : undefined;
132
+ return {
133
+ newInlineOwnerStrategy,
134
+ onCreate,
135
+ };
136
+ }
137
+
138
+ function parseOnInlineCreate(
139
+ onInlineCreate?: OnInlineCreateOptions,
140
+ ): NewInlineOwnerStrategy {
141
+ if (!onInlineCreate || onInlineCreate === "extendsContainer") {
142
+ return extendContainerOwner;
143
+ }
144
+ if (
145
+ typeof onInlineCreate === "object" &&
146
+ "extendsContainer" in onInlineCreate
147
+ ) {
148
+ return extendContainerOwnerFactory(onInlineCreate.extendsContainer);
149
+ }
150
+ if (onInlineCreate === "newGroup") {
151
+ return (createNewGroup) => createNewGroup();
152
+ }
153
+ if (onInlineCreate === "sameAsContainer") {
154
+ return (_createNewGroup, containerOwner) => containerOwner;
155
+ }
156
+ return (createNewGroup, containerOwner) => {
157
+ const newGroup = createNewGroup();
158
+ onInlineCreate(newGroup, { containerOwner });
159
+ return newGroup;
160
+ };
161
+ }
162
+
163
+ export function getDefaultRefPermissions(): RefPermissions {
164
+ return schemaToRefPermissions(DEFAULT_SCHEMA_PERMISSIONS);
165
+ }
166
+
167
+ export function withSchemaPermissions<T extends { owner?: Account | Group }>(
168
+ options?: T | Account | Group,
169
+ schemaPermissions?: SchemaPermissions,
170
+ ): T & { onCreate?: OnCreateCallback } {
171
+ const onCreate = schemaPermissions?.onCreate;
172
+ if (!options) {
173
+ const owner = schemaPermissions?.default?.() ?? Group.create();
174
+ return { owner, onCreate } as T & { onCreate?: OnCreateCallback };
175
+ }
176
+ if (TypeSym in options) {
177
+ return { owner: options, onCreate } as T & {
178
+ onCreate?: OnCreateCallback;
179
+ };
180
+ }
181
+ const owner =
182
+ options.owner ?? schemaPermissions?.default?.() ?? Group.create();
183
+ return {
184
+ ...options,
185
+ owner,
186
+ onCreate,
187
+ } as T & { onCreate?: OnCreateCallback };
188
+ }
@@ -4,6 +4,7 @@ import {
4
4
  BranchDefinition,
5
5
  CoFeed,
6
6
  Group,
7
+ hydrateCoreCoValueSchema,
7
8
  Settled,
8
9
  RefsToResolve,
9
10
  RefsToResolveStrict,
@@ -12,6 +13,7 @@ import {
12
13
  coOptionalDefiner,
13
14
  parseSubscribeRestArgs,
14
15
  unstable_mergeBranchWithResolve,
16
+ withSchemaPermissions,
15
17
  } from "../../../internal.js";
16
18
  import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
17
19
  import { CoFeedSchemaInit } from "../typeConverters/CoFieldSchemaInit.js";
@@ -20,6 +22,10 @@ import { InstanceOrPrimitiveOfSchemaCoValuesMaybeLoaded } from "../typeConverter
20
22
  import { CoOptionalSchema } from "./CoOptionalSchema.js";
21
23
  import { CoreCoValueSchema, CoreResolveQuery } from "./CoValueSchema.js";
22
24
  import { withSchemaResolveQuery } from "../../schemaUtils.js";
25
+ import {
26
+ DEFAULT_SCHEMA_PERMISSIONS,
27
+ SchemaPermissions,
28
+ } from "../schemaPermissions.js";
23
29
 
24
30
  export class CoFeedSchema<
25
31
  T extends AnyZodOrCoValueSchema,
@@ -36,6 +42,12 @@ export class CoFeedSchema<
36
42
  */
37
43
  resolveQuery: DefaultResolveQuery = true as DefaultResolveQuery;
38
44
 
45
+ /**
46
+ * Permissions to be used when creating or composing CoValues
47
+ * @internal
48
+ */
49
+ permissions: SchemaPermissions = DEFAULT_SCHEMA_PERMISSIONS;
50
+
39
51
  constructor(
40
52
  public element: T,
41
53
  private coValueClass: typeof CoFeed,
@@ -54,7 +66,14 @@ export class CoFeedSchema<
54
66
  init: CoFeedSchemaInit<T>,
55
67
  options?: { owner: Account | Group } | Account | Group,
56
68
  ): CoFeedInstance<T> {
57
- return this.coValueClass.create(init as any, options) as CoFeedInstance<T>;
69
+ const optionsWithPermissions = withSchemaPermissions(
70
+ options,
71
+ this.permissions,
72
+ );
73
+ return this.coValueClass.create(
74
+ init as any,
75
+ optionsWithPermissions,
76
+ ) as CoFeedInstance<T>;
58
77
  }
59
78
 
60
79
  load<
@@ -146,8 +165,32 @@ export class CoFeedSchema<
146
165
  >(
147
166
  resolveQuery: RefsToResolveStrict<CoFeedInstanceCoValuesMaybeLoaded<T>, R>,
148
167
  ): CoFeedSchema<T, R> {
149
- const copy = new CoFeedSchema<T, R>(this.element, this.coValueClass);
150
- copy.resolveQuery = resolveQuery as R;
168
+ return this.copy({ resolveQuery: resolveQuery as R });
169
+ }
170
+
171
+ /**
172
+ * Configure permissions to be used when creating or composing CoValues
173
+ */
174
+ withPermissions(
175
+ permissions: SchemaPermissions,
176
+ ): CoFeedSchema<T, DefaultResolveQuery> {
177
+ return this.copy({ permissions });
178
+ }
179
+
180
+ private copy<ResolveQuery extends CoreResolveQuery = DefaultResolveQuery>({
181
+ permissions,
182
+ resolveQuery,
183
+ }: {
184
+ permissions?: SchemaPermissions;
185
+ resolveQuery?: ResolveQuery;
186
+ }): CoFeedSchema<T, ResolveQuery> {
187
+ const coreSchema = createCoreCoFeedSchema(this.element);
188
+ // @ts-expect-error
189
+ const copy: CoFeedSchema<T, ResolveQuery> =
190
+ hydrateCoreCoValueSchema(coreSchema);
191
+ // @ts-expect-error TS cannot infer that the resolveQuery type is valid
192
+ copy.resolveQuery = resolveQuery ?? this.resolveQuery;
193
+ copy.permissions = permissions ?? this.permissions;
151
194
  return copy;
152
195
  }
153
196
  }