@suss/resolution 0.31.2 → 0.33.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
@@ -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,6 +80,7 @@ var ASKING_RELATIONS = [
80
80
  "wanted",
81
81
  "wantedUnder",
82
82
  "wantedSites",
83
+ "wantedEnvObject",
83
84
  "wantedOrigin",
84
85
  "wantedCallOrigin",
85
86
  "wantedExportsOf",
@@ -1119,6 +1120,16 @@ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
1119
1120
  }
1120
1121
  return answers;
1121
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
+ }
1122
1133
  function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1123
1134
  const settled = /* @__PURE__ */ new Map();
1124
1135
  for (const [key, answers] of answersByKey(rows, placeholders)) {
@@ -1210,38 +1221,42 @@ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1210
1221
  return one;
1211
1222
  }
1212
1223
  function settledByKey(db, keys, ask, relation) {
1213
- const placeholders = placeholderValues(db);
1214
- const byKey = answersByKey(db.facts(relation), placeholders);
1215
- const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1216
- const calls = new Set(db.facts("call").map((row) => String(row[0])));
1217
- 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));
1218
1228
  if (throughCalls.length === 0) {
1219
1229
  return direct;
1220
1230
  }
1221
1231
  ask(throughCalls);
1222
- const deeper = singleAnswers(db.facts(relation), placeholders);
1223
1232
  return new Map(
1224
1233
  [...direct].map(([key, answers]) => [
1225
1234
  key,
1226
- collapseCalls(answers, calls, deeper)
1235
+ collapseCalls(db, relation, answers)
1227
1236
  ])
1228
1237
  );
1229
1238
  }
1230
- 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) {
1231
1243
  return [
1232
1244
  ...new Set(
1233
1245
  direct.map(
1234
- (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1246
+ (answer) => isCall(db, answer) ? behindCall(db, relation, answer) : answer
1235
1247
  )
1236
1248
  )
1237
1249
  ];
1238
1250
  }
1251
+ function behindCall(db, relation, call) {
1252
+ const answers = answersFor2(db, relation, call);
1253
+ return answers.length === 1 ? answers[0] : call;
1254
+ }
1239
1255
  function writesLeft(db, key) {
1240
- const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1241
- if (unstated) {
1256
+ if (db.has("writesUnstated", [key])) {
1242
1257
  return [];
1243
1258
  }
1244
- const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1259
+ const candidates = answersFor2(db, "mayHold", key);
1245
1260
  return candidates.length > 1 ? candidates : [];
1246
1261
  }
1247
1262
 
@@ -1994,6 +2009,89 @@ var STATED_RULES = [
1994
2009
  (0, import_datalog4.lit)("callKeywordArg", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("a"))
1995
2010
  ]
1996
2011
  ),
2012
+ // The expressions that refer to a parameter by binding alone. An
2013
+ // adapter may key a read of the parameter as the parameter itself,
2014
+ // or as its own node linked by binds; both arrive here.
2015
+ (0, import_datalog4.rule)(
2016
+ "refersToParam",
2017
+ [(0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("p")],
2018
+ [(0, import_datalog4.lit)("paramOf", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("k"), (0, import_datalog4.variable)("p"))]
2019
+ ),
2020
+ (0, import_datalog4.rule)(
2021
+ "refersToParam",
2022
+ [(0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("p")],
2023
+ [(0, import_datalog4.lit)("paramNamed", (0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("p"))]
2024
+ ),
2025
+ (0, import_datalog4.rule)(
2026
+ "refersToParam",
2027
+ [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("p")],
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"))]
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
+ ),
2075
+ // A parameter whose value is an environment variable's name: a read
2076
+ // site takes its name from it, or it is handed on to a parameter that
2077
+ // does. Asked from the site, so the recursion runs callee to caller.
2078
+ (0, import_datalog4.rule)(
2079
+ "paramNamesEnv",
2080
+ [(0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("site")],
2081
+ [
2082
+ (0, import_datalog4.lit)("readsEnvNamed", (0, import_datalog4.variable)("site"), (0, import_datalog4.variable)("x")),
2083
+ (0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("p"))
2084
+ ]
2085
+ ),
2086
+ (0, import_datalog4.rule)(
2087
+ "paramNamesEnv",
2088
+ [(0, import_datalog4.variable)("p"), (0, import_datalog4.variable)("site")],
2089
+ [
2090
+ (0, import_datalog4.lit)("refersToParam", (0, import_datalog4.variable)("a"), (0, import_datalog4.variable)("p")),
2091
+ (0, import_datalog4.lit)("passesArgument", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("q"), (0, import_datalog4.variable)("a")),
2092
+ (0, import_datalog4.lit)("paramNamesEnv", (0, import_datalog4.variable)("q"), (0, import_datalog4.variable)("site"))
2093
+ ]
2094
+ ),
1997
2095
  // Which module's export a re-exported name forwards to, however
1998
2096
  // many barrels deep the forwarding runs.
1999
2097
  (0, import_datalog4.rule)(
@@ -2137,10 +2235,25 @@ var STATED_RULES = [
2137
2235
  (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("c"))
2138
2236
  ]
2139
2237
  ),
2140
- // 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
2141
2251
  // declaration written as it, a reference or import of that, and a
2142
2252
  // fallback over any of those. Asked from the object, so it visits only them.
2143
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"))]),
2144
2257
  (0, import_datalog4.rule)(
2145
2258
  "refersToObject",
2146
2259
  [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("obj")],
@@ -2530,6 +2643,34 @@ var RESOLUTION_QUESTIONS = [
2530
2643
  [(0, import_datalog4.variable)("f"), (0, import_datalog4.variable)("c")],
2531
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"))]
2532
2645
  ),
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.
2649
+ (0, import_datalog4.rule)(
2650
+ "wantedParamNamesEnv",
2651
+ [(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 same relation asked from one expression. Seeding from the
2659
+ // objects instead would settle it for every expression in the
2660
+ // project, and a caller holding an argument wants only that one.
2661
+ (0, import_datalog4.rule)(
2662
+ "wantedEnvironmentValue",
2663
+ [(0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("w")],
2664
+ [(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"))]
2665
+ ),
2666
+ // Whether an expression is a parameter under another name, for a
2667
+ // caller deciding whether going and reading that parameter's callers
2668
+ // could change the answer it already has.
2669
+ (0, import_datalog4.rule)(
2670
+ "wantedRefersToParam",
2671
+ [(0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("p")],
2672
+ [(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"))]
2673
+ ),
2533
2674
  (0, import_datalog4.rule)(
2534
2675
  "wantedComesFrom",
2535
2676
  [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("m"), (0, import_datalog4.variable)("n")],
@@ -2746,6 +2887,19 @@ var RESOLUTION_QUESTIONS = [
2746
2887
  [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
2747
2888
  [(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"))]
2748
2889
  ),
2890
+ // The method behind each callback the chain registers, by the event
2891
+ // that runs it. A base registering one reaches every class below it,
2892
+ // and the method may be declared anywhere along the same chain.
2893
+ (0, import_datalog4.rule)(
2894
+ "wantedCallbackMethod",
2895
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("event"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held")],
2896
+ [
2897
+ (0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")),
2898
+ (0, import_datalog4.lit)("classCallback", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("event"), (0, import_datalog4.variable)("n")),
2899
+ (0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b2")),
2900
+ (0, import_datalog4.lit)("holdsProperty", (0, import_datalog4.variable)("b2"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held"))
2901
+ ]
2902
+ ),
2749
2903
  (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"))]),
2750
2904
  (0, import_datalog4.rule)(
2751
2905
  "anchorChain",
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ var ASKING_RELATIONS = [
13
13
  "wanted",
14
14
  "wantedUnder",
15
15
  "wantedSites",
16
+ "wantedEnvObject",
16
17
  "wantedOrigin",
17
18
  "wantedCallOrigin",
18
19
  "wantedExportsOf",
@@ -1052,6 +1053,16 @@ function answersByKey(rows, placeholders = NO_PLACEHOLDERS) {
1052
1053
  }
1053
1054
  return answers;
1054
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
+ }
1055
1066
  function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
1056
1067
  const settled = /* @__PURE__ */ new Map();
1057
1068
  for (const [key, answers] of answersByKey(rows, placeholders)) {
@@ -1143,38 +1154,42 @@ function writtenValuesByKey(db, keys, ask, relation = WRITTEN_AS) {
1143
1154
  return one;
1144
1155
  }
1145
1156
  function settledByKey(db, keys, ask, relation) {
1146
- const placeholders = placeholderValues(db);
1147
- const byKey = answersByKey(db.facts(relation), placeholders);
1148
- const direct = new Map(keys.map((key) => [key, byKey.get(key) ?? []]));
1149
- const calls = new Set(db.facts("call").map((row) => String(row[0])));
1150
- 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));
1151
1161
  if (throughCalls.length === 0) {
1152
1162
  return direct;
1153
1163
  }
1154
1164
  ask(throughCalls);
1155
- const deeper = singleAnswers(db.facts(relation), placeholders);
1156
1165
  return new Map(
1157
1166
  [...direct].map(([key, answers]) => [
1158
1167
  key,
1159
- collapseCalls(answers, calls, deeper)
1168
+ collapseCalls(db, relation, answers)
1160
1169
  ])
1161
1170
  );
1162
1171
  }
1163
- 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) {
1164
1176
  return [
1165
1177
  ...new Set(
1166
1178
  direct.map(
1167
- (answer) => calls.has(answer) ? deeper.get(answer) ?? answer : answer
1179
+ (answer) => isCall(db, answer) ? behindCall(db, relation, answer) : answer
1168
1180
  )
1169
1181
  )
1170
1182
  ];
1171
1183
  }
1184
+ function behindCall(db, relation, call) {
1185
+ const answers = answersFor2(db, relation, call);
1186
+ return answers.length === 1 ? answers[0] : call;
1187
+ }
1172
1188
  function writesLeft(db, key) {
1173
- const unstated = db.facts("writesUnstated").some((row) => String(row[0]) === key);
1174
- if (unstated) {
1189
+ if (db.has("writesUnstated", [key])) {
1175
1190
  return [];
1176
1191
  }
1177
- const candidates = answersByKey(db.facts("mayHold"), placeholderValues(db)).get(key) ?? [];
1192
+ const candidates = answersFor2(db, "mayHold", key);
1178
1193
  return candidates.length > 1 ? candidates : [];
1179
1194
  }
1180
1195
 
@@ -1927,6 +1942,89 @@ var STATED_RULES = [
1927
1942
  lit("callKeywordArg", v("r"), v("n"), v("a"))
1928
1943
  ]
1929
1944
  ),
1945
+ // The expressions that refer to a parameter by binding alone. An
1946
+ // adapter may key a read of the parameter as the parameter itself,
1947
+ // or as its own node linked by binds; both arrive here.
1948
+ rule(
1949
+ "refersToParam",
1950
+ [v("p"), v("p")],
1951
+ [lit("paramOf", v("f"), v("k"), v("p"))]
1952
+ ),
1953
+ rule(
1954
+ "refersToParam",
1955
+ [v("p"), v("p")],
1956
+ [lit("paramNamed", v("f"), v("n"), v("p"))]
1957
+ ),
1958
+ rule(
1959
+ "refersToParam",
1960
+ [v("x"), v("p")],
1961
+ [lit("refersToParam", v("y"), v("p")), lit("binds", v("x"), v("y"))]
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
+ ),
2008
+ // A parameter whose value is an environment variable's name: a read
2009
+ // site takes its name from it, or it is handed on to a parameter that
2010
+ // does. Asked from the site, so the recursion runs callee to caller.
2011
+ rule(
2012
+ "paramNamesEnv",
2013
+ [v("p"), v("site")],
2014
+ [
2015
+ lit("readsEnvNamed", v("site"), v("x")),
2016
+ lit("refersToParam", v("x"), v("p"))
2017
+ ]
2018
+ ),
2019
+ rule(
2020
+ "paramNamesEnv",
2021
+ [v("p"), v("site")],
2022
+ [
2023
+ lit("refersToParam", v("a"), v("p")),
2024
+ lit("passesArgument", v("r"), v("q"), v("a")),
2025
+ lit("paramNamesEnv", v("q"), v("site"))
2026
+ ]
2027
+ ),
1930
2028
  // Which module's export a re-exported name forwards to, however
1931
2029
  // many barrels deep the forwarding runs.
1932
2030
  rule(
@@ -2070,10 +2168,25 @@ var STATED_RULES = [
2070
2168
  lit("call", v("r"), v("c"))
2071
2169
  ]
2072
2170
  ),
2073
- // 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
2074
2184
  // declaration written as it, a reference or import of that, and a
2075
2185
  // fallback over any of those. Asked from the object, so it visits only them.
2076
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"))]),
2077
2190
  rule(
2078
2191
  "refersToObject",
2079
2192
  [v("x"), v("obj")],
@@ -2463,6 +2576,34 @@ var RESOLUTION_QUESTIONS = [
2463
2576
  [v("f"), v("c")],
2464
2577
  [lit("wanted", v("f")), lit("returnsCall", v("f"), v("c"))]
2465
2578
  ),
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.
2582
+ rule(
2583
+ "wantedParamNamesEnv",
2584
+ [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 same relation asked from one expression. Seeding from the
2592
+ // objects instead would settle it for every expression in the
2593
+ // project, and a caller holding an argument wants only that one.
2594
+ rule(
2595
+ "wantedEnvironmentValue",
2596
+ [v("o"), v("w")],
2597
+ [lit("wanted", v("o")), lit("environmentValue", v("w"), v("o"))]
2598
+ ),
2599
+ // Whether an expression is a parameter under another name, for a
2600
+ // caller deciding whether going and reading that parameter's callers
2601
+ // could change the answer it already has.
2602
+ rule(
2603
+ "wantedRefersToParam",
2604
+ [v("o"), v("p")],
2605
+ [lit("wanted", v("o")), lit("refersToParam", v("o"), v("p"))]
2606
+ ),
2466
2607
  rule(
2467
2608
  "wantedComesFrom",
2468
2609
  [v("x"), v("m"), v("n")],
@@ -2679,6 +2820,19 @@ var RESOLUTION_QUESTIONS = [
2679
2820
  [v("c"), v("n")],
2680
2821
  [lit("ancestryChain", v("c"), v("b")), lit("declaresName", v("b"), v("n"))]
2681
2822
  ),
2823
+ // The method behind each callback the chain registers, by the event
2824
+ // that runs it. A base registering one reaches every class below it,
2825
+ // and the method may be declared anywhere along the same chain.
2826
+ rule(
2827
+ "wantedCallbackMethod",
2828
+ [v("c"), v("event"), v("n"), v("held")],
2829
+ [
2830
+ lit("ancestryChain", v("c"), v("b")),
2831
+ lit("classCallback", v("b"), v("event"), v("n")),
2832
+ lit("ancestryChain", v("c"), v("b2")),
2833
+ lit("holdsProperty", v("b2"), v("n"), v("held"))
2834
+ ]
2835
+ ),
2682
2836
  rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
2683
2837
  rule(
2684
2838
  "anchorChain",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/resolution",
3
- "version": "0.31.2",
3
+ "version": "0.33.0",
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.31.2"
59
+ "@suss/datalog": "0.33.0"
60
60
  }
61
61
  }