@fraczak/k 7.1.0 → 8.0.0

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 (64) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/DOCS/BIG_PICTURE.md +54 -7
  3. package/DOCS/CODECS.md +47 -2
  4. package/DOCS/CODEC_ARCHITECTURE.md +14 -5
  5. package/DOCS/DICTIONARY.md +13 -4
  6. package/DOCS/FILE_FORMATS.md +3 -2
  7. package/DOCS/JSON_APPROACHES_COMPARISON.md +306 -0
  8. package/DOCS/NAMED_PATTERNS.md +317 -0
  9. package/DOCS/PATTERNS.md +59 -11
  10. package/DOCS/REPL.md +24 -5
  11. package/DOCS/WEB_REPL_REQUIREMENTS.md +4 -2
  12. package/Examples/arithmetics.k +22 -17
  13. package/Examples/bnat.k +21 -11
  14. package/Examples/buda.k +37 -20
  15. package/Examples/byte.k +3 -1
  16. package/Examples/code.k +38 -39
  17. package/Examples/djson-btrie.k +414 -0
  18. package/Examples/djson-ptrie.k +663 -0
  19. package/Examples/djson.k +323 -0
  20. package/Examples/esc.k +1 -1
  21. package/Examples/karatsuba-mult.k +16 -13
  22. package/Examples/list.k +8 -4
  23. package/Examples/nat.k +17 -11
  24. package/Examples/poly.k +11 -3
  25. package/Examples/vhdl_to_k.k +43 -31
  26. package/README.md +42 -15
  27. package/Value.mjs +28 -3
  28. package/backend-api.mjs +5 -1
  29. package/browser/repl-browser.mjs +6 -0
  30. package/browser/tree-view.mjs +82 -24
  31. package/browser/vfs-data.mjs +21 -12
  32. package/codecs/README.md +3 -0
  33. package/codecs/djson-btrie.mjs +560 -0
  34. package/codecs/djson-ptrie.mjs +587 -0
  35. package/codecs/djson.mjs +270 -0
  36. package/codecs/int.mjs +2 -1
  37. package/codecs/json.mjs +7 -2
  38. package/codecs/k-parse.mjs +7 -1
  39. package/codecs/k-print.mjs +4 -0
  40. package/codecs/runtime/codec-sdk.mjs +16 -2
  41. package/codecs/runtime/pattern-json.mjs +80 -1
  42. package/codecs/runtime/prefix-codec.mjs +629 -67
  43. package/codecs/runtime/show-value.mjs +93 -51
  44. package/codecs/runtime/unicode-string.mjs +29 -6
  45. package/codecs/show.mjs +4 -0
  46. package/codecs/utf16.mjs +2 -1
  47. package/core.k +79 -115
  48. package/index.mjs +3 -0
  49. package/named-patterns.mjs +267 -0
  50. package/object.mjs +31 -3
  51. package/package.json +5 -1
  52. package/parser.jison +68 -5
  53. package/parser.mjs +111 -60
  54. package/pretty.mjs +5 -0
  55. package/repl.mjs +245 -28
  56. package/scripts/build-repl-html.mjs +76 -5
  57. package/scripts/perf-djson.mjs +1142 -0
  58. package/scripts/run-tests.mjs +5 -0
  59. package/symbol-table.mjs +20 -2
  60. package/tests/bench-djson-tries.mjs +340 -0
  61. package/tests/test-codecs.mjs +211 -0
  62. package/tests/test-named-patterns.mjs +189 -0
  63. package/tests/test-repl-html.mjs +52 -4
  64. package/tests/test-repl.mjs +223 -0
@@ -6,7 +6,11 @@ function pLabel(label) {
6
6
  return nameRE.test(label) ? ` ${label}` : ` ${JSON.stringify(label)}`;
7
7
  }
8
8
 
9
- function propertyListToFilter(propertyList, varPrefix = "X") {
9
+ function propertyListToFilter(
10
+ propertyList,
11
+ varPrefix = "X",
12
+ limit = Infinity
13
+ ) {
10
14
  if (!propertyList || propertyList.length === 0) return "(...)";
11
15
 
12
16
  const refCount = new Array(propertyList.length).fill(0);
@@ -22,9 +26,15 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
22
26
  }
23
27
 
24
28
  const defined = new Set();
29
+ let curLen = 0;
25
30
 
26
31
  function fmt(nodeId) {
27
- if (varNames.has(nodeId) && defined.has(nodeId)) return varNames.get(nodeId);
32
+ if (curLen >= limit) return "...";
33
+ if (varNames.has(nodeId) && defined.has(nodeId)) {
34
+ const name = varNames.get(nodeId);
35
+ curLen += name.length;
36
+ return name;
37
+ }
28
38
 
29
39
  const [kind, edges] = propertyList[nodeId];
30
40
  const hasVar = varNames.has(nodeId);
@@ -32,16 +42,35 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
32
42
 
33
43
  const suffix = hasVar ? `=${varNames.get(nodeId)}` : "";
34
44
 
35
- if (kind === "any") return hasVar ? varNames.get(nodeId) : "(...)";
45
+ if (kind === "any") {
46
+ const s = (hasVar ? varNames.get(nodeId) : "(...)") + suffix;
47
+ curLen += s.length;
48
+ return s;
49
+ }
36
50
 
37
51
  const isOpen = kind.startsWith("open-");
38
52
  const isProduct = kind.endsWith("product");
39
53
  const open = isProduct ? "{" : "<";
40
54
  const close = isProduct ? "}" : ">";
41
55
 
42
- if (edges.length === 0 && !isOpen) return `${open}${close}${suffix}`;
56
+ if (edges.length === 0 && !isOpen) {
57
+ const s = `${open}${close}${suffix}`;
58
+ curLen += s.length;
59
+ return s;
60
+ }
43
61
 
44
- const fields = edges.map(([label, target]) => `${fmt(target)}${pLabel(label)}`);
62
+ const fields = [];
63
+ for (let i = 0; i < edges.length; i++) {
64
+ if (curLen >= limit) {
65
+ fields.push("...");
66
+ break;
67
+ }
68
+ const [label, target] = edges[i];
69
+ const targetStr = fmt(target);
70
+ const labelStr = pLabel(label);
71
+ fields.push(`${targetStr}${labelStr}`);
72
+ curLen += targetStr.length + labelStr.length + 2;
73
+ }
45
74
  if (isOpen) fields.push("...");
46
75
  return `${open}${fields.join(", ")}${close}${suffix}`;
47
76
  }
@@ -49,81 +78,94 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
49
78
  return fmt(0);
50
79
  }
51
80
 
52
- function valueToK(root) {
53
- let result = "";
54
- const stack = [{
55
- val: root,
56
- assign(s) { result = s; }
57
- }];
81
+ function valueToK(root, limit = Infinity) {
82
+ const out = [];
83
+ let curLen = 0;
84
+ const stack = [root];
58
85
 
59
86
  while (stack.length > 0) {
60
- const frame = stack.pop();
61
- if (frame.finishProduct) {
62
- frame.assign(`{${frame.parts.join(", ")}}`);
87
+ if (curLen >= limit) {
88
+ out.push("...");
89
+ break;
90
+ }
91
+ const item = stack.pop();
92
+ if (typeof item === "string") {
93
+ if (curLen + item.length > limit) {
94
+ out.push(item.slice(0, Math.max(0, limit - curLen)));
95
+ out.push("...");
96
+ break;
97
+ }
98
+ out.push(item);
99
+ curLen += item.length;
63
100
  continue;
64
101
  }
65
- if (frame.finishVariant) {
66
- const base = frame.base;
67
- const tags = frame.tags;
68
- frame.assign(tags.length === 0 ? base : `${base}|${tags.reverse().join("|")}`);
102
+
103
+ if (item == null) {
104
+ const s = String(item);
105
+ if (curLen + s.length > limit) {
106
+ out.push(s.slice(0, Math.max(0, limit - curLen)));
107
+ out.push("...");
108
+ break;
109
+ }
110
+ out.push(s);
111
+ curLen += s.length;
69
112
  continue;
70
113
  }
71
114
 
72
- const v = frame.val;
73
- if (isVariant(v)) {
115
+ if (isVariant(item)) {
74
116
  const tags = [];
75
- let curr = v;
117
+ let curr = item;
76
118
  while (isVariant(curr)) {
77
119
  tags.push(pLabel(curr.tag).trimStart());
78
120
  curr = curr.value;
121
+ if (tags.length > limit) break;
79
122
  }
80
- const finishFrame = {
81
- finishVariant: true,
82
- tags,
83
- base: "",
84
- assign: frame.assign
85
- };
86
- stack.push(finishFrame);
87
- stack.push({
88
- val: curr,
89
- assign(s) { finishFrame.base = s; }
90
- });
123
+ if (tags.length > 0) {
124
+ stack.push("|" + tags.reverse().join("|"));
125
+ }
126
+ stack.push(curr);
91
127
  continue;
92
128
  }
93
129
 
94
- if (isProduct(v)) {
95
- const keys = Object.keys(v.product);
130
+ if (isProduct(item)) {
131
+ const keys = Object.keys(item.product);
96
132
  if (keys.length === 0) {
97
- frame.assign("{}");
133
+ out.push("{}");
134
+ curLen += 2;
98
135
  continue;
99
136
  }
100
- const parts = new Array(keys.length);
101
- const finishFrame = {
102
- finishProduct: true,
103
- parts,
104
- assign: frame.assign
105
- };
106
- stack.push(finishFrame);
137
+ out.push("{");
138
+ curLen += 1;
139
+ stack.push("}");
107
140
  for (let i = keys.length - 1; i >= 0; i--) {
108
141
  const k = keys[i];
109
- const label = pLabel(k);
110
- stack.push({
111
- val: v.product[k],
112
- assign(s) { parts[i] = `${s}${label}`; }
113
- });
142
+ if (i < keys.length - 1) {
143
+ stack.push(", ");
144
+ }
145
+ stack.push(pLabel(k));
146
+ stack.push(item.product[k]);
114
147
  }
115
148
  continue;
116
149
  }
117
150
 
118
- frame.assign(String(v));
151
+ const s = String(item);
152
+ if (curLen + s.length > limit) {
153
+ out.push(s.slice(0, Math.max(0, limit - curLen)));
154
+ out.push("...");
155
+ break;
156
+ }
157
+ out.push(s);
158
+ curLen += s.length;
119
159
  }
120
160
 
121
- return result;
161
+ return out.join("");
122
162
  }
123
163
 
124
- function valueWithEnvelopeToK(value) {
164
+ function valueWithEnvelopeToK(value, limit = Infinity) {
125
165
  if (value === undefined) return "... undefined";
126
- return `${valueToK(value)} ?${propertyListToFilter(value.pattern)}`;
166
+ const valStr = valueToK(value, limit);
167
+ const patStr = propertyListToFilter(value.pattern, "X", limit);
168
+ return `${valStr} ?${patStr}`;
127
169
  }
128
170
 
129
171
  export { propertyListToFilter, valueToK, valueWithEnvelopeToK };
@@ -3,6 +3,15 @@ import { Value, isProduct, isVariant } from "../../Value.mjs";
3
3
  const UNIT = Value.product({});
4
4
  const BIT0 = Value.variant("0", UNIT);
5
5
  const BIT1 = Value.variant("1", UNIT);
6
+ const BIT_KEYS = [
7
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
8
+ ];
9
+
10
+ let lazyStringDecoder = null;
11
+
12
+ function setLazyStringDecoder(fn) {
13
+ lazyStringDecoder = fn;
14
+ }
6
15
 
7
16
  function bitValue(bit) {
8
17
  return bit === 0 ? BIT0 : BIT1;
@@ -25,7 +34,8 @@ function requireVariant(value, where) {
25
34
  function bitsProduct(width, n) {
26
35
  const product = {};
27
36
  for (let i = width - 1; i >= 0; i--) {
28
- product[String(i)] = bitValue((n >> i) & 1);
37
+ const key = i < BIT_KEYS.length ? BIT_KEYS[i] : String(i);
38
+ product[key] = bitValue((n >> i) & 1);
29
39
  }
30
40
  return Value.product(product);
31
41
  }
@@ -34,7 +44,8 @@ function parseBitsProduct(value, maxBit, where) {
34
44
  const product = requireProduct(value, where);
35
45
  let n = 0;
36
46
  for (let i = maxBit; i >= 0; i--) {
37
- const entry = requireVariant(product[String(i)], `${where}.bit${i}`);
47
+ const key = i < BIT_KEYS.length ? BIT_KEYS[i] : String(i);
48
+ const entry = requireVariant(product[key], `${where}.bit${i}`);
38
49
  if (entry.tag !== "0" && entry.tag !== "1") {
39
50
  throw new Error(`${where}.bit${i}: expected tag 0 or 1`);
40
51
  }
@@ -194,20 +205,31 @@ function textToStringValue(text) {
194
205
  }
195
206
 
196
207
  function stringValueToText(value) {
197
- let out = "";
208
+ if (lazyStringDecoder) {
209
+ const fast = lazyStringDecoder(value);
210
+ if (fast !== null) return fast;
211
+ }
212
+
213
+ const chunks = [];
214
+ let chunk = "";
198
215
  let cursor = value;
199
216
 
200
217
  while (true) {
201
218
  const list = requireVariant(cursor, "string");
202
219
  if (list.tag === "nil") {
203
220
  requireProduct(list.value, "string.nil");
204
- return out;
221
+ if (chunk.length > 0) chunks.push(chunk);
222
+ return chunks.join("");
205
223
  }
206
224
  if (list.tag !== "cons") {
207
225
  throw new Error(`Unsupported string list tag: ${list.tag}`);
208
226
  }
209
227
  const pair = requireProduct(list.value, "string.cons");
210
- out += String.fromCodePoint(unicodeValueToCodePoint(pair.car));
228
+ chunk += String.fromCodePoint(unicodeValueToCodePoint(pair.car));
229
+ if (chunk.length >= 8192) {
230
+ chunks.push(chunk);
231
+ chunk = "";
232
+ }
211
233
  cursor = pair.cdr;
212
234
  }
213
235
  }
@@ -216,5 +238,6 @@ export {
216
238
  codePointToUnicodeValue,
217
239
  unicodeValueToCodePoint,
218
240
  textToStringValue,
219
- stringValueToText
241
+ stringValueToText,
242
+ setLazyStringDecoder
220
243
  };
package/codecs/show.mjs CHANGED
@@ -51,6 +51,10 @@ async function main() {
51
51
  else { usage(); process.exit(1); }
52
52
  }
53
53
 
54
+ stdout.on("error", (err) => {
55
+ if (err.code === "EPIPE") process.exit(0);
56
+ });
57
+
54
58
  const buffer = fileArg ? fs.readFileSync(fileArg) : await readAll(stdin);
55
59
 
56
60
  // forward unchanged to stdout
package/codecs/utf16.mjs CHANGED
@@ -10,7 +10,8 @@ import {
10
10
  export const doc = `
11
11
  UTF-16 text codec (BOM-aware).
12
12
 
13
- Reads UTF-16 text (LE/BE with BOM or LE without BOM), outputs UTF-16LE with BOM.
13
+ Reads UTF-16 text (LE/BE with BOM or LE without BOM),
14
+ outputs UTF-16LE with BOM.
14
15
  `;
15
16
 
16
17
  function decodeUtf16Input(buf) {
package/core.k CHANGED
@@ -9,9 +9,11 @@
9
9
  -- §1. Bits
10
10
  ---------------------------------------------------------------
11
11
 
12
- bit? = ?< {} 0, {} 1 >;
12
+ bit[] = < {} 0, {} 1 >;
13
+ bits[] = < {} _, B 0, B 1 > = B;
13
14
 
14
- bits? = ?< {} _, bits? 0, bits? 1 > = bits?;
15
+ bit? = ? bit[];
16
+ bits? = ? bits[];
15
17
 
16
18
  _ = {}|_;
17
19
 
@@ -22,52 +24,66 @@ _ = {}|_;
22
24
  -- §2. Unicode and String
23
25
  ---------------------------------------------------------------
24
26
 
25
- string? = ? <
26
- {} nil,
27
- {
28
- <
29
- {
30
- < {} 0, {} 1 >=Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6
31
- }=Bits7 ascii,
32
- {
33
- <
34
- { Bit 0, Bit 1, Bit 2 }=Bits3 h08_0F,
35
- Bits7 h10_7F,
36
- Bits7 h80_CF,
37
- Bits3 hD0_D7,
38
- {} hF9,
39
- { Bit 0 }=Bits1 hFA_FB,
40
- Bits1 hFC_FD,
41
- Bits1 hFE_FF
42
- > hi,
43
- { Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 }=Bits8 lo
44
- } bmp_common,
45
- {
46
- <
47
- { Bit 0, Bit 1, Bit 2, Bit 3 } hE0_EF,
48
- Bits3 hF0_F7,
49
- {} hF8
50
- > hi,
51
- Bits8 lo
52
- } bmp_private_use,
53
- {
54
- Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7, Bit 8, Bit 9, Bit 10
55
- } plane0,
56
- { Bits8 mid, Bits8 lo } supplementary_plane1,
57
- {
58
- <
59
- Bits1 p02_03,
60
- { Bit 0, Bit 1 } p04_07,
61
- Bits3 p08_0F,
62
- {} p10
63
- > plane,
64
- Bits8 mid,
65
- Bits8 lo
66
- } supplementary_planes2_16
67
- > car,
68
- String cdr
69
- } cons
70
- > = String;
27
+ -- 8-bit octet used as a building block for UTF-8 payload pieces.
28
+ byte[] = {
29
+ bit[] 0, bit[] 1, bit[] 2, bit[] 3,
30
+ bit[] 4, bit[] 5, bit[] 6, bit[] 7
31
+ };
32
+
33
+ -- 7-bit ASCII code point payload (U+0000..U+007F).
34
+ ascii[] = {
35
+ bit[] 0, bit[] 1, bit[] 2, bit[] 3,
36
+ bit[] 4, bit[] 5, bit[] 6
37
+ };
38
+
39
+ -- High-byte classes for common BMP ranges, excluding surrogate
40
+ -- and private-use partitions.
41
+ bmp_hi_common[] = <
42
+ { bit[] 0, bit[] 1, bit[] 2 } h08_0F,
43
+ ascii[] h10_7F,
44
+ ascii[] h80_CF,
45
+ { bit[] 0, bit[] 1, bit[] 2 } hD0_D7,
46
+ {} hF9,
47
+ { bit[] 0 } hFA_FB,
48
+ { bit[] 0 } hFC_FD,
49
+ { bit[] 0 } hFE_FF
50
+ >;
51
+
52
+ -- High-byte classes for BMP private-use/high-reserved slices.
53
+ bmp_hi_private_use[] = <
54
+ { bit[] 0, bit[] 1, bit[] 2, bit[] 3 } hE0_EF,
55
+ { bit[] 0, bit[] 1, bit[] 2 } hF0_F7,
56
+ {} hF8
57
+ >;
58
+
59
+ -- 11-bit payload used for Plane 0 code points U+0080..U+07FF.
60
+ plane0[] = {
61
+ bit[] 0, bit[] 1, bit[] 2, bit[] 3, bit[] 4,
62
+ bit[] 5, bit[] 6, bit[] 7, bit[] 8, bit[] 9, bit[] 10
63
+ };
64
+
65
+ -- Plane selector for supplementary code points in Planes 2..16.
66
+ plane_02_10[] = <
67
+ { bit[] 0 } p02_03,
68
+ { bit[] 0, bit[] 1 } p04_07,
69
+ { bit[] 0, bit[] 1, bit[] 2 } p08_0F,
70
+ {} p10
71
+ >;
72
+
73
+ -- Canonical Unicode scalar-value partition used by string encoding.
74
+ unicode[] = <
75
+ ascii[] ascii,
76
+ { bmp_hi_common[] hi, byte[] lo } bmp_common,
77
+ { bmp_hi_private_use[] hi, byte[] lo } bmp_private_use,
78
+ plane0[] plane0,
79
+ { byte[] mid, byte[] lo } supplementary_plane1,
80
+ { plane_02_10[] plane, byte[] mid, byte[] lo } supplementary_planes2_16
81
+ >;
82
+
83
+ -- Linked-list string: nil or (unicode car, string cdr).
84
+ string[] = < {} nil, { unicode[] car, S cdr } cons > = S;
85
+
86
+ string? = ? string[];
71
87
 
72
88
  ---------------------------------------------------------------
73
89
  -- §3. Pattern
@@ -82,72 +98,20 @@ string? = ? <
82
98
  -- to decode any value, first decode a pattern under that constant,
83
99
  -- then decode the value under the pattern just obtained.
84
100
 
85
- pattern? = ? <
86
- {} nil,
87
- {
88
- <
89
- {} any,
90
- <
91
- {} nil,
92
- {
93
- {
94
- <
95
- {} nil,
96
- {
97
- <
98
- {
99
- < {} 0, {} 1 >=Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6
100
- }=Bits7 ascii,
101
- {
102
- <
103
- { Bit 0, Bit 1, Bit 2 }=Bits3 h08_0F,
104
- Bits7 h10_7F,
105
- Bits7 h80_CF,
106
- Bits3 hD0_D7,
107
- {} hF9,
108
- { Bit 0 }=Bits1 hFA_FB,
109
- Bits1 hFC_FD,
110
- Bits1 hFE_FF
111
- > hi,
112
- { Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 }=Bits8 lo
113
- } bmp_common,
114
- {
115
- <
116
- { Bit 0, Bit 1, Bit 2, Bit 3 } hE0_EF,
117
- Bits3 hF0_F7,
118
- {} hF8
119
- > hi,
120
- Bits8 lo
121
- } bmp_private_use,
122
- {
123
- Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7, Bit 8, Bit 9, Bit 10
124
- } plane0,
125
- { Bits8 mid, Bits8 lo } supplementary_plane1,
126
- {
127
- <
128
- Bits1 p02_03,
129
- { Bit 0, Bit 1 } p04_07,
130
- Bits3 p08_0F,
131
- {} p10
132
- > plane,
133
- Bits8 mid,
134
- Bits8 lo
135
- } supplementary_planes2_16
136
- > car,
137
- String cdr
138
- } cons
139
- > = String label,
140
- < Bits 0, Bits 1, {} _ > = Bits target
141
- } car,
142
- Edges cdr
143
- } cons
144
- > = Edges open-product,
145
- Edges open-union,
146
- Edges closed-product,
147
- Edges closed-union
148
- > car,
149
- Pattern cdr
150
- } cons
151
- > = Pattern;
101
+ edge[] = { string[] label, bits[] target };
102
+
103
+ edges[] = < {} nil, { edge[] car, E cdr } cons > = E;
104
+
105
+ node[] = <
106
+ {} any,
107
+ edges[] open-product,
108
+ edges[] open-union,
109
+ edges[] closed-product,
110
+ edges[] closed-union
111
+ >;
112
+
113
+ pattern[] = < {} nil, { node[] car, P cdr } cons > = P;
114
+
115
+ pattern? = ? pattern[];
152
116
 
153
117
  pattern?
package/index.mjs CHANGED
@@ -2,6 +2,7 @@ import { parse } from "./parser.mjs";
2
2
  import { fromObject, isValue } from "./Value.mjs";
3
3
  import { parseValue } from "./valueIO.mjs";
4
4
  import { compileTypes } from "./compiler.mjs";
5
+ import { expandPatternsInRels } from "./named-patterns.mjs";
5
6
  import run from "./run.mjs";
6
7
  import codes from "./codes.mjs";
7
8
 
@@ -37,6 +38,8 @@ function annotate(script, options = {}) {
37
38
  const representatives = codes.register(defs.codes);
38
39
  const rels = {...defs.rels, "__main__": {def: exp}};
39
40
 
41
+ expandPatternsInRels(rels, defs.patterns || {}, options);
42
+
40
43
  // Inject library relations into the rels table so @hash refs resolve
41
44
  if (options.libraries) {
42
45
  for (const lib of options.libraries) {