spine-rigc 2.57.0 → 2.58.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/src/stop.ts CHANGED
@@ -147,7 +147,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
147
147
  const declared: unknown = spec.stops;
148
148
  if (declared === undefined) return spec;
149
149
  if (!Array.isArray(declared)) {
150
- throw new CompileError(`${where}: "stops" is ${JSON.stringify(declared)}; it is an array of { "chain", "links", "against", "at", "inward", "outward", "lower"? }`);
150
+ throw new CompileError(`${where}: "stops" is ${JSON.stringify(declared)}; it is an array of { "chain", "links", "against", "at", "inward", "outward", "lower"? }`, 'CE_STOP_DECLARATIONS_ARRAY');
151
151
  }
152
152
  const authored = new Map<string, RigBone>();
153
153
  for (const bone of spec.bones) if (isObj(bone) && typeof bone.name === 'string') authored.set(bone.name, bone);
@@ -157,44 +157,44 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
157
157
  const stops: ReadStop[] = [];
158
158
  const stoppedBy = new Map<string, string>();
159
159
  for (const [i, entry] of declared.entries()) {
160
- if (!isObj(entry)) throw new CompileError(`${where}: stops[${i}] is ${JSON.stringify(entry)}; a stop is an object`);
160
+ if (!isObj(entry)) throw new CompileError(`${where}: stops[${i}] is ${JSON.stringify(entry)}; a stop is an object`, 'CE_STOP_DECLARATION_OBJECT');
161
161
  const chain = entry.chain;
162
162
  if (typeof chain !== 'string' || chain.length === 0) {
163
- throw new CompileError(`${where}: stops[${i}] needs a "chain" — the name its point, frame and aim are called after ("<chain>_touch", "<chain>_stop", "<chain>_stop_aim")`);
163
+ throw new CompileError(`${where}: stops[${i}] needs a "chain" — the name its point, frame and aim are called after ("<chain>_touch", "<chain>_stop", "<chain>_stop_aim")`, 'CE_STOP_CHAIN_NAME_REQUIRED');
164
164
  }
165
165
  const who = `${where}: stop "${chain}"`;
166
166
  const links: unknown = entry.links;
167
167
  if (!Array.isArray(links) || links.length === 0 || !links.every((l): l is string => typeof l === 'string')) {
168
- throw new CompileError(`${who} states "links" ${JSON.stringify(links) ?? 'absent'}; it is the chain's swinging links, root first — a non-empty array of bone names`);
168
+ throw new CompileError(`${who} states "links" ${JSON.stringify(links) ?? 'absent'}; it is the chain's swinging links, root first — a non-empty array of bone names`, 'CE_STOP_LINKS_ARRAY');
169
169
  }
170
170
  for (const [k, link] of links.entries()) {
171
171
  const bone = authored.get(link);
172
- if (bone === undefined) throw new CompileError(`${who}: links[${k}] names bone "${link}", which this rig does not declare`);
172
+ if (bone === undefined) throw new CompileError(`${who}: links[${k}] names bone "${link}", which this rig does not declare`, 'CE_STOP_LINK_UNKNOWN');
173
173
  const other = stoppedBy.get(link);
174
174
  if (other !== undefined) {
175
175
  throw new CompileError(
176
- `${who}: links[${k}] "${link}" is already stopped by stop "${other}" — one drawn link "${link}_d" and one clamp "${link}_clamp" per link, so a link is stopped once`,
176
+ `${who}: links[${k}] "${link}" is already stopped by stop "${other}" — one drawn link "${link}_d" and one clamp "${link}_clamp" per link, so a link is stopped once`, 'CE_STOP_LINK_ALREADY_STOPPED',
177
177
  );
178
178
  }
179
179
  stoppedBy.set(link, chain);
180
180
  if (bone.from !== undefined) {
181
- throw new CompileError(`${who}: links[${k}] "${link}" takes its place from the manifest ("from"), and a stop copies the link's stated x, y and rotation onto its drawn link — state them`);
181
+ throw new CompileError(`${who}: links[${k}] "${link}" takes its place from the manifest ("from"), and a stop copies the link's stated x, y and rotation onto its drawn link — state them`, 'CE_STOP_LINK_MANIFEST_POSITION');
182
182
  }
183
183
  if (bone.parent === undefined) {
184
- throw new CompileError(`${who}: links[${k}] "${link}" is the skeleton's root, and a stop hangs its frame and its drawn link where the link hangs — a link has a parent`);
184
+ throw new CompileError(`${who}: links[${k}] "${link}" is the skeleton's root, and a stop hangs its frame and its drawn link where the link hangs — a link has a parent`, 'CE_STOP_LINK_ROOT');
185
185
  }
186
186
  }
187
187
  const against = entry.against;
188
188
  if (typeof against !== 'string' || against.length === 0) {
189
- throw new CompileError(`${who} needs "against" — the bone the point rides on, the leg the chain is held off`);
189
+ throw new CompileError(`${who} needs "against" — the bone the point rides on, the leg the chain is held off`, 'CE_STOP_AGAINST_REQUIRED');
190
190
  }
191
- if (!authored.has(against)) throw new CompileError(`${who}: "against" names bone "${against}", which this rig does not declare`);
191
+ if (!authored.has(against)) throw new CompileError(`${who}: "against" names bone "${against}", which this rig does not declare`, 'CE_STOP_AGAINST_UNKNOWN');
192
192
  const at: unknown = entry.at;
193
193
  if (at === undefined) {
194
- throw new CompileError(`${who} states no "at" — the point on "${against}" the frame aims at, [x, y] in "${against}"'s local space; the compiler places no point of its own`);
194
+ throw new CompileError(`${who} states no "at" — the point on "${against}" the frame aims at, [x, y] in "${against}"'s local space; the compiler places no point of its own`, 'CE_STOP_AT_REQUIRED');
195
195
  }
196
196
  if (!Array.isArray(at) || at.length !== 2 || !at.every((v): v is number => typeof v === 'number')) {
197
- throw new CompileError(`${who} states "at" ${JSON.stringify(at)}; it is [x, y], two numbers in "${against}"'s local space`);
197
+ throw new CompileError(`${who} states "at" ${JSON.stringify(at)}; it is [x, y], two numbers in "${against}"'s local space`, 'CE_STOP_AT_PAIR');
198
198
  }
199
199
  const range = (node: Record<string, unknown>, field: string): { inward: number; outward: number } => {
200
200
  const out: Record<'inward' | 'outward', number> = { inward: 0, outward: 0 };
@@ -202,14 +202,14 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
202
202
  const v = node[end];
203
203
  if (typeof v !== 'number') {
204
204
  throw new CompileError(
205
- `${who} states no "${field}${end}" — degrees from the link's rest angle, counted positive away from "${against}"; the range is [inward, outward], and the compiler states neither end for you`,
205
+ `${who} states no "${field}${end}" — degrees from the link's rest angle, counted positive away from "${against}"; the range is [inward, outward], and the compiler states neither end for you`, 'CE_STOP_RANGE_END_REQUIRED',
206
206
  );
207
207
  }
208
208
  out[end] = v;
209
209
  }
210
210
  if (out.inward > out.outward) {
211
211
  throw new CompileError(
212
- `${who}: "${field}inward" ${out.inward} is greater than "${field}outward" ${out.outward} — the range is [inward, outward] in degrees counted positive away from "${against}", and this one holds no angle`,
212
+ `${who}: "${field}inward" ${out.inward} is greater than "${field}outward" ${out.outward} — the range is [inward, outward] in degrees counted positive away from "${against}", and this one holds no angle`, 'CE_STOP_RANGE_REVERSED',
213
213
  );
214
214
  }
215
215
  return out;
@@ -219,11 +219,11 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
219
219
  let lower: { inward: number; outward: number } | null = null;
220
220
  if (links.length > 1) {
221
221
  if (!isObj(lowerRaw)) {
222
- throw new CompileError(`${who} states no "lower" — the range [inward, outward] each link below the first may turn through off its own rest bend; the chain has ${links.length - 1} of them`);
222
+ throw new CompileError(`${who} states no "lower" — the range [inward, outward] each link below the first may turn through off its own rest bend; the chain has ${links.length - 1} of them`, 'CE_STOP_LOWER_REQUIRED');
223
223
  }
224
224
  lower = range(lowerRaw, 'lower.');
225
225
  } else if (lowerRaw !== undefined) {
226
- throw new CompileError(`${who} states "lower" and its chain has one link — there is no link below the first for it to hold`);
226
+ throw new CompileError(`${who} states "lower" and its chain has one link — there is no link below the first for it to hold`, 'CE_STOP_LOWER_WITH_ONE_LINK');
227
227
  }
228
228
  stops.push({ who, chain, links, against, at: [at[0], at[1]], inward: first.inward, outward: first.outward, lower, names: stopNames(chain, links) });
229
229
  }
@@ -236,16 +236,16 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
236
236
  const { names } = stop;
237
237
  const lowerDrawn = names.drawn.slice(1);
238
238
  for (const name of [names.frame, names.rel, names.drawn[0], ...lowerDrawn, names.point]) {
239
- if (authored.has(name)) throw new CompileError(`${stop.who} generates bone "${name}", which this rig already declares — rename the bone, or the stop's "chain"`);
239
+ if (authored.has(name)) throw new CompileError(`${stop.who} generates bone "${name}", which this rig already declares — rename the bone, or the stop's "chain"`, 'CE_STOP_GENERATED_BONE_DECLARED');
240
240
  const by = generatedBones.get(name);
241
- if (by !== undefined) throw new CompileError(`${stop.who} generates bone "${name}", which stop "${by}" generates too — rename one "chain"`);
241
+ if (by !== undefined) throw new CompileError(`${stop.who} generates bone "${name}", which stop "${by}" generates too — rename one "chain"`, 'CE_STOP_GENERATED_BONE_COLLISION');
242
242
  generatedBones.set(name, stop.chain);
243
243
  }
244
244
  for (const [type, name] of [['ik', names.aim], ['transform', names.copy], ...names.clamp.map((c) => ['transform', c] as const)] as const) {
245
245
  const key = `${type} ${name}`;
246
- if (authoredConstraints.has(key)) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which this rig already declares — rename the constraint, or the stop's "chain"`);
246
+ if (authoredConstraints.has(key)) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which this rig already declares — rename the constraint, or the stop's "chain"`, 'CE_STOP_GENERATED_CONSTRAINT_DECLARED');
247
247
  const by = generatedConstraints.get(key);
248
- if (by !== undefined) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which stop "${by}" generates too — rename one "chain"`);
248
+ if (by !== undefined) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which stop "${by}" generates too — rename one "chain"`, 'CE_STOP_GENERATED_CONSTRAINT_COLLISION');
249
249
  generatedConstraints.set(key, stop.chain);
250
250
  }
251
251
  }
@@ -265,7 +265,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
265
265
  });
266
266
  if (at < 0) {
267
267
  throw new CompileError(
268
- `${stop.who}: links[${k}] "${link}" is written by no constraint of this rig spec — a stop holds a link something swings (a physics, ik, transform or path constraint in "constraints"), and its copy and clamp run right after that. A physics constraint in the motion spec's table is emitted after every rig constraint, past the clamp; declare it here`,
268
+ `${stop.who}: links[${k}] "${link}" is written by no constraint of this rig spec — a stop holds a link something swings (a physics, ik, transform or path constraint in "constraints"), and its copy and clamp run right after that. A physics constraint in the motion spec's table is emitted after every rig constraint, past the clamp; declare it here`, 'CE_STOP_LINK_UNDRIVEN',
269
269
  );
270
270
  }
271
271
  return at;
@@ -296,14 +296,14 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
296
296
  for (let k = 1; k < links.length; k++) {
297
297
  if (!ancestry(links[k]).includes(names.drawn[k - 1])) {
298
298
  throw new CompileError(
299
- `${stop.who}: links[${k}] "${links[k]}" does not hang below "${names.drawn[k - 1]}", the drawn link of links[${k - 1}] — a chain's next link hangs from the drawn link above it, so the clamp that holds that one carries this one; parent it there`,
299
+ `${stop.who}: links[${k}] "${links[k]}" does not hang below "${names.drawn[k - 1]}", the drawn link of links[${k - 1}] — a chain's next link hangs from the drawn link above it, so the clamp that holds that one carries this one; parent it there`, 'CE_STOP_LINK_PARENT_CHAIN',
300
300
  );
301
301
  }
302
302
  }
303
303
  const held = new Set([names.frame, names.rel, ...names.drawn, ...links]);
304
304
  const under = [stop.against, ...ancestry(stop.against)].find((b) => held.has(b));
305
305
  if (under !== undefined) {
306
- throw new CompileError(`${stop.who}: "against" "${stop.against}" hangs below "${under}", which the stop moves — the point would swing with the chain it holds off; name the leg`);
306
+ throw new CompileError(`${stop.who}: "against" "${stop.against}" hangs below "${under}", which the stop moves — the point would swing with the chain it holds off; name the leg`, 'CE_STOP_AGAINST_MOVES_WITH_STOP');
307
307
  }
308
308
  }
309
309
 
@@ -338,7 +338,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
338
338
  for (const [end, value] of [['outward', a], ['inward', b]] as const) {
339
339
  if (Math.abs(value) > BOUND) {
340
340
  throw new CompileError(
341
- `${stop.who}: "${field}${end}" ${range[end]} puts ${what}'s bound at ${value}°, past ±${BOUND}° — the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holds`,
341
+ `${stop.who}: "${field}${end}" ${range[end]} puts ${what}'s bound at ${value}°, past ±${BOUND}° — the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holds`, 'CE_STOP_LOCAL_BOUND_RANGE',
342
342
  );
343
343
  }
344
344
  }
@@ -358,10 +358,10 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
358
358
  if (listed.has(name)) continue;
359
359
  const bone = known(name);
360
360
  if (bone === undefined) {
361
- throw new CompileError(`${who}: "${tip}" hangs below "${name}", which a later stop generates — declare that stop first, so its angles are known when this one is read`);
361
+ throw new CompileError(`${who}: "${tip}" hangs below "${name}", which a later stop generates — declare that stop first, so its angles are known when this one is read`, 'CE_STOP_DEPENDENCY_ORDER');
362
362
  }
363
363
  if (bone.from !== undefined) {
364
- throw new CompileError(`${who}: "${name}" takes its place from the manifest ("from"), and a stop's angles are read off the setup pose this rig spec states — state its x, y and rotation`);
364
+ throw new CompileError(`${who}: "${name}" takes its place from the manifest ("from"), and a stop's angles are read off the setup pose this rig spec states — state its x, y and rotation`, 'CE_STOP_ANCESTOR_MANIFEST_POSITION');
365
365
  }
366
366
  listed.add(name);
367
367
  chain.push(bone);
@@ -371,7 +371,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
371
371
  const parentFrame = frames.get(parent) as BoneTransform;
372
372
  if (!conformal(parentFrame)) {
373
373
  throw new CompileError(
374
- `${who}: "${parent}", where the frame hangs, is scaled unevenly, sheared or reflected at the setup pose — the frame's angles add to its parent's only through a rotation and an even scale, and inward is a turn's sense`,
374
+ `${who}: "${parent}", where the frame hangs, is scaled unevenly, sheared or reflected at the setup pose — the frame's angles add to its parent's only through a rotation and an even scale, and inward is a turn's sense`, 'CE_STOP_FRAME_PARENT_TRANSFORM',
375
375
  );
376
376
  }
377
377
  for (let k = 1; k < links.length; k++) {
@@ -381,7 +381,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
381
381
  const plain = (bone.scaleX ?? 1) === (bone.scaleY ?? 1) && (bone.scaleX ?? 1) > 0 && (bone.shearX ?? 0) === 0 && (bone.shearY ?? 0) === 0 && (bone.inherit ?? 'normal') === 'normal';
382
382
  if (!plain) {
383
383
  throw new CompileError(
384
- `${who}: "${at}", between links[${k}] "${links[k]}" and the drawn link above it, is scaled unevenly, sheared, reflected or inherits other than normally — a lower clamp reads the link's turn in its parent, and inward is a turn's sense`,
384
+ `${who}: "${at}", between links[${k}] "${links[k]}" and the drawn link above it, is scaled unevenly, sheared, reflected or inherits other than normally — a lower clamp reads the link's turn in its parent, and inward is a turn's sense`, 'CE_STOP_LOWER_PARENT_TRANSFORM',
385
385
  );
386
386
  }
387
387
  }
@@ -392,7 +392,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
392
392
  const dx = target[0] - linkFrame.worldX;
393
393
  const dy = target[1] - linkFrame.worldY;
394
394
  if (dx === 0 && dy === 0) {
395
- throw new CompileError(`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" sits on the root of "${links[0]}" at the setup pose — the frame aims from the root at the point, and there is no direction to a point it stands on`);
395
+ throw new CompileError(`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" sits on the root of "${links[0]}" at the setup pose — the frame aims from the root at the point, and there is no direction to a point it stands on`, 'CE_STOP_POINT_AT_ROOT');
396
396
  }
397
397
  const aim = directionDegrees(dx, dy);
398
398
  // Each angle is brought into (-180, 180] by one whole turn at most, so a
@@ -401,7 +401,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
401
401
  const rest = halfTurn(halfTurn(linkFrame.worldRotation) - aim);
402
402
  if (rest === 0) {
403
403
  throw new CompileError(
404
- `${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" lies on "${links[0]}"'s own line at the setup pose — inward is the turn from the link toward the point, and a point on the link's line has no side`,
404
+ `${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" lies on "${links[0]}"'s own line at the setup pose — inward is the turn from the link toward the point, and a point on the link's line has no side`, 'CE_STOP_POINT_ON_LINK_LINE',
405
405
  );
406
406
  }
407
407
  // Inward turns the link toward the aim line: counter-clockwise when the link
package/src/trackgen.ts CHANGED
@@ -199,13 +199,13 @@ export function evaluateTrackDerive(
199
199
  where: string,
200
200
  ): TrackDeriveReport {
201
201
  if (derive === null || typeof derive !== 'object' || Array.isArray(derive)) {
202
- throw new CompileError(`${where}: "derive" is ${JSON.stringify(derive)}; it is an object with a "kind"`);
202
+ throw new CompileError(`${where}: "derive" is ${JSON.stringify(derive)}; it is an object with a "kind"`, 'CE_TRACK_DERIVE_OBJECT');
203
203
  }
204
204
  const kind = (derive as { kind?: unknown }).kind;
205
205
  if (typeof kind !== 'string' || !(TRACK_DERIVE_KINDS as readonly string[]).includes(kind)) {
206
206
  throw new CompileError(
207
207
  `${where}: "derive" has kind ${JSON.stringify(kind)}; this spec evaluates ${TRACK_DERIVE_KINDS.join(', ')}. ` +
208
- 'A model that is none of those is still authorable as a per-member "v" map, or as one track per member.',
208
+ 'A model that is none of those is still authorable as a per-member "v" map, or as one track per member.', 'CE_TRACK_DERIVE_KIND',
209
209
  );
210
210
  }
211
211
  const turn = derive as TrackDeriveTurn;
@@ -223,7 +223,7 @@ export function evaluateTrackDerive(
223
223
  throw new CompileError(
224
224
  `${where}: derive ${kind} states carried=${JSON.stringify(turn.carried)} on a "${property}" track, and the ` +
225
225
  'foreshortening reads no depth difference — it is cos(α − t)/cos α, where α is the member\'s own angle off the ' +
226
- `axis. "carried" belongs on the "${projections.shift}" track, whose shared shift is the thing it names.`,
226
+ `axis. "carried" belongs on the "${projections.shift}" track, whose shared shift is the thing it names.`, 'CE_TRACK_CARRIED_PROJECTION',
227
227
  );
228
228
  }
229
229
  const carried = turn.carried === undefined ? 0 : num(turn.carried, 'carried', where);
@@ -249,7 +249,7 @@ export function evaluateTrackDerive(
249
249
  `${where}: derive ${kind} projects onto "${property}" and member "${member.name}" states depth ${depth}. ` +
250
250
  'Foreshortening is cos(α − t)/cos α with α = atan2(coordinate, depth), which needs the part to be IN FRONT ' +
251
251
  'of the axis; at or behind it there is no front surface to narrow. A part behind the axis still takes the ' +
252
- `"${projections.shift}" projection, where a negative depth is exactly what swings it the other way.`,
252
+ `"${projections.shift}" projection, where a negative depth is exactly what swings it the other way.`, 'CE_TRACK_FORESHORTEN_DEPTH',
253
253
  );
254
254
  }
255
255
  const alpha = Math.atan2(u, depth);
@@ -260,7 +260,7 @@ export function evaluateTrackDerive(
260
260
  `its own edge: α = ${round(alpha)} rad and the turn is ${round(rad)} rad, so cos(α − t) is ` +
261
261
  `${round(Math.cos(alpha - rad))} and the scale would be ${round(scale)}. A scale at or below 0 mirrors the ` +
262
262
  'drawing rather than narrowing it. The part has turned edge-on — the turn is past what this construction ' +
263
- 'carries, and FACE §8 is the page that picks a construction from the angle.',
263
+ 'carries, and FACE §8 is the page that picks a construction from the angle.', 'CE_TRACK_FORESHORTEN_EDGE_ON',
264
264
  );
265
265
  }
266
266
  values.push({ member: member.name, at: u, depth, value: round(scale) });
@@ -298,7 +298,7 @@ function refuseProperty(kind: string, property: string, where: string): never {
298
298
  `${where}: derive ${kind} has no projection onto "${property}". It projects onto "${p.shift}" (the displacement, ` +
299
299
  `FACE §3) and "${p.foreshorten}" (the narrowing, FACE §5), and onto nothing else — a turn about one axis moves ` +
300
300
  `nothing along it, so a paired "translate"/"scale" would need a second channel this model does not state. ` +
301
- 'State the axis, or write the values as a per-member "v" map.',
301
+ 'State the axis, or write the values as a per-member "v" map.', 'CE_TRACK_PROPERTY_PROJECTION',
302
302
  );
303
303
  }
304
304
 
@@ -322,7 +322,7 @@ function depthOf(
322
322
  throw new CompileError(
323
323
  `${where}: derive ${kind} states one depth ${depth} for ${members.length} members ` +
324
324
  `(${members.map((m) => m.name).join(', ')}). One number for every member is the shared value a plain "v" ` +
325
- 'already keys — state a depth per member, as { "member": z, … }.',
325
+ 'already keys — state a depth per member, as { "member": z, … }.', 'CE_TRACK_GROUP_SHARED_DEPTH',
326
326
  );
327
327
  }
328
328
  return num(depth, 'depth', where);
@@ -330,7 +330,7 @@ function depthOf(
330
330
  if (depth === null || typeof depth !== 'object' || Array.isArray(depth)) {
331
331
  throw new CompileError(
332
332
  `${where}: derive ${kind} has depth ${JSON.stringify(depth)}; it is a number on a bone track and ` +
333
- '{ "member": z, … } on a group track',
333
+ '{ "member": z, … } on a group track', 'CE_TRACK_DEPTH_SHAPE',
334
334
  );
335
335
  }
336
336
  const table = depth as Record<string, unknown>;
@@ -340,7 +340,7 @@ function depthOf(
340
340
  if (!known.has(name)) {
341
341
  throw new CompileError(
342
342
  `${where}: derive ${kind} states a depth for "${name}", which this track does not key ` +
343
- `(its members are: ${members.map((m) => m.name).join(', ')})`,
343
+ `(its members are: ${members.map((m) => m.name).join(', ')})`, 'CE_TRACK_DEPTH_MEMBER_UNKNOWN',
344
344
  );
345
345
  }
346
346
  }
@@ -349,7 +349,7 @@ function depthOf(
349
349
  `${where}: derive ${kind} states no depth for member "${member}" ` +
350
350
  `(it states: ${stated.length ? stated.join(', ') : 'nothing'}). ` +
351
351
  'A depth is refused rather than defaulted: a member silently at depth 0 would be keyed with a DIFFERENT ' +
352
- 'model from the ones beside it, and that is exactly the error a table of six is written to make visible.',
352
+ 'model from the ones beside it, and that is exactly the error a table of six is written to make visible.', 'CE_TRACK_DEPTH_MEMBER_REQUIRED',
353
353
  );
354
354
  }
355
355
  return num(table[member], `depth.${member}`, where);
@@ -358,7 +358,7 @@ function depthOf(
358
358
  /** One required finite number, refused by field name rather than compiled as a NaN. */
359
359
  function num(value: unknown, field: string, where: string): number {
360
360
  if (typeof value !== 'number' || !Number.isFinite(value)) {
361
- throw new CompileError(`${where}: derive field "${field}" is ${JSON.stringify(value)}; it is a finite number the spec has to state`);
361
+ throw new CompileError(`${where}: derive field "${field}" is ${JSON.stringify(value)}; it is a finite number the spec has to state`, 'CE_TRACK_FIELD_NUMBER_FINITE');
362
362
  }
363
363
  return value;
364
364
  }