@weborigami/language 0.7.0-beta.2 → 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 (36) hide show
  1. package/main.js +1 -0
  2. package/package.json +2 -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/getSource.js +1 -0
  7. package/src/handlers/ori_handler.js +2 -2
  8. package/src/handlers/oridocument_handler.js +2 -2
  9. package/src/handlers/{processOriExport.js → processOrigamiExport.js} +1 -1
  10. package/src/runtime/AsyncCacheTransform.d.ts +5 -1
  11. package/src/runtime/AsyncCacheTransform.js +22 -25
  12. package/src/runtime/OrigamiFileMap.js +3 -3
  13. package/src/runtime/SyncCacheTransform.d.ts +2 -1
  14. package/src/runtime/SyncCacheTransform.js +31 -34
  15. package/src/runtime/SystemCacheMap.js +28 -17
  16. package/src/runtime/counters.js +3 -0
  17. package/src/runtime/enableValueCaching.js +8 -7
  18. package/src/runtime/errors.js +33 -1
  19. package/src/runtime/execute.js +3 -0
  20. package/src/runtime/explainReferenceError.js +11 -24
  21. package/src/runtime/explainTraverseError.js +15 -6
  22. package/src/runtime/expressionObject.js +53 -34
  23. package/src/runtime/handleExtension.js +18 -9
  24. package/src/runtime/ops.js +4 -3
  25. package/test/compiler/compile.test.js +0 -1
  26. package/test/compiler/optimize.test.js +60 -15
  27. package/test/compiler/parse.test.js +38 -9
  28. package/test/runtime/AsyncCacheTransform.test.js +9 -4
  29. package/test/runtime/SyncCacheTransform.test.js +4 -0
  30. package/test/runtime/asyncCalcs.js +26 -4
  31. package/test/runtime/errors.test.js +4 -4
  32. package/test/runtime/expressionObject.test.js +23 -12
  33. package/test/runtime/handleExtension.test.js +0 -2
  34. package/test/runtime/ops.test.js +5 -3
  35. package/test/runtime/syncCalcs.js +26 -3
  36. package/test/runtime/systemCache.test.js +4 -0
@@ -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",
@@ -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`,
@@ -23,7 +23,7 @@ describe("expressionObject", () => {
23
23
  ];
24
24
  const context = new SyncMap();
25
25
 
26
- const object = await expressionObject(entries, {
26
+ const object = await expressionObject("test.ori/", entries, {
27
27
  object: context,
28
28
  parent,
29
29
  });
@@ -36,7 +36,7 @@ describe("expressionObject", () => {
36
36
  let count = 0;
37
37
  const increment = () => count++;
38
38
  const entries = [["count", [increment]]];
39
- const object = await expressionObject(entries);
39
+ const object = await expressionObject("test.ori/", entries);
40
40
  const propertyDescriptor = Object.getOwnPropertyDescriptor(object, "count");
41
41
  assert.equal(propertyDescriptor?.value, 0);
42
42
  assert.equal(object.count, 0);
@@ -47,7 +47,7 @@ describe("expressionObject", () => {
47
47
  let count = 0;
48
48
  const increment = () => count++;
49
49
  const entries = [["count", [ops.getter, [increment]]]];
50
- const object = await expressionObject(entries);
50
+ const object = await expressionObject("test.ori/", entries);
51
51
  object[cachePathSymbol] = "foo.ori/"; // enable caching on this object tree
52
52
  assert.equal(await object.count, 0);
53
53
  const propertyDescriptor = Object.getOwnPropertyDescriptor(object, "count");
@@ -58,7 +58,7 @@ describe("expressionObject", () => {
58
58
 
59
59
  test("treats a getter for a primitive value as a regular property", async () => {
60
60
  const entries = [["name", [ops.getter, "world"]]];
61
- const object = await expressionObject(entries);
61
+ const object = await expressionObject("test.ori/", entries);
62
62
  assert.equal(object.name, "world");
63
63
  });
64
64
 
@@ -68,7 +68,9 @@ describe("expressionObject", () => {
68
68
  ["message", [ops.deepText, "Hello, ", [[ops.inherited, 0], "name"], "!"]],
69
69
  ];
70
70
  const context = new SyncMap();
71
- const object = await expressionObject(entries, { object: context });
71
+ const object = await expressionObject("test.ori/", entries, {
72
+ object: context,
73
+ });
72
74
  assert.deepEqual(await Tree.plain(object), {
73
75
  name: "world",
74
76
  message: "Hello, world!",
@@ -92,7 +94,9 @@ describe("expressionObject", () => {
92
94
  ["name", "data"],
93
95
  ];
94
96
  const context = new SyncMap();
95
- const object = await expressionObject(entries, { object: context });
97
+ const object = await expressionObject("test.ori/", entries, {
98
+ object: context,
99
+ });
96
100
  assert.deepEqual(await Tree.plain(object), {
97
101
  "data.json": 1,
98
102
  name: "data",
@@ -105,7 +109,7 @@ describe("expressionObject", () => {
105
109
  const globals = {
106
110
  json_handler: { unpack: (data) => JSON.parse(data) },
107
111
  };
108
- const result = await expressionObject(entries, {
112
+ const result = await expressionObject("test.ori/", entries, {
109
113
  object: context,
110
114
  globals,
111
115
  });
@@ -119,7 +123,7 @@ describe("expressionObject", () => {
119
123
  ["(hidden)", "shh"],
120
124
  ["visible", "hey"],
121
125
  ];
122
- const object = await expressionObject(entries);
126
+ const object = await expressionObject("test.ori/", entries);
123
127
  assert.deepEqual(Object.keys(object), ["visible"]);
124
128
  assert.equal(object["hidden"], "shh");
125
129
  });
@@ -127,15 +131,15 @@ describe("expressionObject", () => {
127
131
  test("provides a symbols.keys method returning normalized keys", async () => {
128
132
  const entries = [
129
133
  // Will return a tree, should have a slash
130
- ["getter", [ops.getter, [ops.object, ["b", [ops.literal, 2]]]]],
134
+ ["getter", [ops.getter, [ops.object, null, ["b", [ops.literal, 2]]]]],
131
135
  ["hasSlash/", "This isn't really a tree but says it is"],
132
136
  ["message", "Hello"],
133
137
  // Immediate maplike value, should have a slash
134
- ["object", [ops.object, ["b", [ops.literal, 2]]]],
138
+ ["object", [ops.object, null, ["b", [ops.literal, 2]]]],
135
139
  // Computed key
136
140
  [[ops.deepText, [ops.array, "data", ".json"]], 1],
137
141
  ];
138
- const object = await expressionObject(entries);
142
+ const object = await expressionObject("test.ori/", entries);
139
143
  assert.deepEqual(object[symbols.keys](), [
140
144
  "getter/",
141
145
  "hasSlash/",
@@ -152,7 +156,7 @@ describe("expressionObject", () => {
152
156
  const getNumber = () =>
153
157
  systemCache.getOrInsertComputed("dependency", () => 1);
154
158
  const entries = [["number", [ops.getter, [getNumber]]]];
155
- const object = await expressionObject(entries);
159
+ const object = await expressionObject("test.ori/", entries);
156
160
  object[cachePathSymbol] = "src/test.ori/"; // enable caching on this object tree
157
161
  const number = await object.number;
158
162
  assert.equal(number, 1);
@@ -162,4 +166,11 @@ describe("expressionObject", () => {
162
166
  assert(dependencyEntry.downstreams.has("src/test.ori/number"));
163
167
  assert(objectEntry.upstreams.has("dependency"));
164
168
  });
169
+
170
+ test("generates a unique cache path for an unattached object", async () => {
171
+ const object0 = await expressionObject("test.ori/_objects/", [["a", 1]]);
172
+ assert.equal(object0[cachePathSymbol], "test.ori/_objects/0/");
173
+ const object1 = await expressionObject("test.ori/_objects/", [["a", 1]]);
174
+ assert.equal(object1[cachePathSymbol], "test.ori/_objects/1/");
175
+ });
165
176
  });
@@ -4,11 +4,9 @@ import { describe, test } from "node:test";
4
4
  import * as handlers from "../../src/handlers/handlers.js";
5
5
  import handleExtension from "../../src/runtime/handleExtension.js";
6
6
  import OrigamiFileMap from "../../src/runtime/OrigamiFileMap.js";
7
- import { cachePathSymbol } from "../../src/runtime/symbols.js";
8
7
 
9
8
  const fixturesUrl = new URL("fixtures/unpack", import.meta.url);
10
9
  const fixtureFiles = new OrigamiFileMap(fixturesUrl);
11
- fixtureFiles[cachePathSymbol] = "fixtures";
12
10
  fixtureFiles.globals = handlers;
13
11
 
14
12
  describe("handleExtension", () => {
@@ -56,7 +56,7 @@ describe("ops", () => {
56
56
 
57
57
  test("ops.cache", async () => {
58
58
  const fn = () => 1;
59
- const code = createCode([ops.object, ["a", [ops.getter, [fn]]]]);
59
+ const code = createCode([ops.object, null, ["a", [ops.getter, [fn]]]]);
60
60
  const result = await ops.cache("a.ori/_refs/b.ori/", code, {});
61
61
  assert.deepEqual(await Tree.plain(result), { a: 1 });
62
62
  const cachedResult = await systemCache.get("a.ori/_refs/b.ori/");
@@ -333,15 +333,16 @@ describe("ops", () => {
333
333
  const code = createCode([
334
334
  [
335
335
  ops.object,
336
+ null,
336
337
  ["a", [ops.literal, 1]],
337
338
  ["c", [[ops.inherited, 0], "a"]],
338
339
  [
339
340
  "_result",
340
341
  [
341
342
  ops.merge,
342
- [ops.object, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
343
+ [ops.object, null, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
343
344
  [[ops.scope], "more"],
344
- [ops.object, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
345
+ [ops.object, null, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
345
346
  ],
346
347
  ],
347
348
  ],
@@ -390,6 +391,7 @@ describe("ops", () => {
390
391
 
391
392
  const code = createCode([
392
393
  ops.object,
394
+ null,
393
395
  ["hello", [[[ops.scope], "upper"], "hello"]],
394
396
  ["world", [[[ops.scope], "upper"], "world"]],
395
397
  ]);
@@ -2,12 +2,35 @@ import { SyncMap } from "@weborigami/async-tree";
2
2
  import SyncCacheTransform from "../../src/runtime/SyncCacheTransform.js";
3
3
 
4
4
  export default function syncCalcs(iterable) {
5
- const data = new (SyncCacheTransform(SyncMap))(iterable);
6
- const calcs = new (SyncCacheTransform(SyncResultsMap))(data);
5
+ const data = new (SyncCacheTransform(SyncDataMap))(new Map(iterable));
6
+ const calcs = new (SyncCacheTransform(SyncCalcsMap))(data);
7
7
  return { calcs, data };
8
8
  }
9
9
 
10
- class SyncResultsMap extends SyncMap {
10
+ class SyncDataMap extends SyncMap {
11
+ constructor(source) {
12
+ super();
13
+ this.source = source;
14
+ }
15
+
16
+ delete(key) {
17
+ return this.source.delete(key);
18
+ }
19
+
20
+ get(key) {
21
+ return this.source.get(key);
22
+ }
23
+
24
+ keys() {
25
+ return this.source.keys();
26
+ }
27
+
28
+ set(key, value) {
29
+ return this.source.set(key, value);
30
+ }
31
+ }
32
+
33
+ class SyncCalcsMap extends SyncMap {
11
34
  constructor(source) {
12
35
  super();
13
36
  this.source = source;
@@ -48,12 +48,16 @@ describe("systemCache", () => {
48
48
 
49
49
  // Add new data.json to src folder, overriding the one in project root
50
50
  src.set("data.json", "2");
51
+ // Manually trigger cache invalidation
52
+ src.onValueChange("data.json");
51
53
 
52
54
  const value2 = await site.value;
53
55
  assert.equal(value2, 2);
54
56
 
55
57
  // Delete data.json from src folder, reverting to the one in project root
56
58
  src.delete("data.json");
59
+ // Manually trigger cache invalidation
60
+ src.onValueChange("data.json");
57
61
 
58
62
  const value3 = await site.value;
59
63
  assert.equal(value3, 1);