jazz-tools 0.18.27 → 0.18.29

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 (51) hide show
  1. package/.turbo/turbo-build.log +47 -47
  2. package/CHANGELOG.md +23 -0
  3. package/dist/{chunk-ZIAN4UY5.js → chunk-F55R554M.js} +171 -120
  4. package/dist/chunk-F55R554M.js.map +1 -0
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/inspector/{custom-element-A7UAELEG.js → custom-element-35MDW4SW.js} +1840 -1837
  8. package/dist/inspector/{custom-element-A7UAELEG.js.map → custom-element-35MDW4SW.js.map} +1 -1
  9. package/dist/inspector/custom-element.d.ts.map +1 -1
  10. package/dist/inspector/index.d.ts +1 -1
  11. package/dist/inspector/index.d.ts.map +1 -1
  12. package/dist/inspector/index.js +0 -1
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/react-core/hooks.d.ts +4 -0
  16. package/dist/react-core/hooks.d.ts.map +1 -1
  17. package/dist/react-core/index.js +5 -0
  18. package/dist/react-core/index.js.map +1 -1
  19. package/dist/testing.js +1 -1
  20. package/dist/tools/coValues/coList.d.ts +11 -3
  21. package/dist/tools/coValues/coList.d.ts.map +1 -1
  22. package/dist/tools/coValues/coMap.d.ts +21 -5
  23. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  24. package/dist/tools/coValues/group.d.ts +2 -2
  25. package/dist/tools/coValues/group.d.ts.map +1 -1
  26. package/dist/tools/coValues/inbox.d.ts.map +1 -1
  27. package/dist/tools/coValues/interfaces.d.ts +17 -1
  28. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  29. package/dist/tools/exports.d.ts +1 -1
  30. package/dist/tools/exports.d.ts.map +1 -1
  31. package/dist/tools/tests/coList.unique.test.d.ts +2 -0
  32. package/dist/tools/tests/coList.unique.test.d.ts.map +1 -0
  33. package/dist/tools/tests/coMap.unique.test.d.ts +2 -0
  34. package/dist/tools/tests/coMap.unique.test.d.ts.map +1 -0
  35. package/package.json +5 -5
  36. package/src/inspector/custom-element.tsx +4 -0
  37. package/src/inspector/index.tsx +0 -2
  38. package/src/react-core/hooks.ts +8 -0
  39. package/src/react-core/tests/useAccount.test.ts +61 -1
  40. package/src/react-core/tests/usePassPhraseAuth.test.ts +74 -2
  41. package/src/tools/coValues/coList.ts +38 -35
  42. package/src/tools/coValues/coMap.ts +38 -38
  43. package/src/tools/coValues/group.ts +5 -1
  44. package/src/tools/coValues/inbox.ts +4 -3
  45. package/src/tools/coValues/interfaces.ts +119 -0
  46. package/src/tools/exports.ts +1 -0
  47. package/src/tools/tests/coList.test.ts +0 -190
  48. package/src/tools/tests/coList.unique.test.ts +244 -0
  49. package/src/tools/tests/coMap.test.ts +0 -433
  50. package/src/tools/tests/coMap.unique.test.ts +579 -0
  51. package/dist/chunk-ZIAN4UY5.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import {
2
+ cojsonInternals,
2
3
  type CoValueUniqueness,
3
4
  type CojsonInternalTypes,
4
5
  type RawCoValue,
@@ -25,6 +26,7 @@ import {
25
26
  inspect,
26
27
  } from "../internal.js";
27
28
  import type { BranchDefinition } from "../subscribe/types.js";
29
+ import { CoValueHeader } from "cojson/dist/coValueCore/verifiedState.js";
28
30
 
29
31
  /** @category Abstract interfaces */
30
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -446,6 +448,123 @@ export function parseGroupCreateOptions(
446
448
  : { owner: options.owner ?? activeAccountContext.get() };
447
449
  }
448
450
 
451
+ export function getIdFromHeader(
452
+ header: CoValueHeader,
453
+ loadAs?: Account | AnonymousJazzAgent | Group,
454
+ ) {
455
+ loadAs ||= activeAccountContext.get();
456
+
457
+ const node =
458
+ loadAs[TypeSym] === "Anonymous" ? loadAs.node : loadAs.$jazz.localNode;
459
+
460
+ return cojsonInternals.idforHeader(header, node.crypto);
461
+ }
462
+
463
+ export async function unstable_loadUnique<
464
+ S extends CoValueClassOrSchema,
465
+ const R extends ResolveQuery<S>,
466
+ >(
467
+ schema: S,
468
+ options: {
469
+ unique: CoValueUniqueness["uniqueness"];
470
+ onCreateWhenMissing?: () => void;
471
+ onUpdateWhenFound?: (value: Loaded<S, R>) => void;
472
+ owner: Account | Group;
473
+ resolve?: ResolveQueryStrict<S, R>;
474
+ },
475
+ ): Promise<Loaded<S, R> | null> {
476
+ const cls = coValueClassFromCoValueClassOrSchema(schema);
477
+
478
+ if (
479
+ !("_getUniqueHeader" in cls) ||
480
+ typeof cls._getUniqueHeader !== "function"
481
+ ) {
482
+ throw new Error("CoValue class does not support unique headers");
483
+ }
484
+
485
+ const header = cls._getUniqueHeader(options.unique, options.owner.$jazz.id);
486
+
487
+ return internalLoadUnique(cls, {
488
+ header,
489
+ onCreateWhenMissing: options.onCreateWhenMissing,
490
+ // @ts-expect-error loaded is not compatible with Resolved at type level, but they are the same thing
491
+ onUpdateWhenFound: options.onUpdateWhenFound,
492
+ owner: options.owner,
493
+ // @ts-expect-error loaded is not compatible with Resolved at type level, but they are the same thing
494
+ resolve: options.resolve,
495
+ }) as unknown as Loaded<S, R> | null;
496
+ }
497
+
498
+ export async function internalLoadUnique<
499
+ V extends CoValue,
500
+ R extends RefsToResolve<V>,
501
+ >(
502
+ cls: CoValueClass<V>,
503
+ options: {
504
+ header: CoValueHeader;
505
+ onCreateWhenMissing?: () => void;
506
+ onUpdateWhenFound?: (value: Resolved<V, R>) => void;
507
+ owner: Account | Group;
508
+ resolve?: RefsToResolveStrict<V, R>;
509
+ },
510
+ ): Promise<Resolved<V, R> | null> {
511
+ const loadAs = options.owner.$jazz.loadedAs;
512
+
513
+ const node =
514
+ loadAs[TypeSym] === "Anonymous" ? loadAs.node : loadAs.$jazz.localNode;
515
+
516
+ const id = cojsonInternals.idforHeader(options.header, node.crypto);
517
+
518
+ // We first try to load the unique value without using resolve and without
519
+ // retrying failures
520
+ // This way when we want to upsert we are sure that, if the load failed
521
+ // it failed because the unique value was missing
522
+ await loadCoValueWithoutMe(cls, id, {
523
+ skipRetry: true,
524
+ loadAs,
525
+ });
526
+
527
+ const isAvailable = node.getCoValue(id).hasVerifiedContent();
528
+
529
+ // if load returns unavailable, we check the state in localNode
530
+ // to ward against race conditions that would happen when
531
+ // running the same upsert unique concurrently
532
+ if (options.onCreateWhenMissing && !isAvailable) {
533
+ options.onCreateWhenMissing();
534
+
535
+ return loadCoValueWithoutMe(cls, id, {
536
+ loadAs,
537
+ resolve: options.resolve,
538
+ });
539
+ }
540
+
541
+ if (!isAvailable) {
542
+ return null;
543
+ }
544
+
545
+ if (options.onUpdateWhenFound) {
546
+ // we deeply load the value, retrying any failures
547
+ const loaded = await loadCoValueWithoutMe(cls, id, {
548
+ loadAs,
549
+ resolve: options.resolve,
550
+ });
551
+
552
+ if (loaded) {
553
+ // we don't return the update result because
554
+ // we want to run another load to backfill any possible partially loaded
555
+ // values that have been set in the update
556
+ options.onUpdateWhenFound(loaded);
557
+ } else {
558
+ return loaded;
559
+ }
560
+ }
561
+
562
+ return loadCoValueWithoutMe(cls, id, {
563
+ loadAs,
564
+ resolve: options.resolve,
565
+ });
566
+ }
567
+
449
568
  /**
450
569
  * Deeply export a CoValue to a content piece.
451
570
  *
@@ -56,6 +56,7 @@ export {
56
56
  exportCoValue,
57
57
  importContentPieces,
58
58
  Ref,
59
+ unstable_loadUnique,
59
60
  } from "./internal.js";
60
61
 
61
62
  export {
@@ -1164,196 +1164,6 @@ describe("CoList subscription", async () => {
1164
1164
  });
1165
1165
  });
1166
1166
 
1167
- describe("CoList unique methods", () => {
1168
- test("loadUnique returns existing list", async () => {
1169
- const ItemList = co.list(z.string());
1170
- const group = Group.create();
1171
-
1172
- const originalList = ItemList.create(["item1", "item2", "item3"], {
1173
- owner: group,
1174
- unique: "test-list",
1175
- });
1176
-
1177
- const foundList = await ItemList.loadUnique("test-list", group.$jazz.id);
1178
- expect(foundList).toEqual(originalList);
1179
- expect(foundList?.length).toBe(3);
1180
- expect(foundList?.[0]).toBe("item1");
1181
- });
1182
-
1183
- test("loadUnique returns null for non-existent list", async () => {
1184
- const ItemList = co.list(z.string());
1185
- const group = Group.create();
1186
-
1187
- const foundList = await ItemList.loadUnique("non-existent", group.$jazz.id);
1188
- expect(foundList).toBeNull();
1189
- });
1190
-
1191
- test("upsertUnique creates new list when none exists", async () => {
1192
- const ItemList = co.list(z.string());
1193
- const group = Group.create();
1194
-
1195
- const sourceData = ["item1", "item2", "item3"];
1196
-
1197
- const result = await ItemList.upsertUnique({
1198
- value: sourceData,
1199
- unique: "new-list",
1200
- owner: group,
1201
- });
1202
-
1203
- expect(result).not.toBeNull();
1204
- expect(result?.length).toBe(3);
1205
- expect(result?.[0]).toBe("item1");
1206
- expect(result?.[1]).toBe("item2");
1207
- expect(result?.[2]).toBe("item3");
1208
- });
1209
-
1210
- test("upsertUnique without an active account", async () => {
1211
- const account = activeAccountContext.get();
1212
- const ItemList = co.list(z.string());
1213
-
1214
- const sourceData = ["item1", "item2", "item3"];
1215
-
1216
- const result = await runWithoutActiveAccount(() => {
1217
- return ItemList.upsertUnique({
1218
- value: sourceData,
1219
- unique: "new-list",
1220
- owner: account,
1221
- });
1222
- });
1223
-
1224
- expect(result).not.toBeNull();
1225
- expect(result?.length).toBe(3);
1226
- expect(result?.[0]).toBe("item1");
1227
- expect(result?.[1]).toBe("item2");
1228
- expect(result?.[2]).toBe("item3");
1229
-
1230
- expect(result?.$jazz.owner).toEqual(account);
1231
- });
1232
-
1233
- test("upsertUnique updates existing list", async () => {
1234
- const ItemList = co.list(z.string());
1235
- const group = Group.create();
1236
-
1237
- // Create initial list
1238
- const originalList = ItemList.create(["original1", "original2"], {
1239
- owner: group,
1240
- unique: "update-list",
1241
- });
1242
-
1243
- // Upsert with new data
1244
- const updatedList = await ItemList.upsertUnique({
1245
- value: ["updated1", "updated2", "updated3"],
1246
- unique: "update-list",
1247
- owner: group,
1248
- });
1249
-
1250
- expect(updatedList).toEqual(originalList); // Should be the same instance
1251
- expect(updatedList?.length).toBe(3);
1252
- expect(updatedList?.[0]).toBe("updated1");
1253
- expect(updatedList?.[1]).toBe("updated2");
1254
- expect(updatedList?.[2]).toBe("updated3");
1255
- });
1256
-
1257
- test("upsertUnique with CoValue items", async () => {
1258
- const Item = co.map({
1259
- name: z.string(),
1260
- value: z.number(),
1261
- });
1262
- const ItemList = co.list(Item);
1263
- const group = Group.create();
1264
-
1265
- const items = [
1266
- Item.create({ name: "First", value: 1 }, group),
1267
- Item.create({ name: "Second", value: 2 }, group),
1268
- ];
1269
-
1270
- const result = await ItemList.upsertUnique({
1271
- value: items,
1272
- unique: "item-list",
1273
- owner: group,
1274
- resolve: { $each: true },
1275
- });
1276
-
1277
- expect(result).not.toBeNull();
1278
- expect(result?.length).toBe(2);
1279
- expect(result?.[0]?.name).toBe("First");
1280
- expect(result?.[1]?.name).toBe("Second");
1281
- });
1282
-
1283
- test("upsertUnique updates list with CoValue items", async () => {
1284
- const Item = co.map({
1285
- name: z.string(),
1286
- value: z.number(),
1287
- });
1288
- const ItemList = co.list(Item);
1289
- const group = Group.create();
1290
-
1291
- // Create initial list
1292
- const initialItems = [Item.create({ name: "Initial", value: 0 }, group)];
1293
- const originalList = ItemList.create(initialItems, {
1294
- owner: group,
1295
- unique: "updateable-item-list",
1296
- });
1297
-
1298
- // Upsert with new items
1299
- const newItems = [
1300
- Item.create({ name: "Updated", value: 1 }, group),
1301
- Item.create({ name: "Added", value: 2 }, group),
1302
- ];
1303
-
1304
- const updatedList = await ItemList.upsertUnique({
1305
- value: newItems,
1306
- unique: "updateable-item-list",
1307
- owner: group,
1308
- resolve: { $each: true },
1309
- });
1310
-
1311
- expect(updatedList).toEqual(originalList); // Should be the same instance
1312
- expect(updatedList?.length).toBe(2);
1313
- expect(updatedList?.[0]?.name).toBe("Updated");
1314
- expect(updatedList?.[1]?.name).toBe("Added");
1315
- });
1316
-
1317
- test("findUnique returns correct ID", async () => {
1318
- const ItemList = co.list(z.string());
1319
- const group = Group.create();
1320
-
1321
- const originalList = ItemList.create(["test"], {
1322
- owner: group,
1323
- unique: "find-test",
1324
- });
1325
-
1326
- const foundId = ItemList.findUnique("find-test", group.$jazz.id);
1327
- expect(foundId).toBe(originalList.$jazz.id);
1328
- });
1329
-
1330
- test("upsertUnique with resolve options", async () => {
1331
- const Category = co.map({ title: z.string() });
1332
- const Item = co.map({
1333
- name: z.string(),
1334
- category: Category,
1335
- });
1336
- const ItemList = co.list(Item);
1337
- const group = Group.create();
1338
-
1339
- const category = Category.create({ title: "Category 1" }, group);
1340
-
1341
- const items = [Item.create({ name: "Item 1", category }, group)];
1342
-
1343
- const result = await ItemList.upsertUnique({
1344
- value: items,
1345
- unique: "resolved-list",
1346
- owner: group,
1347
- resolve: { $each: { category: true } },
1348
- });
1349
-
1350
- expect(result).not.toBeNull();
1351
- expect(result?.length).toBe(1);
1352
- expect(result?.[0]?.name).toBe("Item 1");
1353
- expect(result?.[0]?.category?.title).toBe("Category 1");
1354
- });
1355
- });
1356
-
1357
1167
  describe("co.list schema", () => {
1358
1168
  test("can access the inner schema of a co.list", () => {
1359
1169
  const Keywords = co.list(co.plainText());
@@ -0,0 +1,244 @@
1
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
+ import { assert, beforeEach, describe, expect, test, vi } from "vitest";
3
+ import { Account, Group, subscribeToCoValue, z } from "../index.js";
4
+ import {
5
+ Loaded,
6
+ activeAccountContext,
7
+ co,
8
+ coValueClassFromCoValueClassOrSchema,
9
+ } from "../internal.js";
10
+ import {
11
+ createJazzTestAccount,
12
+ runWithoutActiveAccount,
13
+ setupJazzTestSync,
14
+ } from "../testing.js";
15
+ import { setupTwoNodes, waitFor } from "./utils.js";
16
+
17
+ const Crypto = await WasmCrypto.create();
18
+
19
+ let me = await Account.create({
20
+ creationProps: { name: "Hermes Puggington" },
21
+ crypto: Crypto,
22
+ });
23
+
24
+ beforeEach(async () => {
25
+ await setupJazzTestSync();
26
+
27
+ me = await createJazzTestAccount({
28
+ isCurrentActiveAccount: true,
29
+ creationProps: { name: "Hermes Puggington" },
30
+ });
31
+ });
32
+
33
+ describe("CoList unique methods", () => {
34
+ test("loadUnique returns existing list", async () => {
35
+ const ItemList = co.list(z.string());
36
+ const group = Group.create();
37
+
38
+ const originalList = ItemList.create(["item1", "item2", "item3"], {
39
+ owner: group,
40
+ unique: "test-list",
41
+ });
42
+
43
+ const foundList = await ItemList.loadUnique("test-list", group.$jazz.id);
44
+ expect(foundList).toEqual(originalList);
45
+ expect(foundList?.length).toBe(3);
46
+ expect(foundList?.[0]).toBe("item1");
47
+ });
48
+
49
+ test("loadUnique returns null for non-existent list", async () => {
50
+ const ItemList = co.list(z.string());
51
+ const group = Group.create();
52
+
53
+ const foundList = await ItemList.loadUnique("non-existent", group.$jazz.id);
54
+ expect(foundList).toBeNull();
55
+ });
56
+
57
+ test("upsertUnique creates new list when none exists", async () => {
58
+ const ItemList = co.list(z.string());
59
+ const group = Group.create();
60
+
61
+ const sourceData = ["item1", "item2", "item3"];
62
+
63
+ const result = await ItemList.upsertUnique({
64
+ value: sourceData,
65
+ unique: "new-list",
66
+ owner: group,
67
+ });
68
+
69
+ expect(result).not.toBeNull();
70
+ expect(result?.length).toBe(3);
71
+ expect(result?.[0]).toBe("item1");
72
+ expect(result?.[1]).toBe("item2");
73
+ expect(result?.[2]).toBe("item3");
74
+ });
75
+
76
+ test("upsertUnique without an active account", async () => {
77
+ const account = activeAccountContext.get();
78
+ const ItemList = co.list(z.string());
79
+
80
+ const sourceData = ["item1", "item2", "item3"];
81
+
82
+ const result = await runWithoutActiveAccount(() => {
83
+ return ItemList.upsertUnique({
84
+ value: sourceData,
85
+ unique: "new-list",
86
+ owner: account,
87
+ });
88
+ });
89
+
90
+ expect(result).not.toBeNull();
91
+ expect(result?.length).toBe(3);
92
+ expect(result?.[0]).toBe("item1");
93
+ expect(result?.[1]).toBe("item2");
94
+ expect(result?.[2]).toBe("item3");
95
+
96
+ expect(result?.$jazz.owner).toEqual(account);
97
+ });
98
+
99
+ test("upsertUnique updates existing list", async () => {
100
+ const ItemList = co.list(z.string());
101
+ const group = Group.create();
102
+
103
+ // Create initial list
104
+ const originalList = ItemList.create(["original1", "original2"], {
105
+ owner: group,
106
+ unique: "update-list",
107
+ });
108
+
109
+ // Upsert with new data
110
+ const updatedList = await ItemList.upsertUnique({
111
+ value: ["updated1", "updated2", "updated3"],
112
+ unique: "update-list",
113
+ owner: group,
114
+ });
115
+
116
+ expect(updatedList).toEqual(originalList); // Should be the same instance
117
+ expect(updatedList?.length).toBe(3);
118
+ expect(updatedList?.[0]).toBe("updated1");
119
+ expect(updatedList?.[1]).toBe("updated2");
120
+ expect(updatedList?.[2]).toBe("updated3");
121
+ });
122
+
123
+ test("upsertUnique with CoValue items", async () => {
124
+ const Item = co.map({
125
+ name: z.string(),
126
+ value: z.number(),
127
+ });
128
+ const ItemList = co.list(Item);
129
+ const group = Group.create();
130
+
131
+ const items = [
132
+ Item.create({ name: "First", value: 1 }, group),
133
+ Item.create({ name: "Second", value: 2 }, group),
134
+ ];
135
+
136
+ const result = await ItemList.upsertUnique({
137
+ value: items,
138
+ unique: "item-list",
139
+ owner: group,
140
+ resolve: { $each: true },
141
+ });
142
+
143
+ expect(result).not.toBeNull();
144
+ expect(result?.length).toBe(2);
145
+ expect(result?.[0]?.name).toBe("First");
146
+ expect(result?.[1]?.name).toBe("Second");
147
+ });
148
+
149
+ test("upsertUnique updates list with CoValue items", async () => {
150
+ const Item = co.map({
151
+ name: z.string(),
152
+ value: z.number(),
153
+ });
154
+ const ItemList = co.list(Item);
155
+ const group = Group.create();
156
+
157
+ // Create initial list
158
+ const initialItems = [Item.create({ name: "Initial", value: 0 }, group)];
159
+ const originalList = ItemList.create(initialItems, {
160
+ owner: group,
161
+ unique: "updateable-item-list",
162
+ });
163
+
164
+ // Upsert with new items
165
+ const newItems = [
166
+ Item.create({ name: "Updated", value: 1 }, group),
167
+ Item.create({ name: "Added", value: 2 }, group),
168
+ ];
169
+
170
+ const updatedList = await ItemList.upsertUnique({
171
+ value: newItems,
172
+ unique: "updateable-item-list",
173
+ owner: group,
174
+ resolve: { $each: true },
175
+ });
176
+
177
+ expect(updatedList).toEqual(originalList); // Should be the same instance
178
+ expect(updatedList?.length).toBe(2);
179
+ expect(updatedList?.[0]?.name).toBe("Updated");
180
+ expect(updatedList?.[1]?.name).toBe("Added");
181
+ });
182
+
183
+ test("findUnique returns correct ID", async () => {
184
+ const ItemList = co.list(z.string());
185
+ const group = Group.create();
186
+
187
+ const originalList = ItemList.create(["test"], {
188
+ owner: group,
189
+ unique: "find-test",
190
+ });
191
+
192
+ const foundId = ItemList.findUnique("find-test", group.$jazz.id);
193
+ expect(foundId).toBe(originalList.$jazz.id);
194
+ });
195
+
196
+ test("upsertUnique with resolve options", async () => {
197
+ const Category = co.map({ title: z.string() });
198
+ const Item = co.map({
199
+ name: z.string(),
200
+ category: Category,
201
+ });
202
+ const ItemList = co.list(Item);
203
+ const group = Group.create();
204
+
205
+ const category = Category.create({ title: "Category 1" }, group);
206
+
207
+ const items = [Item.create({ name: "Item 1", category }, group)];
208
+
209
+ const result = await ItemList.upsertUnique({
210
+ value: items,
211
+ unique: "resolved-list",
212
+ owner: group,
213
+ resolve: { $each: { category: true } },
214
+ });
215
+
216
+ expect(result).not.toBeNull();
217
+ expect(result?.length).toBe(1);
218
+ expect(result?.[0]?.name).toBe("Item 1");
219
+ expect(result?.[0]?.category?.title).toBe("Category 1");
220
+ });
221
+
222
+ test("concurrently upserting the same value", async () => {
223
+ const ItemList = co.list(z.string());
224
+
225
+ const owner = Group.create();
226
+
227
+ const promises = Array.from({ length: 3 }, (_, i) =>
228
+ ItemList.upsertUnique({
229
+ owner,
230
+ unique: "concurrent",
231
+ value: [`Item ${i}`, `Second ${i}`],
232
+ }),
233
+ );
234
+
235
+ await Promise.all(promises);
236
+
237
+ const result = await ItemList.loadUnique("concurrent", owner.$jazz.id);
238
+ assert(result);
239
+
240
+ expect(result.length).toBe(2);
241
+ expect(result[0]).toBe(`Item 2`);
242
+ expect(result[1]).toBe(`Second 2`);
243
+ });
244
+ });