@suss/resolution 0.19.0 → 0.20.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.
package/README.md CHANGED
@@ -173,6 +173,33 @@ than a copy of every other. It differs from `callsFunction`, which
173
173
  starts from the function because a caller asking for call sites has the
174
174
  function in hand.
175
175
 
176
+ ## The anchor behind a receiver
177
+
178
+ A pack sometimes wants a call handed back, not a yes or no. Mongoose is
179
+ the picture: `model("User", schema)` is the anchor, and the pack reads
180
+ the model name and the collection off that call's own arguments. The
181
+ receiver in front of a matched method can be the model, a construction
182
+ of it (`new User({...})`), or a document a query returned
183
+ (`await User.findById(id)`), and each is a different number of hops
184
+ from the anchor.
185
+
186
+ `anchorChain` is the reachability that covers all three. From an asked
187
+ value (`wantedAnchor`) it follows names (`binds`, `endsHolding`,
188
+ `fallbackBranch`), imports through the export table, a call to its
189
+ callee, and a method's callee to its receiver. Every call the chain
190
+ passes lands in `wantedAnchorCall`, keyed by the asked value.
191
+
192
+ Candidates, not one answer: the chain has no way to rank a nearer call
193
+ above a farther one, and ranking inside the rules would be a depth
194
+ bound in a new spelling. The asking side filters the candidates against
195
+ its own origin (which module, which callee name) and applies the
196
+ single-answer policy: exactly one distinct match is the anchor, none or
197
+ several is a refusal.
198
+
199
+ The demand cone stays on the base facts named above. Nothing here pulls
200
+ `reaches` or `callsInto`, so asking about a receiver does not price in
201
+ call-graph closure.
202
+
176
203
  ## Explaining an answer
177
204
 
178
205
  Evaluate the same rules under `@suss/datalog`'s `witnesses` algebra and
package/dist/index.cjs CHANGED
@@ -22,12 +22,17 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  ANSWER_RELATIONS: () => ANSWER_RELATIONS,
24
24
  FACT_CONTRACT_CASES: () => FACT_CONTRACT_CASES,
25
+ NAMESPACE_IMPORT: () => NAMESPACE_IMPORT,
26
+ NAMESPACE_IMPORT_NAME: () => NAMESPACE_IMPORT_NAME,
25
27
  RESOLUTION_QUESTIONS: () => RESOLUTION_QUESTIONS,
26
28
  RESOLUTION_RULES: () => RESOLUTION_RULES,
27
29
  RESULT_STEP: () => RESULT_STEP,
28
30
  VALUE_STEP: () => VALUE_STEP,
31
+ agreedMountPrefix: () => agreedMountPrefix,
29
32
  checkFactContract: () => checkFactContract,
30
33
  explainResolutionProof: () => explainResolutionProof,
34
+ joinMountedPath: () => joinMountedPath,
35
+ mountPathsOf: () => mountPathsOf,
31
36
  renderExplanation: () => renderExplanation
32
37
  });
33
38
  module.exports = __toCommonJS(index_exports);
@@ -461,9 +466,61 @@ function renderExplanation(explanation, describe) {
461
466
  return lines;
462
467
  }
463
468
 
469
+ // src/mount.ts
470
+ function joinMountedPath(prefix, path) {
471
+ const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
472
+ return trimmed === "" ? path : trimmed + path;
473
+ }
474
+ function mountPathsOf(edges, childId) {
475
+ return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
476
+ }
477
+ function agreedMountPrefix(edges, childId) {
478
+ const paths = mountPathsOf(edges, childId);
479
+ if (paths === null) {
480
+ return null;
481
+ }
482
+ const distinct2 = new Set(paths);
483
+ const only = [...distinct2][0];
484
+ return distinct2.size === 1 && only !== void 0 ? only : null;
485
+ }
486
+ function pathsOf(edges, memo, childId, visiting) {
487
+ const cached = memo.get(childId);
488
+ if (cached !== void 0) {
489
+ return cached;
490
+ }
491
+ if (visiting.has(childId)) {
492
+ memo.set(childId, null);
493
+ return null;
494
+ }
495
+ const own = edges.get(childId);
496
+ if (own === void 0 || own.length === 0) {
497
+ memo.set(childId, [""]);
498
+ return [""];
499
+ }
500
+ visiting.add(childId);
501
+ const composed = /* @__PURE__ */ new Set();
502
+ let unstated = false;
503
+ for (const edge of own) {
504
+ const parentPaths = pathsOf(edges, memo, edge.parentId, visiting);
505
+ if (parentPaths === null) {
506
+ unstated = true;
507
+ break;
508
+ }
509
+ for (const parentPath of parentPaths) {
510
+ composed.add(joinMountedPath(parentPath, edge.prefix));
511
+ }
512
+ }
513
+ visiting.delete(childId);
514
+ const result = unstated ? null : [...composed];
515
+ memo.set(childId, result);
516
+ return result;
517
+ }
518
+
464
519
  // src/index.ts
465
520
  var import_datalog2 = require("@suss/datalog");
466
521
  var VALUE_STEP = (0, import_datalog2.constant)("value");
522
+ var NAMESPACE_IMPORT_NAME = "*";
523
+ var NAMESPACE_IMPORT = (0, import_datalog2.constant)(NAMESPACE_IMPORT_NAME);
467
524
  var RESULT_STEP = (0, import_datalog2.constant)("result");
468
525
  var RESOLUTION_RULES = [
469
526
  // Aliasing: const x = y, or an identifier referencing a declaration.
@@ -683,6 +740,21 @@ var RESOLUTION_RULES = [
683
740
  (0, import_datalog2.lit)("callKeywordArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("a"))
684
741
  ]
685
742
  ),
743
+ // Which module's export a re-exported name forwards to, however
744
+ // many barrels deep the forwarding runs.
745
+ (0, import_datalog2.rule)(
746
+ "moduleForwards",
747
+ [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
748
+ [(0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))]
749
+ ),
750
+ (0, import_datalog2.rule)(
751
+ "moduleForwards",
752
+ [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3")],
753
+ [
754
+ (0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")),
755
+ (0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3"))
756
+ ]
757
+ ),
686
758
  // What a module exports: directly, or through re-export chains.
687
759
  (0, import_datalog2.rule)(
688
760
  "moduleExport",
@@ -862,15 +934,9 @@ var RESOLUTION_RULES = [
862
934
  (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
863
935
  ]
864
936
  ),
865
- // Calling f ends up calling the name n that module m exports. One hop
866
- // is f's own body, including the closures it declares; the other hop
867
- // is a function f's body calls, so a wrapper of a wrapper answers
868
- // with what the innermost one reaches.
869
- //
870
- // Several answers for one function is the normal case, not an
871
- // ambiguity: a project decorator that composes two library decorators
872
- // applies both. A caller asks whether a particular (m, n) is among
873
- // them.
937
+ // Calling f ends up calling the name n that module m exports, one
938
+ // hop through f's own body or deeper through a wrapper of a wrapper.
939
+ // Several results is normal: a composed decorator applies them all.
874
940
  (0, import_datalog2.rule)(
875
941
  "callsInto",
876
942
  [(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
@@ -938,6 +1004,155 @@ var RESOLUTION_QUESTIONS = [
938
1004
  [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
939
1005
  [(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
940
1006
  ),
1007
+ // Call-origin questions, for attribution, in their own demand
1008
+ // class. Attribution stops at the import declaration, so nothing
1009
+ // here demands reaches or callsInto. The chains are closures with
1010
+ // no depth bound: an alias run of any length resolves.
1011
+ (0, import_datalog2.rule)("callOriginChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedCallOrigin", (0, import_datalog2.variable)("x"))]),
1012
+ (0, import_datalog2.rule)(
1013
+ "callOriginChain",
1014
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1015
+ [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1016
+ ),
1017
+ (0, import_datalog2.rule)(
1018
+ "callOriginChain",
1019
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1020
+ [
1021
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1022
+ (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1023
+ ]
1024
+ ),
1025
+ (0, import_datalog2.rule)(
1026
+ "callOriginChain",
1027
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1028
+ [
1029
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1030
+ (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1031
+ ]
1032
+ ),
1033
+ // The chain with at least one hop taken, which is what tells a
1034
+ // destructured binding apart from asking about a node directly.
1035
+ (0, import_datalog2.rule)(
1036
+ "callOriginChainStepped",
1037
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1038
+ [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1039
+ ),
1040
+ (0, import_datalog2.rule)(
1041
+ "callOriginChainStepped",
1042
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1043
+ [
1044
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1045
+ (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1046
+ ]
1047
+ ),
1048
+ (0, import_datalog2.rule)(
1049
+ "callOriginChainStepped",
1050
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1051
+ [
1052
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1053
+ (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1054
+ ]
1055
+ ),
1056
+ (0, import_datalog2.rule)(
1057
+ "wantedCallOriginPair",
1058
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1059
+ [
1060
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1061
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1062
+ ]
1063
+ ),
1064
+ // An import of a project barrel is an import of what the barrel
1065
+ // forwards, so the pair surfaces the module behind the chain.
1066
+ (0, import_datalog2.rule)(
1067
+ "wantedCallOriginPair",
1068
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
1069
+ [
1070
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1071
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1072
+ (0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))
1073
+ ]
1074
+ ),
1075
+ // A namespace member: the module's export of the member's own name.
1076
+ // The imports literal leads, since it is the smallest relation.
1077
+ (0, import_datalog2.rule)(
1078
+ "wantedCallOriginPair",
1079
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
1080
+ [
1081
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
1082
+ (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
1083
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
1084
+ ]
1085
+ ),
1086
+ (0, import_datalog2.rule)(
1087
+ "wantedCallOriginPair",
1088
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
1089
+ [
1090
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("d"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
1091
+ (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("d")),
1092
+ (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
1093
+ (0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
1094
+ ]
1095
+ ),
1096
+ // What a call made: through the call to its callee, whose own alias
1097
+ // chain leads to the import.
1098
+ (0, import_datalog2.rule)(
1099
+ "callMadeChain",
1100
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f")],
1101
+ [(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
1102
+ ),
1103
+ (0, import_datalog2.rule)(
1104
+ "callMadeChain",
1105
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1106
+ [(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1107
+ ),
1108
+ (0, import_datalog2.rule)(
1109
+ "callMadeChain",
1110
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1111
+ [(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1112
+ ),
1113
+ (0, import_datalog2.rule)(
1114
+ "wantedCallOriginPair",
1115
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
1116
+ [
1117
+ (0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1118
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1119
+ ]
1120
+ ),
1121
+ // A member destructured off what a call made keeps the member name
1122
+ // as one more path segment.
1123
+ (0, import_datalog2.rule)(
1124
+ "callMemberChain",
1125
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("p")],
1126
+ [
1127
+ (0, import_datalog2.lit)("callOriginChainStepped", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("e")),
1128
+ (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("e"), (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("p")),
1129
+ (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))
1130
+ ]
1131
+ ),
1132
+ (0, import_datalog2.rule)(
1133
+ "callMemberChain",
1134
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
1135
+ [
1136
+ (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1137
+ (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1138
+ ]
1139
+ ),
1140
+ (0, import_datalog2.rule)(
1141
+ "callMemberChain",
1142
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
1143
+ [
1144
+ (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1145
+ (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
1146
+ ]
1147
+ ),
1148
+ (0, import_datalog2.rule)(
1149
+ "wantedCallOriginMember",
1150
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("p")],
1151
+ [
1152
+ (0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
1153
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
1154
+ ]
1155
+ ),
941
1156
  (0, import_datalog2.rule)(
942
1157
  "wantedCallsInto",
943
1158
  [(0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
@@ -946,6 +1161,81 @@ var RESOLUTION_QUESTIONS = [
946
1161
  (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("g")),
947
1162
  (0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
948
1163
  ]
1164
+ ),
1165
+ // The export table of one module, keyed by the module asked about.
1166
+ // The moduleExport rules flatten re-export chains of any length, and
1167
+ // the demand cone stays on export facts alone.
1168
+ (0, import_datalog2.rule)(
1169
+ "wantedModuleExport",
1170
+ [(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
1171
+ [
1172
+ (0, import_datalog2.lit)("wantedExportsOf", (0, import_datalog2.variable)("m")),
1173
+ (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
1174
+ ]
1175
+ ),
1176
+ // The calls behind a receiver, for a pack that wants the anchor a
1177
+ // chain hangs off; the README's anchor section says which hops and
1178
+ // why the asking side applies the single-answer policy.
1179
+ // A class's ancestry, one hop per extends through the binding that
1180
+ // says which class the written base is; the names on the way out
1181
+ // are what a storage pack matches its library's bases against.
1182
+ (0, import_datalog2.rule)("ancestryChain", [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("c")], [(0, import_datalog2.lit)("wantedAncestry", (0, import_datalog2.variable)("c"))]),
1183
+ (0, import_datalog2.rule)(
1184
+ "ancestryChain",
1185
+ [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b2")],
1186
+ [
1187
+ (0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")),
1188
+ (0, import_datalog2.lit)("extends", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("x")),
1189
+ (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b2"))
1190
+ ]
1191
+ ),
1192
+ (0, import_datalog2.rule)(
1193
+ "wantedBaseName",
1194
+ [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("n")],
1195
+ [(0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")), (0, import_datalog2.lit)("extendsNamed", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("n"))]
1196
+ ),
1197
+ (0, import_datalog2.rule)("anchorChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedAnchor", (0, import_datalog2.variable)("x"))]),
1198
+ (0, import_datalog2.rule)(
1199
+ "anchorChain",
1200
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1201
+ [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1202
+ ),
1203
+ (0, import_datalog2.rule)(
1204
+ "anchorChain",
1205
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1206
+ [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1207
+ ),
1208
+ (0, import_datalog2.rule)(
1209
+ "anchorChain",
1210
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1211
+ [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
1212
+ ),
1213
+ (0, import_datalog2.rule)(
1214
+ "anchorChain",
1215
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
1216
+ [
1217
+ (0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1218
+ (0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
1219
+ (0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("z"))
1220
+ ]
1221
+ ),
1222
+ (0, import_datalog2.rule)(
1223
+ "anchorChain",
1224
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")],
1225
+ [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
1226
+ ),
1227
+ (0, import_datalog2.rule)(
1228
+ "anchorChain",
1229
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("o")],
1230
+ [
1231
+ (0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
1232
+ (0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"))
1233
+ ]
1234
+ ),
1235
+ (0, import_datalog2.rule)(
1236
+ "wantedAnchorCall",
1237
+ [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
1238
+ [(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
949
1239
  )
950
1240
  ];
951
1241
  var ANSWER_RELATIONS = [
@@ -955,11 +1245,16 @@ var ANSWER_RELATIONS = [
955
1245
  0 && (module.exports = {
956
1246
  ANSWER_RELATIONS,
957
1247
  FACT_CONTRACT_CASES,
1248
+ NAMESPACE_IMPORT,
1249
+ NAMESPACE_IMPORT_NAME,
958
1250
  RESOLUTION_QUESTIONS,
959
1251
  RESOLUTION_RULES,
960
1252
  RESULT_STEP,
961
1253
  VALUE_STEP,
1254
+ agreedMountPrefix,
962
1255
  checkFactContract,
963
1256
  explainResolutionProof,
1257
+ joinMountedPath,
1258
+ mountPathsOf,
964
1259
  renderExplanation
965
1260
  });
package/dist/index.d.cts CHANGED
@@ -105,8 +105,59 @@ declare function explainResolutionProof(proof: Proof, options: ExplainOptions):
105
105
  */
106
106
  declare function renderExplanation(explanation: ResolutionExplanation, describe: DescribeAtom): string[];
107
107
 
108
+ /**
109
+ * Mount composition, shared between adapters. An adapter states one
110
+ * fact per mount call, `mounted(child, parent, prefix)`, however its
111
+ * language spells the call: Express's `app.use(prefix, router)`,
112
+ * FastAPI's `app.include_router(router, prefix=...)`. This module
113
+ * composes those edges into the full prefix a child's routes are
114
+ * served under, so both adapters compute the same paths.
115
+ *
116
+ * The composition is a function over the edge set rather than a
117
+ * datalog rule because a rule head cannot build a new atom, and a
118
+ * path-concatenating closure would not terminate on a mount cycle:
119
+ * the string grows on every round. Here a cycle is an abstention.
120
+ */
121
+ interface MountEdge {
122
+ parentId: string;
123
+ prefix: string;
124
+ }
125
+ /** Every mount edge, keyed by the mounted child. */
126
+ type MountEdges = ReadonlyMap<string, readonly MountEdge[]>;
127
+ /**
128
+ * Join a mount prefix onto a path written with its own leading slash.
129
+ * A root prefix strips to nothing, so mounting under "/" leaves the
130
+ * path as written.
131
+ */
132
+ declare function joinMountedPath(prefix: string, path: string): string;
133
+ /**
134
+ * Every full prefix the child's mount chains compose to, one per
135
+ * mount, which is what one-boundary-per-mount reads. Null when a
136
+ * chain cannot be stated: a cycle, or an ancestor whose own paths
137
+ * cannot be stated. An unmounted child composes to [""].
138
+ */
139
+ declare function mountPathsOf(edges: MountEdges, childId: string): readonly string[] | null;
140
+ /**
141
+ * The one prefix every mount chain agrees on. "" for an unmounted
142
+ * child, null when a cycle or a disagreement leaves nothing to say.
143
+ *
144
+ * Agreement is checked on the composed result, not the literal prefix
145
+ * a mount call gives: two mounts spelling the identical local prefix
146
+ * still land at different full paths when one parent is itself
147
+ * mounted somewhere the other is not.
148
+ */
149
+ declare function agreedMountPrefix(edges: MountEdges, childId: string): string | null;
150
+
108
151
  /** A step to the value x is written as. */
109
152
  declare const VALUE_STEP: _suss_datalog.Term;
153
+ /**
154
+ * The name a whole-module import records itself under, whatever the
155
+ * language spells it as: `import * as ns` in TypeScript, `import
156
+ * module` in Python. Adapters emit this name; the member-read rule
157
+ * joins on it.
158
+ */
159
+ declare const NAMESPACE_IMPORT_NAME = "*";
160
+ declare const NAMESPACE_IMPORT: _suss_datalog.Term;
110
161
  /** A step to what running the call x is handed back. */
111
162
  declare const RESULT_STEP: _suss_datalog.Term;
112
163
  /**
@@ -145,4 +196,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
145
196
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
146
197
  declare const ANSWER_RELATIONS: string[];
147
198
 
148
- export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, checkFactContract, explainResolutionProof, renderExplanation };
199
+ export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, agreedMountPrefix, checkFactContract, explainResolutionProof, joinMountedPath, mountPathsOf, renderExplanation };
package/dist/index.d.ts CHANGED
@@ -105,8 +105,59 @@ declare function explainResolutionProof(proof: Proof, options: ExplainOptions):
105
105
  */
106
106
  declare function renderExplanation(explanation: ResolutionExplanation, describe: DescribeAtom): string[];
107
107
 
108
+ /**
109
+ * Mount composition, shared between adapters. An adapter states one
110
+ * fact per mount call, `mounted(child, parent, prefix)`, however its
111
+ * language spells the call: Express's `app.use(prefix, router)`,
112
+ * FastAPI's `app.include_router(router, prefix=...)`. This module
113
+ * composes those edges into the full prefix a child's routes are
114
+ * served under, so both adapters compute the same paths.
115
+ *
116
+ * The composition is a function over the edge set rather than a
117
+ * datalog rule because a rule head cannot build a new atom, and a
118
+ * path-concatenating closure would not terminate on a mount cycle:
119
+ * the string grows on every round. Here a cycle is an abstention.
120
+ */
121
+ interface MountEdge {
122
+ parentId: string;
123
+ prefix: string;
124
+ }
125
+ /** Every mount edge, keyed by the mounted child. */
126
+ type MountEdges = ReadonlyMap<string, readonly MountEdge[]>;
127
+ /**
128
+ * Join a mount prefix onto a path written with its own leading slash.
129
+ * A root prefix strips to nothing, so mounting under "/" leaves the
130
+ * path as written.
131
+ */
132
+ declare function joinMountedPath(prefix: string, path: string): string;
133
+ /**
134
+ * Every full prefix the child's mount chains compose to, one per
135
+ * mount, which is what one-boundary-per-mount reads. Null when a
136
+ * chain cannot be stated: a cycle, or an ancestor whose own paths
137
+ * cannot be stated. An unmounted child composes to [""].
138
+ */
139
+ declare function mountPathsOf(edges: MountEdges, childId: string): readonly string[] | null;
140
+ /**
141
+ * The one prefix every mount chain agrees on. "" for an unmounted
142
+ * child, null when a cycle or a disagreement leaves nothing to say.
143
+ *
144
+ * Agreement is checked on the composed result, not the literal prefix
145
+ * a mount call gives: two mounts spelling the identical local prefix
146
+ * still land at different full paths when one parent is itself
147
+ * mounted somewhere the other is not.
148
+ */
149
+ declare function agreedMountPrefix(edges: MountEdges, childId: string): string | null;
150
+
108
151
  /** A step to the value x is written as. */
109
152
  declare const VALUE_STEP: _suss_datalog.Term;
153
+ /**
154
+ * The name a whole-module import records itself under, whatever the
155
+ * language spells it as: `import * as ns` in TypeScript, `import
156
+ * module` in Python. Adapters emit this name; the member-read rule
157
+ * joins on it.
158
+ */
159
+ declare const NAMESPACE_IMPORT_NAME = "*";
160
+ declare const NAMESPACE_IMPORT: _suss_datalog.Term;
110
161
  /** A step to what running the call x is handed back. */
111
162
  declare const RESULT_STEP: _suss_datalog.Term;
112
163
  /**
@@ -145,4 +196,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
145
196
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
146
197
  declare const ANSWER_RELATIONS: string[];
147
198
 
148
- export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, checkFactContract, explainResolutionProof, renderExplanation };
199
+ export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, agreedMountPrefix, checkFactContract, explainResolutionProof, joinMountedPath, mountPathsOf, renderExplanation };
package/dist/index.js CHANGED
@@ -427,9 +427,61 @@ function renderExplanation(explanation, describe) {
427
427
  return lines;
428
428
  }
429
429
 
430
+ // src/mount.ts
431
+ function joinMountedPath(prefix, path) {
432
+ const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
433
+ return trimmed === "" ? path : trimmed + path;
434
+ }
435
+ function mountPathsOf(edges, childId) {
436
+ return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
437
+ }
438
+ function agreedMountPrefix(edges, childId) {
439
+ const paths = mountPathsOf(edges, childId);
440
+ if (paths === null) {
441
+ return null;
442
+ }
443
+ const distinct2 = new Set(paths);
444
+ const only = [...distinct2][0];
445
+ return distinct2.size === 1 && only !== void 0 ? only : null;
446
+ }
447
+ function pathsOf(edges, memo, childId, visiting) {
448
+ const cached = memo.get(childId);
449
+ if (cached !== void 0) {
450
+ return cached;
451
+ }
452
+ if (visiting.has(childId)) {
453
+ memo.set(childId, null);
454
+ return null;
455
+ }
456
+ const own = edges.get(childId);
457
+ if (own === void 0 || own.length === 0) {
458
+ memo.set(childId, [""]);
459
+ return [""];
460
+ }
461
+ visiting.add(childId);
462
+ const composed = /* @__PURE__ */ new Set();
463
+ let unstated = false;
464
+ for (const edge of own) {
465
+ const parentPaths = pathsOf(edges, memo, edge.parentId, visiting);
466
+ if (parentPaths === null) {
467
+ unstated = true;
468
+ break;
469
+ }
470
+ for (const parentPath of parentPaths) {
471
+ composed.add(joinMountedPath(parentPath, edge.prefix));
472
+ }
473
+ }
474
+ visiting.delete(childId);
475
+ const result = unstated ? null : [...composed];
476
+ memo.set(childId, result);
477
+ return result;
478
+ }
479
+
430
480
  // src/index.ts
431
481
  import { constant, lit, rule, variable as v } from "@suss/datalog";
432
482
  var VALUE_STEP = constant("value");
483
+ var NAMESPACE_IMPORT_NAME = "*";
484
+ var NAMESPACE_IMPORT = constant(NAMESPACE_IMPORT_NAME);
433
485
  var RESULT_STEP = constant("result");
434
486
  var RESOLUTION_RULES = [
435
487
  // Aliasing: const x = y, or an identifier referencing a declaration.
@@ -649,6 +701,21 @@ var RESOLUTION_RULES = [
649
701
  lit("callKeywordArg", v("r"), v("n"), v("a"))
650
702
  ]
651
703
  ),
704
+ // Which module's export a re-exported name forwards to, however
705
+ // many barrels deep the forwarding runs.
706
+ rule(
707
+ "moduleForwards",
708
+ [v("m"), v("n"), v("m2"), v("n2")],
709
+ [lit("reExports", v("m"), v("n"), v("m2"), v("n2"))]
710
+ ),
711
+ rule(
712
+ "moduleForwards",
713
+ [v("m"), v("n"), v("m3"), v("n3")],
714
+ [
715
+ lit("reExports", v("m"), v("n"), v("m2"), v("n2")),
716
+ lit("moduleForwards", v("m2"), v("n2"), v("m3"), v("n3"))
717
+ ]
718
+ ),
652
719
  // What a module exports: directly, or through re-export chains.
653
720
  rule(
654
721
  "moduleExport",
@@ -828,15 +895,9 @@ var RESOLUTION_RULES = [
828
895
  lit("imports", v("y"), v("m"), v("n"))
829
896
  ]
830
897
  ),
831
- // Calling f ends up calling the name n that module m exports. One hop
832
- // is f's own body, including the closures it declares; the other hop
833
- // is a function f's body calls, so a wrapper of a wrapper answers
834
- // with what the innermost one reaches.
835
- //
836
- // Several answers for one function is the normal case, not an
837
- // ambiguity: a project decorator that composes two library decorators
838
- // applies both. A caller asks whether a particular (m, n) is among
839
- // them.
898
+ // Calling f ends up calling the name n that module m exports, one
899
+ // hop through f's own body or deeper through a wrapper of a wrapper.
900
+ // Several results is normal: a composed decorator applies them all.
840
901
  rule(
841
902
  "callsInto",
842
903
  [v("f"), v("m"), v("n")],
@@ -904,6 +965,155 @@ var RESOLUTION_QUESTIONS = [
904
965
  [v("x"), v("m"), v("n")],
905
966
  [lit("wantedOrigin", v("x")), lit("comesFrom", v("x"), v("m"), v("n"))]
906
967
  ),
968
+ // Call-origin questions, for attribution, in their own demand
969
+ // class. Attribution stops at the import declaration, so nothing
970
+ // here demands reaches or callsInto. The chains are closures with
971
+ // no depth bound: an alias run of any length resolves.
972
+ rule("callOriginChain", [v("x"), v("x")], [lit("wantedCallOrigin", v("x"))]),
973
+ rule(
974
+ "callOriginChain",
975
+ [v("x"), v("z")],
976
+ [lit("callOriginChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
977
+ ),
978
+ rule(
979
+ "callOriginChain",
980
+ [v("x"), v("z")],
981
+ [
982
+ lit("callOriginChain", v("x"), v("y")),
983
+ lit("endsHolding", v("y"), v("z"))
984
+ ]
985
+ ),
986
+ rule(
987
+ "callOriginChain",
988
+ [v("x"), v("z")],
989
+ [
990
+ lit("callOriginChain", v("x"), v("y")),
991
+ lit("fallbackBranch", v("y"), v("z"))
992
+ ]
993
+ ),
994
+ // The chain with at least one hop taken, which is what tells a
995
+ // destructured binding apart from asking about a node directly.
996
+ rule(
997
+ "callOriginChainStepped",
998
+ [v("x"), v("z")],
999
+ [lit("callOriginChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
1000
+ ),
1001
+ rule(
1002
+ "callOriginChainStepped",
1003
+ [v("x"), v("z")],
1004
+ [
1005
+ lit("callOriginChain", v("x"), v("y")),
1006
+ lit("endsHolding", v("y"), v("z"))
1007
+ ]
1008
+ ),
1009
+ rule(
1010
+ "callOriginChainStepped",
1011
+ [v("x"), v("z")],
1012
+ [
1013
+ lit("callOriginChain", v("x"), v("y")),
1014
+ lit("fallbackBranch", v("y"), v("z"))
1015
+ ]
1016
+ ),
1017
+ rule(
1018
+ "wantedCallOriginPair",
1019
+ [v("x"), v("m"), v("n")],
1020
+ [
1021
+ lit("callOriginChain", v("x"), v("y")),
1022
+ lit("imports", v("y"), v("m"), v("n"))
1023
+ ]
1024
+ ),
1025
+ // An import of a project barrel is an import of what the barrel
1026
+ // forwards, so the pair surfaces the module behind the chain.
1027
+ rule(
1028
+ "wantedCallOriginPair",
1029
+ [v("x"), v("m2"), v("n2")],
1030
+ [
1031
+ lit("callOriginChain", v("x"), v("y")),
1032
+ lit("imports", v("y"), v("m"), v("n")),
1033
+ lit("moduleForwards", v("m"), v("n"), v("m2"), v("n2"))
1034
+ ]
1035
+ ),
1036
+ // A namespace member: the module's export of the member's own name.
1037
+ // The imports literal leads, since it is the smallest relation.
1038
+ rule(
1039
+ "wantedCallOriginPair",
1040
+ [v("x"), v("m"), v("p")],
1041
+ [
1042
+ lit("imports", v("o"), v("m"), NAMESPACE_IMPORT),
1043
+ lit("readsProperty", v("y"), v("o"), v("p")),
1044
+ lit("callOriginChain", v("x"), v("y"))
1045
+ ]
1046
+ ),
1047
+ rule(
1048
+ "wantedCallOriginPair",
1049
+ [v("x"), v("m"), v("p")],
1050
+ [
1051
+ lit("imports", v("d"), v("m"), NAMESPACE_IMPORT),
1052
+ lit("binds", v("o"), v("d")),
1053
+ lit("readsProperty", v("y"), v("o"), v("p")),
1054
+ lit("callOriginChain", v("x"), v("y"))
1055
+ ]
1056
+ ),
1057
+ // What a call made: through the call to its callee, whose own alias
1058
+ // chain leads to the import.
1059
+ rule(
1060
+ "callMadeChain",
1061
+ [v("x"), v("f")],
1062
+ [lit("callOriginChain", v("x"), v("c")), lit("call", v("c"), v("f"))]
1063
+ ),
1064
+ rule(
1065
+ "callMadeChain",
1066
+ [v("x"), v("z")],
1067
+ [lit("callMadeChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
1068
+ ),
1069
+ rule(
1070
+ "callMadeChain",
1071
+ [v("x"), v("z")],
1072
+ [lit("callMadeChain", v("x"), v("y")), lit("endsHolding", v("y"), v("z"))]
1073
+ ),
1074
+ rule(
1075
+ "wantedCallOriginPair",
1076
+ [v("x"), v("m"), v("n")],
1077
+ [
1078
+ lit("callMadeChain", v("x"), v("y")),
1079
+ lit("imports", v("y"), v("m"), v("n"))
1080
+ ]
1081
+ ),
1082
+ // A member destructured off what a call made keeps the member name
1083
+ // as one more path segment.
1084
+ rule(
1085
+ "callMemberChain",
1086
+ [v("x"), v("f"), v("p")],
1087
+ [
1088
+ lit("callOriginChainStepped", v("x"), v("e")),
1089
+ lit("readsProperty", v("e"), v("c"), v("p")),
1090
+ lit("call", v("c"), v("f"))
1091
+ ]
1092
+ ),
1093
+ rule(
1094
+ "callMemberChain",
1095
+ [v("x"), v("z"), v("p")],
1096
+ [
1097
+ lit("callMemberChain", v("x"), v("y"), v("p")),
1098
+ lit("binds", v("y"), v("z"))
1099
+ ]
1100
+ ),
1101
+ rule(
1102
+ "callMemberChain",
1103
+ [v("x"), v("z"), v("p")],
1104
+ [
1105
+ lit("callMemberChain", v("x"), v("y"), v("p")),
1106
+ lit("endsHolding", v("y"), v("z"))
1107
+ ]
1108
+ ),
1109
+ rule(
1110
+ "wantedCallOriginMember",
1111
+ [v("x"), v("m"), v("n"), v("p")],
1112
+ [
1113
+ lit("callMemberChain", v("x"), v("y"), v("p")),
1114
+ lit("imports", v("y"), v("m"), v("n"))
1115
+ ]
1116
+ ),
907
1117
  rule(
908
1118
  "wantedCallsInto",
909
1119
  [v("g"), v("m"), v("n")],
@@ -912,6 +1122,81 @@ var RESOLUTION_QUESTIONS = [
912
1122
  lit("comesTo", v("x"), v("g")),
913
1123
  lit("callsInto", v("g"), v("m"), v("n"))
914
1124
  ]
1125
+ ),
1126
+ // The export table of one module, keyed by the module asked about.
1127
+ // The moduleExport rules flatten re-export chains of any length, and
1128
+ // the demand cone stays on export facts alone.
1129
+ rule(
1130
+ "wantedModuleExport",
1131
+ [v("m"), v("n"), v("value")],
1132
+ [
1133
+ lit("wantedExportsOf", v("m")),
1134
+ lit("moduleExport", v("m"), v("n"), v("value"))
1135
+ ]
1136
+ ),
1137
+ // The calls behind a receiver, for a pack that wants the anchor a
1138
+ // chain hangs off; the README's anchor section says which hops and
1139
+ // why the asking side applies the single-answer policy.
1140
+ // A class's ancestry, one hop per extends through the binding that
1141
+ // says which class the written base is; the names on the way out
1142
+ // are what a storage pack matches its library's bases against.
1143
+ rule("ancestryChain", [v("c"), v("c")], [lit("wantedAncestry", v("c"))]),
1144
+ rule(
1145
+ "ancestryChain",
1146
+ [v("c"), v("b2")],
1147
+ [
1148
+ lit("ancestryChain", v("c"), v("b")),
1149
+ lit("extends", v("b"), v("x")),
1150
+ lit("binds", v("x"), v("b2"))
1151
+ ]
1152
+ ),
1153
+ rule(
1154
+ "wantedBaseName",
1155
+ [v("c"), v("n")],
1156
+ [lit("ancestryChain", v("c"), v("b")), lit("extendsNamed", v("b"), v("n"))]
1157
+ ),
1158
+ rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
1159
+ rule(
1160
+ "anchorChain",
1161
+ [v("x"), v("z")],
1162
+ [lit("anchorChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
1163
+ ),
1164
+ rule(
1165
+ "anchorChain",
1166
+ [v("x"), v("z")],
1167
+ [lit("anchorChain", v("x"), v("y")), lit("endsHolding", v("y"), v("z"))]
1168
+ ),
1169
+ rule(
1170
+ "anchorChain",
1171
+ [v("x"), v("z")],
1172
+ [lit("anchorChain", v("x"), v("y")), lit("fallbackBranch", v("y"), v("z"))]
1173
+ ),
1174
+ rule(
1175
+ "anchorChain",
1176
+ [v("x"), v("z")],
1177
+ [
1178
+ lit("anchorChain", v("x"), v("y")),
1179
+ lit("imports", v("y"), v("m"), v("n")),
1180
+ lit("moduleExport", v("m"), v("n"), v("z"))
1181
+ ]
1182
+ ),
1183
+ rule(
1184
+ "anchorChain",
1185
+ [v("x"), v("c")],
1186
+ [lit("anchorChain", v("x"), v("r")), lit("call", v("r"), v("c"))]
1187
+ ),
1188
+ rule(
1189
+ "anchorChain",
1190
+ [v("x"), v("o")],
1191
+ [
1192
+ lit("anchorChain", v("x"), v("y")),
1193
+ lit("readsProperty", v("y"), v("o"), v("n"))
1194
+ ]
1195
+ ),
1196
+ rule(
1197
+ "wantedAnchorCall",
1198
+ [v("x"), v("r")],
1199
+ [lit("anchorChain", v("x"), v("r")), lit("call", v("r"), v("c"))]
915
1200
  )
916
1201
  ];
917
1202
  var ANSWER_RELATIONS = [
@@ -920,11 +1205,16 @@ var ANSWER_RELATIONS = [
920
1205
  export {
921
1206
  ANSWER_RELATIONS,
922
1207
  FACT_CONTRACT_CASES,
1208
+ NAMESPACE_IMPORT,
1209
+ NAMESPACE_IMPORT_NAME,
923
1210
  RESOLUTION_QUESTIONS,
924
1211
  RESOLUTION_RULES,
925
1212
  RESULT_STEP,
926
1213
  VALUE_STEP,
1214
+ agreedMountPrefix,
927
1215
  checkFactContract,
928
1216
  explainResolutionProof,
1217
+ joinMountedPath,
1218
+ mountPathsOf,
929
1219
  renderExplanation
930
1220
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/resolution",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Language-neutral rules for following a value to the function it comes down to.",
6
6
  "keywords": [
@@ -53,6 +53,6 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@suss/datalog": "0.19.0"
56
+ "@suss/datalog": "0.20.0"
57
57
  }
58
58
  }