@weborigami/language 0.7.0-beta.1 → 0.7.0-beta.3

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 (43) hide show
  1. package/main.js +1 -0
  2. package/package.json +3 -2
  3. package/src/compiler/compile.js +0 -3
  4. package/src/compiler/optimize.js +56 -23
  5. package/src/compiler/parserHelpers.js +11 -8
  6. package/src/handlers/addExtensionKeyFn.js +18 -0
  7. package/src/handlers/epub_handler.js +54 -0
  8. package/src/handlers/getSource.js +1 -0
  9. package/src/handlers/handlers.js +2 -0
  10. package/src/handlers/ori_handler.js +2 -2
  11. package/src/handlers/oridocument_handler.js +5 -21
  12. package/src/handlers/{processOriExport.js → processOrigamiExport.js} +1 -1
  13. package/src/handlers/zip_handler.js +112 -0
  14. package/src/runtime/AsyncCacheTransform.d.ts +5 -1
  15. package/src/runtime/AsyncCacheTransform.js +22 -25
  16. package/src/runtime/OrigamiFileMap.js +3 -3
  17. package/src/runtime/SyncCacheTransform.d.ts +2 -1
  18. package/src/runtime/SyncCacheTransform.js +31 -34
  19. package/src/runtime/SystemCacheMap.js +28 -17
  20. package/src/runtime/counters.js +3 -0
  21. package/src/runtime/enableValueCaching.js +8 -7
  22. package/src/runtime/errors.js +33 -1
  23. package/src/runtime/execute.js +3 -0
  24. package/src/runtime/explainReferenceError.js +11 -24
  25. package/src/runtime/explainTraverseError.js +15 -6
  26. package/src/runtime/expressionObject.js +53 -34
  27. package/src/runtime/handleExtension.js +18 -9
  28. package/src/runtime/ops.js +4 -3
  29. package/test/compiler/compile.test.js +0 -1
  30. package/test/compiler/optimize.test.js +60 -15
  31. package/test/compiler/parse.test.js +38 -9
  32. package/test/handlers/epub_handler.test.js +27 -0
  33. package/test/handlers/fixtures/test.zip +0 -0
  34. package/test/handlers/zip_handler.test.js +45 -0
  35. package/test/runtime/AsyncCacheTransform.test.js +9 -4
  36. package/test/runtime/SyncCacheTransform.test.js +4 -0
  37. package/test/runtime/asyncCalcs.js +26 -4
  38. package/test/runtime/errors.test.js +4 -4
  39. package/test/runtime/expressionObject.test.js +23 -12
  40. package/test/runtime/handleExtension.test.js +0 -2
  41. package/test/runtime/ops.test.js +5 -3
  42. package/test/runtime/syncCalcs.js +26 -3
  43. package/test/runtime/systemCache.test.js +4 -0
@@ -15,6 +15,16 @@ import {
15
15
  } from "./codeHelpers.js";
16
16
 
17
17
  describe("optimize", () => {
18
+ test("define cache path based on source", () => {
19
+ const expression = "{ a = { b: 1 } }";
20
+ const expected = [
21
+ ops.object,
22
+ "/folder/test.ori/",
23
+ ["a", [ops.getter, [ops.object, "/folder/test.ori/a/", ["b", 1]]]],
24
+ ];
25
+ assertCompile(expression, expected);
26
+ });
27
+
18
28
  test("change local references to context references", () => {
19
29
  const expression = `(name) => {
20
30
  a: name,
@@ -26,6 +36,7 @@ describe("optimize", () => {
26
36
  [["name", [[ops.params, 0], 0]]],
27
37
  [
28
38
  ops.object,
39
+ "/folder/test.ori/_objects/",
29
40
  [
30
41
  "a",
31
42
  [
@@ -49,11 +60,13 @@ describe("optimize", () => {
49
60
  const expression = `{ a: 1, more: { a } }`;
50
61
  const expected = [
51
62
  ops.object,
63
+ "/folder/test.ori/",
52
64
  ["a", 1],
53
65
  [
54
66
  "more",
55
67
  [
56
68
  ops.object,
69
+ "/folder/test.ori/more/",
57
70
  [
58
71
  "a",
59
72
  [
@@ -76,11 +89,13 @@ describe("optimize", () => {
76
89
  }`;
77
90
  const expected = [
78
91
  ops.object,
92
+ "/folder/test.ori/",
79
93
  ["name", "Alice"],
80
94
  [
81
95
  "user",
82
96
  [
83
97
  ops.object,
98
+ "/folder/test.ori/user/",
84
99
  [
85
100
  "name",
86
101
  [
@@ -95,7 +110,7 @@ describe("optimize", () => {
95
110
  assertCompile(expression, expected, "shell");
96
111
  });
97
112
 
98
- describe.only("resolve reference", () => {
113
+ describe("resolve reference", () => {
99
114
  test("external reference", () => {
100
115
  // Compilation of `folder` where folder isn't a variable
101
116
  const code = createCode([
@@ -115,6 +130,31 @@ describe("optimize", () => {
115
130
  );
116
131
  });
117
132
 
133
+ test("external reference with path", () => {
134
+ // Compilation of `src/templates/page.ori` where src isn't a variable
135
+ const code = createCode([
136
+ markers.traverse,
137
+ [markers.reference, "src/"],
138
+ [ops.literal, "templates/"],
139
+ [ops.literal, "page.ori"],
140
+ ]);
141
+ const parent = {};
142
+ const expected = [
143
+ [
144
+ ops.cache,
145
+ "test.ori/_refs/src/",
146
+ [[ops.scope], [ops.literal, "src/"]],
147
+ ],
148
+ [ops.literal, "templates/"],
149
+ [ops.literal, "page.ori"],
150
+ ];
151
+ const globals = {};
152
+ assertCodeEqual(
153
+ optimize(code, { cachePath: "test.ori", globals, parent }),
154
+ expected,
155
+ );
156
+ });
157
+
118
158
  test("external reference with implied unpack", () => {
119
159
  // Compilation of `greet.ori("world")`
120
160
  const code = createCode([
@@ -141,6 +181,7 @@ describe("optimize", () => {
141
181
  // Compilation of `{ (posts) = posts.txt }`
142
182
  const code = createCode([
143
183
  ops.object,
184
+ null,
144
185
  [
145
186
  "(posts)",
146
187
  [ops.getter, [markers.traverse, [markers.reference, "posts.txt"]]],
@@ -149,13 +190,14 @@ describe("optimize", () => {
149
190
  const parent = {};
150
191
  const expected = [
151
192
  ops.object,
193
+ "/folder/test.ori/",
152
194
  [
153
195
  "(posts)",
154
196
  [
155
197
  ops.getter,
156
198
  [
157
199
  ops.cache,
158
- "test.ori/_refs/posts.txt",
200
+ "/folder/test.ori/_refs/posts.txt",
159
201
  [[ops.scope], [ops.literal, "posts.txt"]],
160
202
  ],
161
203
  ],
@@ -163,7 +205,7 @@ describe("optimize", () => {
163
205
  ];
164
206
  const globals = {};
165
207
  assertCodeEqual(
166
- optimize(code, { cachePath: "test.ori", globals, parent }),
208
+ optimize(code, { cachePath: "/folder/test.ori", globals, parent }),
167
209
  expected,
168
210
  );
169
211
  });
@@ -233,9 +275,9 @@ describe("optimize", () => {
233
275
  [ops.literal, "passwd"],
234
276
  ]);
235
277
  const expected = [
236
- ops.cache,
237
- "test.ori/_refs//etc/passwd",
238
- [[ops.rootDirectory], [ops.literal, "etc/"], [ops.literal, "passwd"]],
278
+ [ops.cache, "test.ori/_refs//", [ops.rootDirectory]],
279
+ [ops.literal, "etc/"],
280
+ [ops.literal, "passwd"],
239
281
  ];
240
282
  assertCodeEqual(optimize(code, { cachePath: "test.ori" }), expected);
241
283
  });
@@ -259,14 +301,13 @@ describe("optimize", () => {
259
301
  ]);
260
302
  const parent = {};
261
303
  const expected = [
262
- ops.cache,
263
- "test.ori/_refs/path/to/file",
264
304
  [
265
- [ops.scope],
266
- [ops.literal, "path/"],
267
- [ops.literal, "to/"],
268
- [ops.literal, "file"],
305
+ ops.cache,
306
+ "test.ori/_refs/path/",
307
+ [[ops.scope], [ops.literal, "path/"]],
269
308
  ],
309
+ [ops.literal, "to/"],
310
+ [ops.literal, "file"],
270
311
  ];
271
312
  assertCodeEqual(
272
313
  optimize(code, { cachePath: "test.ori", parent }),
@@ -284,9 +325,12 @@ describe("optimize", () => {
284
325
  const globals = {};
285
326
  const parent = {};
286
327
  const expected = [
287
- ops.cache,
288
- "test.ori/_refs/package.json/name",
289
- [[ops.scope], [ops.literal, "package.json/"], [ops.literal, "name"]],
328
+ [
329
+ ops.cache,
330
+ "test.ori/_refs/package.json/",
331
+ [[ops.scope], [ops.literal, "package.json/"]],
332
+ ],
333
+ [ops.literal, "name"],
290
334
  ];
291
335
  assertCodeEqual(
292
336
  optimize(code, { cachePath: "test.ori", globals, parent }),
@@ -319,6 +363,7 @@ function assertCompile(expression, expected, mode = "shell") {
319
363
  typeof expression !== "string"
320
364
  ? expression
321
365
  : {
366
+ cachePath: "/folder/test.ori",
322
367
  text: expression,
323
368
  relativePath: "test.ori",
324
369
  };
@@ -604,7 +604,7 @@ describe("Origami parser", () => {
604
604
  [ops.literal, "key"],
605
605
  ]);
606
606
  assertParse("callExpression", "{ a: 1, b: 2}/b", [
607
- [ops.object, ["a", [ops.literal, 1]], ["b", [ops.literal, 2]]],
607
+ [ops.object, null, ["a", [ops.literal, 1]], ["b", [ops.literal, 2]]],
608
608
  [ops.literal, "b"],
609
609
  ]);
610
610
  assertParse("callExpression", "files:foo/bar", [
@@ -897,12 +897,13 @@ Body`,
897
897
  assertParse("expression", "1", [ops.literal, 1]);
898
898
  assertParse("expression", "{ a: 1, b: 2 }", [
899
899
  ops.object,
900
+ null,
900
901
  ["a", [ops.literal, 1]],
901
902
  ["b", [ops.literal, 2]],
902
903
  ]);
903
904
  assertParse("expression", "serve { index.html: 'hello' }", [
904
905
  [markers.traverse, [markers.reference, "serve"]],
905
- [ops.object, ["index.html", [ops.literal, "hello"]]],
906
+ [ops.object, null, ["index.html", [ops.literal, "hello"]]],
906
907
  ]);
907
908
  assertParse("expression", "fn =`x`", [
908
909
  [markers.traverse, [markers.reference, "fn"]],
@@ -991,6 +992,7 @@ Body`,
991
992
  }`,
992
993
  [
993
994
  ops.object,
995
+ null,
994
996
  [
995
997
  "index.html",
996
998
  [
@@ -1010,6 +1012,7 @@ Body`,
1010
1012
  [markers.traverse, [markers.reference, "images"]],
1011
1013
  [
1012
1014
  ops.object,
1015
+ null,
1013
1016
  [
1014
1017
  "value",
1015
1018
  [markers.traverse, [markers.reference, "thumbnail.js"]],
@@ -1253,27 +1256,32 @@ Body`,
1253
1256
 
1254
1257
  describe("objectLiteral", () => {
1255
1258
  test("basic objects", () => {
1256
- assertParse("objectLiteral", "{}", [ops.object]);
1259
+ assertParse("objectLiteral", "{}", [ops.object, null]);
1257
1260
  assertParse("objectLiteral", "{ a: 1, b }", [
1258
1261
  ops.object,
1262
+ null,
1259
1263
  ["a", [ops.literal, 1]],
1260
1264
  ["b", [markers.traverse, [markers.reference, "b"]]],
1261
1265
  ]);
1262
1266
  assertParse("objectLiteral", "{ sub: { a: 1 } }", [
1263
1267
  ops.object,
1264
- ["sub", [ops.object, ["a", [ops.literal, 1]]]],
1268
+ null,
1269
+ ["sub", [ops.object, null, ["a", [ops.literal, 1]]]],
1265
1270
  ]);
1266
1271
  assertParse("objectLiteral", "{ sub: { a/: 1 } }", [
1267
1272
  ops.object,
1268
- ["sub", [ops.object, ["a/", [ops.literal, 1]]]],
1273
+ null,
1274
+ ["sub", [ops.object, null, ["a/", [ops.literal, 1]]]],
1269
1275
  ]);
1270
1276
  assertParse("objectLiteral", `{ "a": 1, "b": 2 }`, [
1271
1277
  ops.object,
1278
+ null,
1272
1279
  ["a", [ops.literal, 1]],
1273
1280
  ["b", [ops.literal, 2]],
1274
1281
  ]);
1275
1282
  assertParse("objectLiteral", "{ a = b, b = 2 }", [
1276
1283
  ops.object,
1284
+ null,
1277
1285
  ["a", [ops.getter, [markers.traverse, [markers.reference, "b"]]]],
1278
1286
  ["b", [ops.literal, 2]],
1279
1287
  ]);
@@ -1285,24 +1293,29 @@ Body`,
1285
1293
  }`,
1286
1294
  [
1287
1295
  ops.object,
1296
+ null,
1288
1297
  ["a", [ops.getter, [markers.traverse, [markers.reference, "b"]]]],
1289
1298
  ["b", [ops.literal, 2]],
1290
1299
  ],
1291
1300
  );
1292
1301
  assertParse("objectLiteral", "{ a: { b: 1 } }", [
1293
1302
  ops.object,
1294
- ["a", [ops.object, ["b", [ops.literal, 1]]]],
1303
+ null,
1304
+ ["a", [ops.object, null, ["b", [ops.literal, 1]]]],
1295
1305
  ]);
1296
1306
  assertParse("objectLiteral", "{ a: { b = 1 } }", [
1297
1307
  ops.object,
1298
- ["a", [ops.object, ["b", [ops.literal, 1]]]],
1308
+ null,
1309
+ ["a", [ops.object, null, ["b", [ops.literal, 1]]]],
1299
1310
  ]);
1300
1311
  assertParse("objectLiteral", "{ a: { b = fn() } }", [
1301
1312
  ops.object,
1313
+ null,
1302
1314
  [
1303
1315
  "a",
1304
1316
  [
1305
1317
  ops.object,
1318
+ null,
1306
1319
  [
1307
1320
  "b",
1308
1321
  [ops.getter, [[markers.traverse, [markers.reference, "fn"]]]],
@@ -1312,6 +1325,7 @@ Body`,
1312
1325
  ]);
1313
1326
  assertParse("objectLiteral", "{ x = fn.js('a') }", [
1314
1327
  ops.object,
1328
+ null,
1315
1329
  [
1316
1330
  "x",
1317
1331
  [
@@ -1326,10 +1340,12 @@ Body`,
1326
1340
 
1327
1341
  assertParse("objectLiteral", "{ (a): 1 }", [
1328
1342
  ops.object,
1343
+ null,
1329
1344
  ["(a)", [ops.literal, 1]],
1330
1345
  ]);
1331
1346
  assertParse("objectLiteral", "{ <path/to/file.txt> }", [
1332
1347
  ops.object,
1348
+ null,
1333
1349
  [
1334
1350
  "file.txt",
1335
1351
  [
@@ -1350,15 +1366,24 @@ Body`,
1350
1366
  assertParse("objectLiteral", "{ a: 1, ...more, c: a }", [
1351
1367
  [
1352
1368
  ops.object,
1369
+ null,
1353
1370
  ["a", [ops.literal, 1]],
1354
1371
  ["c", [markers.traverse, [markers.reference, "a"]]],
1355
1372
  [
1356
1373
  "_result",
1357
1374
  [
1358
1375
  ops.merge,
1359
- [ops.object, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
1376
+ [
1377
+ ops.object,
1378
+ null,
1379
+ ["a", [ops.getter, [[ops.inherited, 1], "a"]]],
1380
+ ],
1360
1381
  [markers.traverse, [markers.reference, "more"]],
1361
- [ops.object, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
1382
+ [
1383
+ ops.object,
1384
+ null,
1385
+ ["c", [ops.getter, [[ops.inherited, 1], "c"]]],
1386
+ ],
1362
1387
  ],
1363
1388
  ],
1364
1389
  ],
@@ -1366,6 +1391,7 @@ Body`,
1366
1391
  ]);
1367
1392
  assertParse("objectLiteral", "{ a: 1, ...{ b: 2 } }", [
1368
1393
  ops.object,
1394
+ null,
1369
1395
  ["a", [ops.literal, 1]],
1370
1396
  ["b", [ops.literal, 2]],
1371
1397
  ]);
@@ -1374,6 +1400,7 @@ Body`,
1374
1400
  test("computed property keys", () => {
1375
1401
  assertParse("objectLiteral", "{ [key]: value }", [
1376
1402
  ops.object,
1403
+ null,
1377
1404
  [
1378
1405
  [markers.traverse, [markers.reference, "key"]],
1379
1406
  [markers.traverse, [markers.reference, "value"]],
@@ -1816,6 +1843,7 @@ title: Title goes here
1816
1843
  Body text`,
1817
1844
  [
1818
1845
  ops.object,
1846
+ null,
1819
1847
  ["title", [ops.literal, "Title goes here"]],
1820
1848
  ["_body", [ops.templateIndent, [ops.literal, ["Body text"]]]],
1821
1849
  ],
@@ -1835,6 +1863,7 @@ Body text`,
1835
1863
  `,
1836
1864
  [
1837
1865
  ops.object,
1866
+ null,
1838
1867
  ["title", [ops.literal, "Title"]],
1839
1868
  [
1840
1869
  "_body",
@@ -0,0 +1,27 @@
1
+ import Zip from "adm-zip";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import epub_handler from "../../src/handlers/epub_handler.js";
5
+
6
+ describe("EPUB handler", () => {
7
+ test("ensures mimetype file comes first", async () => {
8
+ const tree = {
9
+ EPUB: {
10
+ "index.xhtml": "This is where the book content goes",
11
+ },
12
+ "META-INF": {
13
+ "container.xml": "This is where the metadata goes",
14
+ },
15
+ mimetype: "application/epub+zip",
16
+ };
17
+ const buffer = await epub_handler.pack(tree);
18
+ const unzipped = new Zip(buffer);
19
+ const entries = unzipped.getEntries();
20
+ const entryNames = entries.map((entry) => entry.entryName);
21
+ assert.deepEqual(entryNames, [
22
+ "mimetype",
23
+ "EPUB/index.xhtml",
24
+ "META-INF/container.xml",
25
+ ]);
26
+ });
27
+ });
Binary file
@@ -0,0 +1,45 @@
1
+ import { Tree } from "@weborigami/async-tree";
2
+ import Zip from "adm-zip";
3
+ import assert from "node:assert";
4
+ import fs from "node:fs/promises";
5
+ import { describe, test } from "node:test";
6
+ import zip_handler from "../../src/handlers/zip_handler.js";
7
+
8
+ describe("ZIP handler", () => {
9
+ test("creates a ZIP file as Buffer", async () => {
10
+ const tree = {
11
+ "ReadMe.md": "This is a ReadMe file.",
12
+ sub: {
13
+ "file.txt": "This is a text file in a subfolder.",
14
+ },
15
+ };
16
+ const buffer = await zip_handler.pack(tree);
17
+ const unzipped = new Zip(buffer);
18
+ const entries = unzipped.getEntries();
19
+ assert.equal(entries.length, 2);
20
+ assert.equal(entries[0].entryName, "ReadMe.md");
21
+ assert.equal(
22
+ entries[0].getData().toString("utf8"),
23
+ "This is a ReadMe file.",
24
+ );
25
+ assert.equal(entries[1].entryName, "sub/file.txt");
26
+ assert.equal(
27
+ entries[1].getData().toString("utf8"),
28
+ "This is a text file in a subfolder.",
29
+ );
30
+ });
31
+
32
+ test("reads a ZIP file", async () => {
33
+ const fixturePath = new URL("fixtures/test.zip", import.meta.url);
34
+ const buffer = await fs.readFile(fixturePath);
35
+ const tree = await zip_handler.unpack(buffer);
36
+ const plain = await Tree.plain(tree);
37
+ assert.deepEqual(Object.keys(plain), ["ReadMe.md", "sub"]);
38
+ assert.deepEqual(Object.keys(plain.sub), ["file.txt"]);
39
+ assert.equal(String(plain["ReadMe.md"]), "This is a ReadMe file.\n");
40
+ assert.equal(
41
+ String(plain.sub["file.txt"]),
42
+ "This is a text file in a subfolder.\n",
43
+ );
44
+ });
45
+ });
@@ -41,7 +41,8 @@ describe("AsyncCacheTransform", () => {
41
41
  ],
42
42
  ]);
43
43
 
44
- assert.deepEqual(await Tree.entries(calcs), [
44
+ const entries = await Tree.entries(calcs);
45
+ assert.deepEqual(entries, [
45
46
  ["a", 8],
46
47
  ["b", 4],
47
48
  ["c", 3],
@@ -55,11 +56,13 @@ describe("AsyncCacheTransform", () => {
55
56
 
56
57
  // Replace formula for a
57
58
  // { a = 3 * b, b = c + 1, c = 3 }
58
- data.set("a", async () => {
59
+ await data.set("a", async () => {
59
60
  log.push("a");
60
61
  const b = await calcs.get("b");
61
62
  return 3 * b;
62
63
  });
64
+ // Manually trigger cache invalidation
65
+ data.onValueChange("a");
63
66
  log = [];
64
67
  const a2 = await calcs.get("a");
65
68
  assert.strictEqual(a2, 12);
@@ -67,11 +70,12 @@ describe("AsyncCacheTransform", () => {
67
70
 
68
71
  // Replace formula for b
69
72
  // { a = 3 * b, b = c + 10, c = 3 }
70
- data.set("b", async () => {
73
+ await data.set("b", async () => {
71
74
  log.push("b");
72
75
  const c = await calcs.get("c");
73
76
  return c + 10;
74
77
  });
78
+ data.onValueChange("b");
75
79
  log = [];
76
80
  const a3 = await calcs.get("a");
77
81
  assert.strictEqual(a3, 39);
@@ -79,10 +83,11 @@ describe("AsyncCacheTransform", () => {
79
83
 
80
84
  // Replace value of c
81
85
  // { a = 3 * b, b = c + 10, c = 100 }
82
- data.set("c", async () => {
86
+ await data.set("c", async () => {
83
87
  log.push("c");
84
88
  return 100;
85
89
  });
90
+ data.onValueChange("c");
86
91
  log = [];
87
92
  const a4 = await calcs.get("a");
88
93
  assert.strictEqual(a4, 330);
@@ -62,6 +62,8 @@ describe("SyncCacheTransform", () => {
62
62
  const b = calcs.get("b");
63
63
  return 3 * b;
64
64
  });
65
+ // Manually trigger cache invalidation
66
+ data.onValueChange("a");
65
67
  log = [];
66
68
  const a2 = calcs.get("a");
67
69
  assert.strictEqual(a2, 12);
@@ -74,6 +76,7 @@ describe("SyncCacheTransform", () => {
74
76
  const c = calcs.get("c");
75
77
  return c + 10;
76
78
  });
79
+ data.onValueChange("b");
77
80
  log = [];
78
81
  const a3 = calcs.get("a");
79
82
  assert.strictEqual(a3, 39);
@@ -85,6 +88,7 @@ describe("SyncCacheTransform", () => {
85
88
  log.push("c");
86
89
  return 100;
87
90
  });
91
+ data.onValueChange("c");
88
92
  log = [];
89
93
  const a4 = calcs.get("a");
90
94
  assert.strictEqual(a4, 330);
@@ -1,13 +1,35 @@
1
- import { AsyncMap, SyncMap } from "@weborigami/async-tree";
1
+ import { AsyncMap } from "@weborigami/async-tree";
2
2
  import AsyncCacheTransform from "../../src/runtime/AsyncCacheTransform.js";
3
- import SyncCacheTransform from "../../src/runtime/SyncCacheTransform.js";
4
3
 
5
4
  export default function asyncCalcs(iterable) {
6
- const data = new (SyncCacheTransform(SyncMap))(iterable);
5
+ const data = new (AsyncCacheTransform(AsyncDataMap))(new Map(iterable));
7
6
  const calcs = new (AsyncCacheTransform(AsyncResultsMap))(data);
8
7
  return { calcs, data };
9
8
  }
10
9
 
10
+ class AsyncDataMap extends AsyncMap {
11
+ constructor(source) {
12
+ super();
13
+ this.source = source;
14
+ }
15
+
16
+ async delete(key) {
17
+ return this.source.delete(key);
18
+ }
19
+
20
+ async get(key) {
21
+ return this.source.get(key);
22
+ }
23
+
24
+ async *keys() {
25
+ yield* this.source.keys();
26
+ }
27
+
28
+ async set(key, value) {
29
+ return this.source.set(key, value);
30
+ }
31
+ }
32
+
11
33
  class AsyncResultsMap extends AsyncMap {
12
34
  constructor(source) {
13
35
  super();
@@ -15,7 +37,7 @@ class AsyncResultsMap extends AsyncMap {
15
37
  }
16
38
 
17
39
  async get(key) {
18
- let value = this.source.get(key);
40
+ let value = await this.source.get(key);
19
41
  if (typeof value === "function") {
20
42
  value = await value();
21
43
  }
@@ -188,7 +188,7 @@ evaluating: \x1B[31mposts.md\x1B[0m
188
188
  `(post1/totle).toUpperCase()`,
189
189
  `ReferenceError: Tried to get a property of something that doesn't exist.
190
190
  This path returned undefined: post1/totle
191
- evaluating: \x1B[31mpost1/totle\x1B[0m`,
191
+ evaluating: \x1B[31m(post1/totle)\x1B[0m`,
192
192
  { parent },
193
193
  );
194
194
  });
@@ -238,7 +238,7 @@ evaluating: \x1B[31msup/\x1B[0m`,
238
238
  Tried to traverse path: map/1/a
239
239
  Stopped unexpectedly at: 1/
240
240
  Slash-separated keys are searched as strings. Here there's no string "1" key, but there is a number 1 key.
241
- To get the value for that number key, use parentheses: map/(1)
241
+ To get the value for that number key, use parentheses: map/(1)/a
242
242
  evaluating: \x1B[31m1/\x1B[0m`,
243
243
  { parent },
244
244
  );
@@ -250,8 +250,8 @@ evaluating: \x1B[31m1/\x1B[0m`,
250
250
  });
251
251
  await assertError(
252
252
  `file.foo/bar`,
253
- `TraverseError: A path hit binary data that can't be unpacked.
254
- Tried to traverse path: file.foo/bar
253
+ `TraverseError: A path tried to unpack data but there's no unpack function.
254
+ Tried to traverse path: file.foo/
255
255
  Stopped unexpectedly at: file.foo/
256
256
  The value couldn't be unpacked because no file extension handler is registered for ".foo".
257
257
  evaluating: \x1B[31mfile.foo/\x1B[0m`,