@suss/resolution 0.32.0 → 0.33.1

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
@@ -21,7 +21,7 @@ and a handler factory in TypeScript.
21
21
  ## More
22
22
 
23
23
  - [How the rules follow a value](./DESIGN.md)
24
- - [Documentation](https://nimbuscloud-ai.github.io/suss/)
24
+ - [Documentation](https://suss.sh/)
25
25
  - [Source and issues](https://github.com/nimbuscloud-ai/suss)
26
26
 
27
27
  ![coverage](../../.github/badges/coverage-resolution.svg)
package/dist/index.cjs CHANGED
@@ -80,7 +80,7 @@ var ASKING_RELATIONS = [
80
80
  "wanted",
81
81
  "wantedUnder",
82
82
  "wantedSites",
83
- "wantedEnvSite",
83
+ "wantedEnvObject",
84
84
  "wantedOrigin",
85
85
  "wantedCallOrigin",
86
86
  "wantedExportsOf",
@@ -1120,6 +1120,16 @@ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
1120
1120
  }
1121
1121
  return answers;
1122
1122
  }
1123
+ function answersFor2(db, relation, key) {
1124
+ const rows = db.lookup(relation, 0, key);
1125
+ if (rows.length === 0) {
1126
+ return [];
1127
+ }
1128
+ const placeholders = new Set(
1129
+ rows.map((row) => String(row[1])).filter((answer) => db.has("placeholderValue", [answer]))
1130
+ );
1131
+ return answersByKey(rows, placeholders).get(key) ?? [];
1132
+ }
1123
1133
  function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1124
1134
  const settled = /* @__PURE__ */ new Map();
1125
1135
  for (const [key, answers] of answersByKey(rows, placeholders)) {
@@ -1211,38 +1221,42 @@ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1211
1221
  return one;
1212
1222
  }
1213
1223
  function settledByKey(db, keys, ask, relation) {
1214
- const placeholders = placeholderValues(db);
1215
- const byKey = answersByKey(db.facts(relation), placeholders);
1216
- const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1217
- const calls = new Set(db.facts("call").map((row) => String(row[0])));
1218
- const throughCalls = [...direct.values()].flat().filter((answer) => calls.has(answer));
1224
+ const direct = new Map(
1225
+ keys.map((key) => [key, answersFor2(db, relation, key)])
1226
+ );
1227
+ const throughCalls = [...direct.values()].flat().filter((answer) => isCall(db, answer));
1219
1228
  if (throughCalls.length === 0) {
1220
1229
  return direct;
1221
1230
  }
1222
1231
  ask(throughCalls);
1223
- const deeper = singleAnswers(db.facts(relation), placeholders);
1224
1232
  return new Map(
1225
1233
  [...direct].map(([key, answers]) => [
1226
1234
  key,
1227
- collapseCalls(answers, calls, deeper)
1235
+ collapseCalls(db, relation, answers)
1228
1236
  ])
1229
1237
  );
1230
1238
  }
1231
- function collapseCalls(direct, calls, deeper) {
1239
+ function isCall(db, key) {
1240
+ return db.lookup("call", 0, key).length > 0;
1241
+ }
1242
+ function collapseCalls(db, relation, direct) {
1232
1243
  return [
1233
1244
  ...new Set(
1234
1245
  direct.map(
1235
- (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1246
+ (answer) => isCall(db, answer) ? behindCall(db, relation, answer) : answer
1236
1247
  )
1237
1248
  )
1238
1249
  ];
1239
1250
  }
1251
+ function behindCall(db, relation, call) {
1252
+ const answers = answersFor2(db, relation, call);
1253
+ return answers.length === 1 ? answers[0] : call;
1254
+ }
1240
1255
  function writesLeft(db, key) {
1241
- const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1242
- if (unstated) {
1256
+ if (db.has("writesUnstated", [key])) {
1243
1257
  return [];
1244
1258
  }
1245
- const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1259
+ const candidates = answersFor2(db, "mayHold", key);
1246
1260
  return candidates.length > 1 ? candidates : [];
1247
1261
  }
1248
1262
 
@@ -2013,6 +2027,51 @@ var STATED_RULES = [
2013
2027
  [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("p")],
2014
2028
  [(0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("y"), (0, import_datalog4.variable)("p")), (0, import_datalog4.lit)("binds", (0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("y"))]
2015
2029
  ),
2030
+ // An expression whose value is the environment object w: the way a
2031
+ // pack spells it, a name declared as that, or a parameter a caller
2032
+ // handed one of those to, however many calls deep.
2033
+ (0, import_datalog4.rule)(
2034
+ "environmentValue",
2035
+ [(0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("o")],
2036
+ [(0, import_datalog4.lit)("environmentObject", (0, import_datalog4.variable)("w")), (0, import_datalog4.lit)("refersToObject", (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("w"))]
2037
+ ),
2038
+ (0, import_datalog4.rule)(
2039
+ "environmentValue",
2040
+ [(0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("o")],
2041
+ [
2042
+ (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("a")),
2043
+ (0, import_datalog4.lit)("passesArgument", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("a")),
2044
+ (0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("p"))
2045
+ ]
2046
+ ),
2047
+ // A parameter that defaults to the environment, for the callers that
2048
+ // pass nothing. `loadConfig(env = process.env)` is written that way
2049
+ // so that most of the program never mentions the environment at all.
2050
+ (0, import_datalog4.rule)(
2051
+ "environmentValue",
2052
+ [(0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("o")],
2053
+ [
2054
+ (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("d")),
2055
+ (0, import_datalog4.lit)("paramDefault", (0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("d")),
2056
+ (0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("p"))
2057
+ ]
2058
+ ),
2059
+ // Which environment object a keyed read takes its entry from. The
2060
+ // object comes first because a project writes a handful of those and
2061
+ // thousands of keyed reads, and the join starts at the small end.
2062
+ (0, import_datalog4.rule)(
2063
+ "environmentRead",
2064
+ [(0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("x")],
2065
+ [
2066
+ (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("o")),
2067
+ (0, import_datalog4.lit)("readsKeyed", (0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("x"))
2068
+ ]
2069
+ ),
2070
+ (0, import_datalog4.rule)(
2071
+ "readsEnvNamed",
2072
+ [(0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("x")],
2073
+ [(0, import_datalog4.lit)("environmentRead", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("x"))]
2074
+ ),
2016
2075
  // A parameter whose value is an environment variable's name: a read
2017
2076
  // site takes its name from it, or it is handed on to a parameter that
2018
2077
  // does. Asked from the site, so the recursion runs callee to caller.
@@ -2176,10 +2235,25 @@ var STATED_RULES = [
2176
2235
  (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("c"))
2177
2236
  ]
2178
2237
  ),
2179
- // The expressions that refer to an object literal by binding alone: the
2238
+ // A name bound to a factory's result, called: `const get =
2239
+ // makeReader(env)` then `get(name)`. Written from the factory's call so
2240
+ // the walk starts where `callsFunction` already has an answer.
2241
+ (0, import_datalog4.rule)(
2242
+ "callsFunction",
2243
+ [(0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("f")],
2244
+ [
2245
+ (0, import_datalog4.lit)("returnsValue", (0, import_datalog4.variable)("g"), (0, import_datalog4.variable)("f")),
2246
+ (0, import_datalog4.lit)("callsFunction", (0, import_datalog4.variable)("r0"), (0, import_datalog4.variable)("g")),
2247
+ (0, import_datalog4.lit)("callsNamed", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("r0"))
2248
+ ]
2249
+ ),
2250
+ // The expressions that refer to an object by binding alone: the
2180
2251
  // declaration written as it, a reference or import of that, and a
2181
2252
  // fallback over any of those. Asked from the object, so it visits only them.
2182
2253
  (0, import_datalog4.rule)("refersToObject", [(0, import_datalog4.variable)("obj"), (0, import_datalog4.variable)("obj")], [(0, import_datalog4.lit)("objectValue", (0, import_datalog4.variable)("obj"))]),
2254
+ // The process environment is an object nothing declares, so a pack
2255
+ // saying which expression spells it is the only way in.
2256
+ (0, import_datalog4.rule)("refersToObject", [(0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("w")], [(0, import_datalog4.lit)("environmentObject", (0, import_datalog4.variable)("w"))]),
2183
2257
  (0, import_datalog4.rule)(
2184
2258
  "refersToObject",
2185
2259
  [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("obj")],
@@ -2569,12 +2643,56 @@ var RESOLUTION_QUESTIONS = [
2569
2643
  [(0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("c")],
2570
2644
  [(0, import_datalog4.lit)("wanted", (0, import_datalog4.variable)("f")), (0, import_datalog4.lit)("returnsCall", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("c"))]
2571
2645
  ),
2572
- // Seeded with the read sites, a handful per project, so one question
2573
- // gives every parameter a reader at a call could meet.
2646
+ // Seeded with the expressions that spell the environment, a handful
2647
+ // per project. The reads cannot be the seed: one written through a
2648
+ // parameter is nowhere a scan of the source looks.
2574
2649
  (0, import_datalog4.rule)(
2575
2650
  "wantedParamNamesEnv",
2576
2651
  [(0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("site")],
2577
- [(0, import_datalog4.lit)("wantedEnvSite", (0, import_datalog4.variable)("site")), (0, import_datalog4.lit)("paramNamesEnv", (0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("site"))]
2652
+ [
2653
+ (0, import_datalog4.lit)("wantedEnvObject", (0, import_datalog4.variable)("w")),
2654
+ (0, import_datalog4.lit)("environmentRead", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("x")),
2655
+ (0, import_datalog4.lit)("paramNamesEnv", (0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("site"))
2656
+ ]
2657
+ ),
2658
+ // The parameters the environment object is handed to, from the same
2659
+ // seed, so a question about one is a lookup rather than a read of
2660
+ // every file that could call its function.
2661
+ (0, import_datalog4.rule)(
2662
+ "wantedEnvParameter",
2663
+ [(0, import_datalog4.variable)("p")],
2664
+ [
2665
+ (0, import_datalog4.lit)("wantedEnvObject", (0, import_datalog4.variable)("w")),
2666
+ (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("p")),
2667
+ (0, import_datalog4.lit)("paramNamed", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("p"))
2668
+ ]
2669
+ ),
2670
+ // The calls that hand it on, for an asker to resolve, since the
2671
+ // parameter on the other side is in a file nothing has read yet.
2672
+ (0, import_datalog4.rule)(
2673
+ "wantedEnvPassingCall",
2674
+ [(0, import_datalog4.variable)("r")],
2675
+ [
2676
+ (0, import_datalog4.lit)("wantedEnvObject", (0, import_datalog4.variable)("w")),
2677
+ (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("a")),
2678
+ (0, import_datalog4.lit)("callArg", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("k"), (0, import_datalog4.variable)("a"))
2679
+ ]
2680
+ ),
2681
+ // The same relation asked from one expression. Seeding from the
2682
+ // objects instead would settle it for every expression in the
2683
+ // project, and a caller holding an argument wants only that one.
2684
+ (0, import_datalog4.rule)(
2685
+ "wantedEnvironmentValue",
2686
+ [(0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("w")],
2687
+ [(0, import_datalog4.lit)("wanted", (0, import_datalog4.variable)("o")), (0, import_datalog4.lit)("environmentValue", (0, import_datalog4.variable)("w"), (0, import_datalog4.variable)("o"))]
2688
+ ),
2689
+ // Whether an expression is a parameter under another name, for a
2690
+ // caller deciding whether going and reading that parameter's callers
2691
+ // could change the answer it already has.
2692
+ (0, import_datalog4.rule)(
2693
+ "wantedRefersToParam",
2694
+ [(0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("p")],
2695
+ [(0, import_datalog4.lit)("wanted", (0, import_datalog4.variable)("o")), (0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("p"))]
2578
2696
  ),
2579
2697
  (0, import_datalog4.rule)(
2580
2698
  "wantedComesFrom",
@@ -2792,6 +2910,19 @@ var RESOLUTION_QUESTIONS = [
2792
2910
  [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
2793
2911
  [(0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")), (0, import_datalog4.lit)("declaresName", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("n"))]
2794
2912
  ),
2913
+ // The method behind each callback the chain registers, by the event
2914
+ // that runs it. A base registering one reaches every class below it,
2915
+ // and the method may be declared anywhere along the same chain.
2916
+ (0, import_datalog4.rule)(
2917
+ "wantedCallbackMethod",
2918
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("event"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held")],
2919
+ [
2920
+ (0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")),
2921
+ (0, import_datalog4.lit)("classCallback", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("event"), (0, import_datalog4.variable)("n")),
2922
+ (0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b2")),
2923
+ (0, import_datalog4.lit)("holdsProperty", (0, import_datalog4.variable)("b2"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held"))
2924
+ ]
2925
+ ),
2795
2926
  (0, import_datalog4.rule)("anchorChain", [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("x")], [(0, import_datalog4.lit)("wantedAnchor", (0, import_datalog4.variable)("x"))]),
2796
2927
  (0, import_datalog4.rule)(
2797
2928
  "anchorChain",
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var ASKING_RELATIONS = [
13
13
  "wanted",
14
14
  "wantedUnder",
15
15
  "wantedSites",
16
- "wantedEnvSite",
16
+ "wantedEnvObject",
17
17
  "wantedOrigin",
18
18
  "wantedCallOrigin",
19
19
  "wantedExportsOf",
@@ -1053,6 +1053,16 @@ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
1053
1053
  }
1054
1054
  return answers;
1055
1055
  }
1056
+ function answersFor2(db, relation, key) {
1057
+ const rows = db.lookup(relation, 0, key);
1058
+ if (rows.length === 0) {
1059
+ return [];
1060
+ }
1061
+ const placeholders = new Set(
1062
+ rows.map((row) => String(row[1])).filter((answer) => db.has("placeholderValue", [answer]))
1063
+ );
1064
+ return answersByKey(rows, placeholders).get(key) ?? [];
1065
+ }
1056
1066
  function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1057
1067
  const settled = /* @__PURE__ */ new Map();
1058
1068
  for (const [key, answers] of answersByKey(rows, placeholders)) {
@@ -1144,38 +1154,42 @@ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1144
1154
  return one;
1145
1155
  }
1146
1156
  function settledByKey(db, keys, ask, relation) {
1147
- const placeholders = placeholderValues(db);
1148
- const byKey = answersByKey(db.facts(relation), placeholders);
1149
- const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1150
- const calls = new Set(db.facts("call").map((row) => String(row[0])));
1151
- const throughCalls = [...direct.values()].flat().filter((answer) => calls.has(answer));
1157
+ const direct = new Map(
1158
+ keys.map((key) => [key, answersFor2(db, relation, key)])
1159
+ );
1160
+ const throughCalls = [...direct.values()].flat().filter((answer) => isCall(db, answer));
1152
1161
  if (throughCalls.length === 0) {
1153
1162
  return direct;
1154
1163
  }
1155
1164
  ask(throughCalls);
1156
- const deeper = singleAnswers(db.facts(relation), placeholders);
1157
1165
  return new Map(
1158
1166
  [...direct].map(([key, answers]) => [
1159
1167
  key,
1160
- collapseCalls(answers, calls, deeper)
1168
+ collapseCalls(db, relation, answers)
1161
1169
  ])
1162
1170
  );
1163
1171
  }
1164
- function collapseCalls(direct, calls, deeper) {
1172
+ function isCall(db, key) {
1173
+ return db.lookup("call", 0, key).length > 0;
1174
+ }
1175
+ function collapseCalls(db, relation, direct) {
1165
1176
  return [
1166
1177
  ...new Set(
1167
1178
  direct.map(
1168
- (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1179
+ (answer) => isCall(db, answer) ? behindCall(db, relation, answer) : answer
1169
1180
  )
1170
1181
  )
1171
1182
  ];
1172
1183
  }
1184
+ function behindCall(db, relation, call) {
1185
+ const answers = answersFor2(db, relation, call);
1186
+ return answers.length === 1 ? answers[0] : call;
1187
+ }
1173
1188
  function writesLeft(db, key) {
1174
- const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1175
- if (unstated) {
1189
+ if (db.has("writesUnstated", [key])) {
1176
1190
  return [];
1177
1191
  }
1178
- const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1192
+ const candidates = answersFor2(db, "mayHold", key);
1179
1193
  return candidates.length > 1 ? candidates : [];
1180
1194
  }
1181
1195
 
@@ -1946,6 +1960,51 @@ var STATED_RULES = [
1946
1960
  [v("x"), v("p")],
1947
1961
  [lit("refersToParam", v("y"), v("p")), lit("binds", v("x"), v("y"))]
1948
1962
  ),
1963
+ // An expression whose value is the environment object w: the way a
1964
+ // pack spells it, a name declared as that, or a parameter a caller
1965
+ // handed one of those to, however many calls deep.
1966
+ rule(
1967
+ "environmentValue",
1968
+ [v("w"), v("o")],
1969
+ [lit("environmentObject", v("w")), lit("refersToObject", v("o"), v("w"))]
1970
+ ),
1971
+ rule(
1972
+ "environmentValue",
1973
+ [v("w"), v("o")],
1974
+ [
1975
+ lit("environmentValue", v("w"), v("a")),
1976
+ lit("passesArgument", v("r"), v("p"), v("a")),
1977
+ lit("refersToParam", v("o"), v("p"))
1978
+ ]
1979
+ ),
1980
+ // A parameter that defaults to the environment, for the callers that
1981
+ // pass nothing. `loadConfig(env = process.env)` is written that way
1982
+ // so that most of the program never mentions the environment at all.
1983
+ rule(
1984
+ "environmentValue",
1985
+ [v("w"), v("o")],
1986
+ [
1987
+ lit("environmentValue", v("w"), v("d")),
1988
+ lit("paramDefault", v("p"), v("d")),
1989
+ lit("refersToParam", v("o"), v("p"))
1990
+ ]
1991
+ ),
1992
+ // Which environment object a keyed read takes its entry from. The
1993
+ // object comes first because a project writes a handful of those and
1994
+ // thousands of keyed reads, and the join starts at the small end.
1995
+ rule(
1996
+ "environmentRead",
1997
+ [v("w"), v("site"), v("x")],
1998
+ [
1999
+ lit("environmentValue", v("w"), v("o")),
2000
+ lit("readsKeyed", v("site"), v("o"), v("x"))
2001
+ ]
2002
+ ),
2003
+ rule(
2004
+ "readsEnvNamed",
2005
+ [v("site"), v("x")],
2006
+ [lit("environmentRead", v("w"), v("site"), v("x"))]
2007
+ ),
1949
2008
  // A parameter whose value is an environment variable's name: a read
1950
2009
  // site takes its name from it, or it is handed on to a parameter that
1951
2010
  // does. Asked from the site, so the recursion runs callee to caller.
@@ -2109,10 +2168,25 @@ var STATED_RULES = [
2109
2168
  lit("call", v("r"), v("c"))
2110
2169
  ]
2111
2170
  ),
2112
- // The expressions that refer to an object literal by binding alone: the
2171
+ // A name bound to a factory's result, called: `const get =
2172
+ // makeReader(env)` then `get(name)`. Written from the factory's call so
2173
+ // the walk starts where `callsFunction` already has an answer.
2174
+ rule(
2175
+ "callsFunction",
2176
+ [v("r"), v("f")],
2177
+ [
2178
+ lit("returnsValue", v("g"), v("f")),
2179
+ lit("callsFunction", v("r0"), v("g")),
2180
+ lit("callsNamed", v("r"), v("r0"))
2181
+ ]
2182
+ ),
2183
+ // The expressions that refer to an object by binding alone: the
2113
2184
  // declaration written as it, a reference or import of that, and a
2114
2185
  // fallback over any of those. Asked from the object, so it visits only them.
2115
2186
  rule("refersToObject", [v("obj"), v("obj")], [lit("objectValue", v("obj"))]),
2187
+ // The process environment is an object nothing declares, so a pack
2188
+ // saying which expression spells it is the only way in.
2189
+ rule("refersToObject", [v("w"), v("w")], [lit("environmentObject", v("w"))]),
2116
2190
  rule(
2117
2191
  "refersToObject",
2118
2192
  [v("x"), v("obj")],
@@ -2502,12 +2576,56 @@ var RESOLUTION_QUESTIONS = [
2502
2576
  [v("f"), v("c")],
2503
2577
  [lit("wanted", v("f")), lit("returnsCall", v("f"), v("c"))]
2504
2578
  ),
2505
- // Seeded with the read sites, a handful per project, so one question
2506
- // gives every parameter a reader at a call could meet.
2579
+ // Seeded with the expressions that spell the environment, a handful
2580
+ // per project. The reads cannot be the seed: one written through a
2581
+ // parameter is nowhere a scan of the source looks.
2507
2582
  rule(
2508
2583
  "wantedParamNamesEnv",
2509
2584
  [v("p"), v("site")],
2510
- [lit("wantedEnvSite", v("site")), lit("paramNamesEnv", v("p"), v("site"))]
2585
+ [
2586
+ lit("wantedEnvObject", v("w")),
2587
+ lit("environmentRead", v("w"), v("site"), v("x")),
2588
+ lit("paramNamesEnv", v("p"), v("site"))
2589
+ ]
2590
+ ),
2591
+ // The parameters the environment object is handed to, from the same
2592
+ // seed, so a question about one is a lookup rather than a read of
2593
+ // every file that could call its function.
2594
+ rule(
2595
+ "wantedEnvParameter",
2596
+ [v("p")],
2597
+ [
2598
+ lit("wantedEnvObject", v("w")),
2599
+ lit("environmentValue", v("w"), v("p")),
2600
+ lit("paramNamed", v("f"), v("n"), v("p"))
2601
+ ]
2602
+ ),
2603
+ // The calls that hand it on, for an asker to resolve, since the
2604
+ // parameter on the other side is in a file nothing has read yet.
2605
+ rule(
2606
+ "wantedEnvPassingCall",
2607
+ [v("r")],
2608
+ [
2609
+ lit("wantedEnvObject", v("w")),
2610
+ lit("environmentValue", v("w"), v("a")),
2611
+ lit("callArg", v("r"), v("k"), v("a"))
2612
+ ]
2613
+ ),
2614
+ // The same relation asked from one expression. Seeding from the
2615
+ // objects instead would settle it for every expression in the
2616
+ // project, and a caller holding an argument wants only that one.
2617
+ rule(
2618
+ "wantedEnvironmentValue",
2619
+ [v("o"), v("w")],
2620
+ [lit("wanted", v("o")), lit("environmentValue", v("w"), v("o"))]
2621
+ ),
2622
+ // Whether an expression is a parameter under another name, for a
2623
+ // caller deciding whether going and reading that parameter's callers
2624
+ // could change the answer it already has.
2625
+ rule(
2626
+ "wantedRefersToParam",
2627
+ [v("o"), v("p")],
2628
+ [lit("wanted", v("o")), lit("refersToParam", v("o"), v("p"))]
2511
2629
  ),
2512
2630
  rule(
2513
2631
  "wantedComesFrom",
@@ -2725,6 +2843,19 @@ var RESOLUTION_QUESTIONS = [
2725
2843
  [v("c"), v("n")],
2726
2844
  [lit("ancestryChain", v("c"), v("b")), lit("declaresName", v("b"), v("n"))]
2727
2845
  ),
2846
+ // The method behind each callback the chain registers, by the event
2847
+ // that runs it. A base registering one reaches every class below it,
2848
+ // and the method may be declared anywhere along the same chain.
2849
+ rule(
2850
+ "wantedCallbackMethod",
2851
+ [v("c"), v("event"), v("n"), v("held")],
2852
+ [
2853
+ lit("ancestryChain", v("c"), v("b")),
2854
+ lit("classCallback", v("b"), v("event"), v("n")),
2855
+ lit("ancestryChain", v("c"), v("b2")),
2856
+ lit("holdsProperty", v("b2"), v("n"), v("held"))
2857
+ ]
2858
+ ),
2728
2859
  rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
2729
2860
  rule(
2730
2861
  "anchorChain",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/resolution",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "type": "module",
5
5
  "description": "The rules suss uses to follow a name in TypeScript, Python or Ruby to the function it comes down to.",
6
6
  "keywords": [
@@ -56,6 +56,6 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@suss/datalog": "0.32.0"
59
+ "@suss/datalog": "0.33.1"
60
60
  }
61
61
  }