@scientific-method/standard-checker 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ import { isSuperseded } from "../evidence.js";
6
6
  import { asList } from "../ids.js";
7
7
  import { checkAddress, checkOffset } from "../locations.js";
8
8
  import { locationRule } from "../standard.js";
9
+ import { parseScalar } from "../yaml.js";
9
10
  /** Checks a finding's method, environment and locations. */
10
11
  export function checkFinding(ctx, e) {
11
12
  const { problem } = ctx;
@@ -121,6 +122,24 @@ export function checkExperiment(ctx, id, e, isSup) {
121
122
  const builds = asList(meta.builds);
122
123
  if (builds.length !== 1)
123
124
  problem(file, "an experiment lists exactly one build", "ENTRY-TYPES-7");
125
+ // The standard's experiment entry lists the starting states: a save patch, a save, new-game,
126
+ // emulated-call or null, where a save or a patch is in saves/. A missing field is reported with
127
+ // the other required fields. Which save hashes the fixture needs depends on the form, so an
128
+ // unknown one gets no save hash problem. A path in saves/ names a file under it, so it has a
129
+ // name after saves/ and no empty, . or .. segment.
130
+ const state = meta.starting_state;
131
+ const savePath = typeof state === "string" &&
132
+ state.startsWith("saves/") &&
133
+ state
134
+ .split("/")
135
+ .slice(1)
136
+ .every((s) => s !== "" && s !== "." && s !== "..");
137
+ const fromSave = state === null || savePath;
138
+ if (state !== undefined && !fromSave && state !== "new-game" && state !== "emulated-call") {
139
+ // A string the front matter would read as another value, such as a quoted "null", is shown quoted.
140
+ const shown = typeof state === "string" && state !== "" && parseScalar(state) === state ? state : JSON.stringify(state);
141
+ problem(file, `starting_state ${shown} is none of the forms the standard defines: a save or save patch in saves/, new-game, emulated-call or null`);
142
+ }
124
143
  const fixture = meta.fixture && join(specDir, "experiments", meta.fixture);
125
144
  if (!fixture || !existsSync(fixture))
126
145
  problem(file, `fixture ${meta.fixture} does not exist`);
@@ -132,13 +151,13 @@ export function checkExperiment(ctx, id, e, isSup) {
132
151
  // Only a new game and an emulated call start without a save. starting_state null still has
133
152
  // one: a save that cannot be committed, kept with the captures and found by its hash.
134
153
  const save = fx.starting_state;
135
- if (!["new-game", "emulated-call"].includes(meta.starting_state) && !save?.xxh3) {
154
+ if (fromSave && !save?.xxh3) {
136
155
  const where = "gives the hash of the save its runs started from in starting_state.xxh3";
137
- problem(fixture, meta.starting_state === null
156
+ problem(fixture, state === null
138
157
  ? `${where}; starting_state null names a save kept with the captures, and an experiment that starts without a save has starting_state new-game or emulated-call`
139
158
  : where);
140
159
  }
141
- if (typeof meta.starting_state === "string" && meta.starting_state.endsWith(".patch.json") && !save?.base_xxh3)
160
+ if (savePath && state.endsWith(".patch.json") && !save?.base_xxh3)
142
161
  problem(fixture, "a patch fixture gives the base save's hash as well");
143
162
  for (const key of ["xxh3", "base_xxh3"])
144
163
  if (save?.[key] && !/^[0-9a-f]{32}$/.test(String(save[key])))
@@ -156,10 +175,8 @@ export function checkExperiment(ctx, id, e, isSup) {
156
175
  problem(fixture, `is not valid JSON: ${err.message}`);
157
176
  }
158
177
  }
159
- if (typeof meta.starting_state === "string" &&
160
- meta.starting_state.startsWith("saves/") &&
161
- !existsSync(join(specDir, "experiments", meta.starting_state)))
162
- problem(file, `starting_state ${meta.starting_state} does not exist`);
178
+ if (savePath && !existsSync(join(specDir, "experiments", state)))
179
+ problem(file, `starting_state ${state} does not exist`);
163
180
  // A recording is committed in recordings/, kept with the captures, or one of the build's files.
164
181
  if (typeof meta.recording === "string" && meta.recording !== "") {
165
182
  const rec = meta.recording;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scientific-method/standard-checker",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Checks a restoration's spec/, parity/ and deviations/ against version 1 of the dinorefurb documentation standard.",
5
5
  "type": "module",
6
6
  "license": "MIT",