jazz-tools 0.19.13 → 0.19.15

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.
@@ -29,7 +29,6 @@ import {
29
29
  } from "./errorReporting.js";
30
30
  import {
31
31
  createCoValue,
32
- isEqualRefsToResolve,
33
32
  myRoleForRawValue,
34
33
  PromiseWithStatus,
35
34
  rejectedPromise,
@@ -49,25 +48,25 @@ export class SubscriptionScope<D extends CoValue> {
49
48
  /**
50
49
  * Autoloaded child ids that are unloaded
51
50
  */
52
- pendingAutoloadedChildren: Set<string> = new Set();
51
+ private pendingAutoloadedChildren: Set<string> = new Set();
53
52
  value: SubscriptionValue<D, any> | SubscriptionValueLoading;
54
- childErrors: Map<string, JazzError> = new Map();
55
- validationErrors: Map<string, JazzError> = new Map();
53
+ private childErrors: Map<string, JazzError> = new Map();
54
+ private validationErrors: Map<string, JazzError> = new Map();
56
55
  errorFromChildren: JazzError | undefined;
57
- subscription: CoValueCoreSubscription;
58
- dirty = false;
59
- resolve: RefsToResolve<any>;
60
- idsSubscribed = new Set<string>();
61
- autoloaded = new Set<string>();
62
- autoloadedKeys = new Set<string>();
63
- skipInvalidKeys = new Set<string>();
64
- totalValidTransactions = 0;
65
- version = 0;
66
- migrated = false;
67
- migrating = false;
56
+ private subscription: CoValueCoreSubscription;
57
+ private dirty = false;
58
+ private resolve: RefsToResolve<any>;
59
+ private idsSubscribed = new Set<string>();
60
+ private autoloaded = new Set<string>();
61
+ private autoloadedKeys = new Set<string>();
62
+ private skipInvalidKeys = new Set<string>();
63
+ private totalValidTransactions = 0;
64
+ private version = 0;
65
+ private migrated = false;
66
+ private migrating = false;
68
67
  closed = false;
69
68
 
70
- silenceUpdates = false;
69
+ private silenceUpdates = false;
71
70
 
72
71
  /**
73
72
  * Stack trace captured at subscription creation time.
@@ -145,7 +144,9 @@ export class SubscriptionScope<D extends CoValue> {
145
144
  this.dirty = true;
146
145
  }
147
146
 
148
- handleUpdate(update: RawCoValue | typeof CoValueLoadingState.UNAVAILABLE) {
147
+ private handleUpdate(
148
+ update: RawCoValue | typeof CoValueLoadingState.UNAVAILABLE,
149
+ ) {
149
150
  if (update === CoValueLoadingState.UNAVAILABLE) {
150
151
  if (this.value.type === CoValueLoadingState.LOADING) {
151
152
  const error = new JazzError(this.id, CoValueLoadingState.UNAVAILABLE, [
@@ -213,7 +214,7 @@ export class SubscriptionScope<D extends CoValue> {
213
214
  this.triggerUpdate();
214
215
  }
215
216
 
216
- computeChildErrors() {
217
+ private computeChildErrors() {
217
218
  let issues: JazzErrorIssue[] = [];
218
219
  let errorType: JazzError["type"] = CoValueLoadingState.UNAVAILABLE;
219
220
 
@@ -259,11 +260,11 @@ export class SubscriptionScope<D extends CoValue> {
259
260
  return undefined;
260
261
  }
261
262
 
262
- handleChildUpdate = (
263
+ handleChildUpdate(
263
264
  id: string,
264
265
  value: SubscriptionValue<any, any> | SubscriptionValueLoading,
265
266
  key?: string,
266
- ) => {
267
+ ) {
267
268
  if (value.type === CoValueLoadingState.LOADING) {
268
269
  return;
269
270
  }
@@ -296,9 +297,9 @@ export class SubscriptionScope<D extends CoValue> {
296
297
  }
297
298
 
298
299
  this.triggerUpdate();
299
- };
300
+ }
300
301
 
301
- shouldSendUpdates() {
302
+ private shouldSendUpdates() {
302
303
  if (this.value.type === CoValueLoadingState.LOADING) return false;
303
304
 
304
305
  // If the value is in error, we send the update regardless of the children statuses
@@ -309,9 +310,9 @@ export class SubscriptionScope<D extends CoValue> {
309
310
 
310
311
  unloadedValue: NotLoaded<D> | undefined;
311
312
 
312
- lastPromise: PromiseWithStatus<D> | undefined;
313
+ private lastPromise: PromiseWithStatus<D> | undefined;
313
314
 
314
- getPromise() {
315
+ private getPromise() {
315
316
  const currentValue = this.getCurrentValue();
316
317
 
317
318
  if (currentValue.$isLoaded) {
@@ -403,7 +404,7 @@ export class SubscriptionScope<D extends CoValue> {
403
404
  return unloadedValue;
404
405
  }
405
406
 
406
- lastErrorLogged: JazzError | undefined;
407
+ private lastErrorLogged: JazzError | undefined;
407
408
 
408
409
  getCurrentValue(): MaybeLoaded<D> {
409
410
  const rawValue = this.getCurrentRawValue();
@@ -420,7 +421,7 @@ export class SubscriptionScope<D extends CoValue> {
420
421
  return rawValue;
421
422
  }
422
423
 
423
- getCurrentRawValue(): D | NotLoadedCoValueState {
424
+ private getCurrentRawValue(): D | NotLoadedCoValueState {
424
425
  if (
425
426
  this.value.type === CoValueLoadingState.UNAUTHORIZED ||
426
427
  this.value.type === CoValueLoadingState.UNAVAILABLE
@@ -443,7 +444,7 @@ export class SubscriptionScope<D extends CoValue> {
443
444
  return CoValueLoadingState.LOADING;
444
445
  }
445
446
 
446
- getCreationStackLines() {
447
+ private getCreationStackLines() {
447
448
  const stack = this.callerStack?.stack;
448
449
 
449
450
  if (!stack) {
@@ -474,7 +475,7 @@ export class SubscriptionScope<D extends CoValue> {
474
475
  return result;
475
476
  }
476
477
 
477
- getError() {
478
+ private getError() {
478
479
  if (
479
480
  this.value.type === CoValueLoadingState.UNAUTHORIZED ||
480
481
  this.value.type === CoValueLoadingState.UNAVAILABLE
@@ -487,7 +488,7 @@ export class SubscriptionScope<D extends CoValue> {
487
488
  }
488
489
  }
489
490
 
490
- logError() {
491
+ private logError() {
491
492
  const error = this.getError();
492
493
 
493
494
  if (!error || this.lastErrorLogged === error) {
@@ -510,7 +511,7 @@ export class SubscriptionScope<D extends CoValue> {
510
511
  }
511
512
  }
512
513
 
513
- triggerUpdate() {
514
+ private triggerUpdate() {
514
515
  if (!this.shouldSendUpdates()) return;
515
516
  if (!this.dirty) return;
516
517
  if (this.subscribers.size === 0) return;
@@ -700,7 +701,7 @@ export class SubscriptionScope<D extends CoValue> {
700
701
  this.silenceUpdates = false;
701
702
  }
702
703
 
703
- loadChildren() {
704
+ private loadChildren() {
704
705
  const { resolve } = this;
705
706
 
706
707
  if (this.value.type !== CoValueLoadingState.LOADED) {
@@ -816,7 +817,11 @@ export class SubscriptionScope<D extends CoValue> {
816
817
  return hasChanged;
817
818
  }
818
819
 
819
- loadCoMapKey(map: CoMap, key: string, depth: Record<string, any> | true) {
820
+ private loadCoMapKey(
821
+ map: CoMap,
822
+ key: string,
823
+ depth: Record<string, any> | true,
824
+ ) {
820
825
  if (key === "$onError") {
821
826
  return undefined;
822
827
  }
@@ -858,7 +863,11 @@ export class SubscriptionScope<D extends CoValue> {
858
863
  return undefined;
859
864
  }
860
865
 
861
- loadCoListKey(list: CoList, key: string, depth: Record<string, any> | true) {
866
+ private loadCoListKey(
867
+ list: CoList,
868
+ key: string,
869
+ depth: Record<string, any> | true,
870
+ ) {
862
871
  const descriptor = list.$jazz.getItemsDescriptor();
863
872
 
864
873
  if (!descriptor || !isRefEncoded(descriptor)) {
@@ -896,7 +905,7 @@ export class SubscriptionScope<D extends CoValue> {
896
905
  return undefined;
897
906
  }
898
907
 
899
- loadChildNode(
908
+ private loadChildNode(
900
909
  id: string,
901
910
  query: RefsToResolve<any>,
902
911
  descriptor: RefEncoded<any>,
@@ -506,4 +506,23 @@ describe("createAs", () => {
506
506
  // Verify execution order
507
507
  expect(executionOrder).toEqual(["migration", "onCreate"]);
508
508
  });
509
+ test("createdBy returns undefined", async () => {
510
+ const CustomAccount = co.account({
511
+ profile: co.profile({
512
+ name: z.string(),
513
+ }),
514
+ root: co.map({}),
515
+ });
516
+
517
+ const worker = await createJazzTestAccount({
518
+ isCurrentActiveAccount: true,
519
+ });
520
+
521
+ const createdAccount = await CustomAccount.createAs(worker, {
522
+ creationProps: { name: "Test Account" },
523
+ });
524
+
525
+ assertLoaded(createdAccount.account);
526
+ expect(createdAccount.account.$jazz.createdBy).toBe(undefined);
527
+ });
509
528
  });
@@ -194,6 +194,49 @@ describe("CoMap.Record", async () => {
194
194
  $jazz: expect.objectContaining({ id: me.$jazz.id }),
195
195
  });
196
196
  });
197
+
198
+ test("getEdits() keys should return deleted keys", () => {
199
+ const me = Account.getMe();
200
+
201
+ const Person = co.record(z.string(), z.string());
202
+ const person = Person.create({ name: "John" });
203
+ person.$jazz.set("name", "Jane");
204
+ person.$jazz.delete("name");
205
+
206
+ expect(Object.keys(person.$jazz.getEdits())).toEqual(["name"]);
207
+
208
+ const edits = person.$jazz.getEdits().name?.all;
209
+
210
+ expect(edits).toEqual([
211
+ expect.objectContaining({
212
+ value: "John",
213
+ key: "name",
214
+ ref: undefined,
215
+ madeAt: expect.any(Date),
216
+ }),
217
+ expect.objectContaining({
218
+ value: "Jane",
219
+ key: "name",
220
+ ref: undefined,
221
+ madeAt: expect.any(Date),
222
+ }),
223
+ expect.objectContaining({
224
+ value: undefined,
225
+ key: "name",
226
+ ref: undefined,
227
+ madeAt: expect.any(Date),
228
+ }),
229
+ ]);
230
+
231
+ expect(edits?.[0]?.by).toMatchObject({
232
+ [TypeSym]: "Account",
233
+ $jazz: expect.objectContaining({ id: me.$jazz.id }),
234
+ });
235
+ expect(edits?.[1]?.by).toMatchObject({
236
+ [TypeSym]: "Account",
237
+ $jazz: expect.objectContaining({ id: me.$jazz.id }),
238
+ });
239
+ });
197
240
  });
198
241
 
199
242
  describe("Record resolution", async () => {
@@ -26,6 +26,7 @@ import {
26
26
  disableJazzTestSync,
27
27
  getPeerConnectedToTestSyncServer,
28
28
  runWithoutActiveAccount,
29
+ setActiveAccount,
29
30
  setupJazzTestSync,
30
31
  } from "../testing.js";
31
32
  import { assertLoaded, setupTwoNodes, waitFor } from "./utils.js";
@@ -2316,28 +2317,93 @@ describe("CoMap migration", () => {
2316
2317
  });
2317
2318
  });
2318
2319
 
2319
- describe("createdAt & lastUpdatedAt", () => {
2320
+ describe("createdAt, lastUpdatedAt, createdBy", () => {
2320
2321
  test("empty map created time", () => {
2321
2322
  const emptyMap = co.map({}).create({});
2322
2323
 
2323
2324
  expect(emptyMap.$jazz.lastUpdatedAt).toEqual(emptyMap.$jazz.createdAt);
2324
2325
  });
2325
2326
 
2327
+ test("empty map created by", () => {
2328
+ const emptyMap = co.map({}).create({});
2329
+ const me = Account.getMe();
2330
+ expect(emptyMap.$jazz.createdBy).toEqual(me.$jazz.id);
2331
+ });
2332
+
2326
2333
  test("created time and last updated time", async () => {
2327
2334
  const Person = co.map({
2328
2335
  name: z.string(),
2329
2336
  });
2337
+ const me = Account.getMe();
2330
2338
 
2331
2339
  const person = Person.create({ name: "John" });
2332
2340
 
2333
2341
  const createdAt = person.$jazz.createdAt;
2334
2342
  expect(person.$jazz.lastUpdatedAt).toEqual(createdAt);
2335
2343
 
2344
+ const createdBy = person.$jazz.createdBy;
2345
+ expect(createdBy).toEqual(me.$jazz.id);
2346
+
2336
2347
  await new Promise((r) => setTimeout(r, 10));
2337
2348
  person.$jazz.set("name", "Jane");
2338
2349
 
2339
2350
  expect(person.$jazz.createdAt).toEqual(createdAt);
2340
2351
  expect(person.$jazz.lastUpdatedAt).not.toEqual(createdAt);
2352
+
2353
+ // Double check after update.
2354
+ expect(createdBy).toEqual(me.$jazz.id);
2355
+ });
2356
+
2357
+ test("createdBy does not change when updated", async () => {
2358
+ const Person = co.map({
2359
+ name: z.string(),
2360
+ });
2361
+ const me = Account.getMe();
2362
+
2363
+ const person = Person.create({ name: "John" });
2364
+
2365
+ const createdBy = person.$jazz.createdBy;
2366
+ expect(createdBy).toEqual(me.$jazz.id);
2367
+
2368
+ await new Promise((r) => setTimeout(r, 10));
2369
+ person.$jazz.set("name", "Jane");
2370
+
2371
+ // Double check after update.
2372
+ expect(createdBy).toEqual(me.$jazz.id);
2373
+ });
2374
+
2375
+ test("createdBy is after key rotation", async () => {
2376
+ const Person = co.map({
2377
+ name: z.string(),
2378
+ });
2379
+ const me = Account.getMe();
2380
+
2381
+ // Create person
2382
+ const person = Person.create({ name: "John" });
2383
+
2384
+ // True created by
2385
+ const createdBy = person.$jazz.createdBy;
2386
+
2387
+ // Create a user, grant access, then kick to trigger key rotation.
2388
+ const newUser = await createJazzTestAccount();
2389
+
2390
+ person.$jazz.owner.addMember(newUser, "reader");
2391
+
2392
+ // This should trigger read key rotation
2393
+ person.$jazz.owner.removeMember(newUser);
2394
+
2395
+ // Now create a new user and grant access
2396
+ const newUser2 = await createJazzTestAccount();
2397
+ person.$jazz.owner.addMember(newUser2, "reader");
2398
+
2399
+ // Load the CoValue as the new user:
2400
+ setActiveAccount(newUser2);
2401
+
2402
+ const personLoadedAsUser2 = await Person.load(person.$jazz.id);
2403
+ assertLoaded(personLoadedAsUser2);
2404
+ const createdByPerUser2 = personLoadedAsUser2.$jazz.createdBy;
2405
+ // Double check after update.
2406
+ expect(createdBy).toEqual(createdByPerUser2);
2341
2407
  });
2342
2408
  });
2343
2409