@dice-o-rolla/dice-core 0.2.0 → 0.3.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
@@ -15,5 +15,8 @@ const random = new SeededRandomSource(42);
15
15
 
16
16
  Standard dice terms support `kh`, `kl`, `dh`, and `dl` selection followed by an optional score map.
17
17
  Unlisted faces score zero. Paired percentile and d66 terms intentionally reject these operations.
18
+ Engine-produced die results may include immutable provenance with stable term, logical-die, and
19
+ physical-die coordinates, the settled face, inclusion state, and score contribution. Core result
20
+ aggregation validates and freezes this metadata without depending on a renderer or physics backend.
18
21
 
19
22
  Licensed under Apache-2.0. See `LICENSE`, `NOTICE`, and `THIRD_PARTY_NOTICES.md` in the package.
@@ -7,6 +7,17 @@ export interface DieDefinition {
7
7
  interface BaseDieResult {
8
8
  readonly id: string;
9
9
  readonly value: number;
10
+ readonly provenance?: DieResultProvenance;
11
+ }
12
+ export type DieResultState = 'included' | 'discarded';
13
+ export interface DieResultProvenance {
14
+ readonly termId: string;
15
+ readonly termIndex: number;
16
+ readonly dieIndex: number;
17
+ readonly physicalIndex: number;
18
+ readonly state: DieResultState;
19
+ readonly faceValue: number;
20
+ readonly contribution?: number;
10
21
  }
11
22
  export type DiceComponentRole = 'tens' | 'units';
12
23
  export interface DiceComponentResult {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { DiceComponentResult, DiceComponentRole, ComponentDieResult, DieDefinition, DieResult, DieType, StandardDieResult, } from './dice/types.js';
1
+ export type { DiceComponentResult, DiceComponentRole, ComponentDieResult, DieDefinition, DieResult, DieResultProvenance, DieResultState, DieType, StandardDieResult, } from './dice/types.js';
2
2
  export { isDieType, STANDARD_DIE_TYPES } from './dice/types.js';
3
3
  export { TypedEventEmitter } from './events/typed-event-emitter.js';
4
4
  export type { QuaternionLike, Vector3Like } from './math/types.js';
@@ -21,9 +21,52 @@ export function createRollResult(options) {
21
21
  });
22
22
  }
23
23
  function freezeDieResult(die) {
24
- if (die.component === undefined)
25
- return Object.freeze({ ...die });
26
- return Object.freeze({ ...die, component: Object.freeze({ ...die.component }) });
24
+ validateProvenance(die);
25
+ const provenance = die.provenance === undefined ? undefined : Object.freeze({ ...die.provenance });
26
+ if (die.component === undefined) {
27
+ return Object.freeze({
28
+ ...die,
29
+ ...(provenance === undefined ? {} : { provenance }),
30
+ });
31
+ }
32
+ return Object.freeze({
33
+ ...die,
34
+ component: Object.freeze({ ...die.component }),
35
+ ...(provenance === undefined ? {} : { provenance }),
36
+ });
37
+ }
38
+ function validateProvenance(die) {
39
+ const provenance = die.provenance;
40
+ if (provenance === undefined)
41
+ return;
42
+ if (provenance.termId.length === 0) {
43
+ throw new TypeError(`Dice provenance termId must not be empty`);
44
+ }
45
+ for (const [name, value] of [
46
+ ['termIndex', provenance.termIndex],
47
+ ['dieIndex', provenance.dieIndex],
48
+ ['physicalIndex', provenance.physicalIndex],
49
+ ]) {
50
+ if (!Number.isSafeInteger(value) || value < 0) {
51
+ throw new RangeError(`Dice provenance ${name} must be a non-negative safe integer`);
52
+ }
53
+ }
54
+ const expectedState = die.included === false ? 'discarded' : 'included';
55
+ if (provenance.state !== expectedState) {
56
+ throw new RangeError(`Dice provenance state does not match inclusion metadata for ${die.id}`);
57
+ }
58
+ const expectedFaceValue = die.component?.faceValue ?? die.value;
59
+ if (provenance.faceValue !== expectedFaceValue) {
60
+ throw new RangeError(`Dice provenance face does not match result value for ${die.id}`);
61
+ }
62
+ const expectedContribution = die.component === undefined
63
+ ? provenance.state === 'discarded'
64
+ ? 0
65
+ : (die.score ?? die.value)
66
+ : undefined;
67
+ if (provenance.contribution !== expectedContribution) {
68
+ throw new RangeError(`Dice provenance contribution does not match result value for ${die.id}`);
69
+ }
27
70
  }
28
71
  function getDiceTotal(dice) {
29
72
  let total = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dice-o-rolla/dice-core",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Framework-neutral dice notation, roll results, events, and random sources.",
5
5
  "keywords": [
6
6
  "dice",