@dicebear/core 10.0.0-rc.4 → 10.0.0-rc.5

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
@@ -27,7 +27,8 @@ Playground, your next avatar is always just a stone's throw away!
27
27
 
28
28
  ## Sponsors
29
29
 
30
- Advertisement: Many thanks to our sponsors who provide us with free or discounted products.
30
+ Advertisement: Many thanks to our sponsors who provide us with free or
31
+ discounted products.
31
32
 
32
33
  <a href="https://bunny.net/" target="_blank" rel="noopener noreferrer">
33
34
  <picture>
package/lib/Options.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Range } from './StyleDefinition.js';
1
2
  import type { StyleOptions, StyleOptionsFlipValue, StyleOptionsColorFillValue } from './StyleOptions.js';
2
3
  /**
3
4
  * Validates the raw user-supplied options and exposes them through typed
@@ -19,11 +20,11 @@ export declare class Options<D = unknown> {
19
20
  flip(): readonly StyleOptionsFlipValue[];
20
21
  fontFamily(): readonly string[];
21
22
  fontWeight(): readonly number[];
22
- scale(): readonly number[];
23
- borderRadius(): readonly number[];
24
- rotate(): readonly number[];
25
- translateX(): readonly number[];
26
- translateY(): readonly number[];
23
+ scale(): Range | undefined;
24
+ borderRadius(): Range | undefined;
25
+ rotate(): Range | undefined;
26
+ translateX(): Range | undefined;
27
+ translateY(): Range | undefined;
27
28
  /**
28
29
  * Returns the user-set variant constraint for `name`:
29
30
  * - `undefined` when the user did not set `${name}Variant`,
@@ -39,6 +40,6 @@ export declare class Options<D = unknown> {
39
40
  */
40
41
  color(name: string): readonly string[] | undefined;
41
42
  colorFill(name: string): readonly StyleOptionsColorFillValue[];
42
- colorAngle(name: string): readonly number[];
43
- colorFillStops(name: string): readonly number[];
43
+ colorAngle(name: string): Range | undefined;
44
+ colorFillStops(name: string): Range | undefined;
44
45
  }
package/lib/Options.js CHANGED
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _Options_instances, _Options_data, _Options_dynamic, _Options_asArray;
12
+ var _Options_instances, _Options_data, _Options_dynamic, _Options_asArray, _Options_toRange;
13
13
  import { OptionsValidator } from './Validator/OptionsValidator.js';
14
14
  /**
15
15
  * Validates the raw user-supplied options and exposes them through typed
@@ -50,19 +50,19 @@ export class Options {
50
50
  return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").fontWeight);
51
51
  }
52
52
  scale() {
53
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").scale);
53
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_data, "f").scale);
54
54
  }
55
55
  borderRadius() {
56
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").borderRadius);
56
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_data, "f").borderRadius);
57
57
  }
58
58
  rotate() {
59
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").rotate);
59
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_data, "f").rotate);
60
60
  }
61
61
  translateX() {
62
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").translateX);
62
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_data, "f").translateX);
63
63
  }
64
64
  translateY() {
65
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_data, "f").translateY);
65
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_data, "f").translateY);
66
66
  }
67
67
  /**
68
68
  * Returns the user-set variant constraint for `name`:
@@ -98,10 +98,10 @@ export class Options {
98
98
  return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_instances, "m", _Options_dynamic).call(this, `${name}ColorFill`));
99
99
  }
100
100
  colorAngle(name) {
101
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_instances, "m", _Options_dynamic).call(this, `${name}ColorAngle`));
101
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_instances, "m", _Options_dynamic).call(this, `${name}ColorAngle`));
102
102
  }
103
103
  colorFillStops(name) {
104
- return __classPrivateFieldGet(this, _Options_instances, "m", _Options_asArray).call(this, __classPrivateFieldGet(this, _Options_instances, "m", _Options_dynamic).call(this, `${name}ColorFillStops`));
104
+ return __classPrivateFieldGet(this, _Options_instances, "m", _Options_toRange).call(this, __classPrivateFieldGet(this, _Options_instances, "m", _Options_dynamic).call(this, `${name}ColorFillStops`));
105
105
  }
106
106
  }
107
107
  _Options_data = new WeakMap(), _Options_instances = new WeakSet(), _Options_dynamic = function _Options_dynamic(key) {
@@ -114,4 +114,12 @@ _Options_data = new WeakMap(), _Options_instances = new WeakSet(), _Options_dyna
114
114
  return value;
115
115
  }
116
116
  return [value];
117
+ }, _Options_toRange = function _Options_toRange(value) {
118
+ if (value === undefined) {
119
+ return undefined;
120
+ }
121
+ if (typeof value === 'number') {
122
+ return { min: value, max: value };
123
+ }
124
+ return { min: value[0], max: value[1] };
117
125
  };
@@ -36,7 +36,7 @@ export class Mulberry32 {
36
36
  const z = (__classPrivateFieldSet(this, _Mulberry32_state, (__classPrivateFieldGet(this, _Mulberry32_state, "f") + 0x6d2b79f5) | 0, "f"));
37
37
  let t = Math.imul(z ^ (z >>> 15), z | 1);
38
38
  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
39
- return ((t ^ (t >>> 14)) >>> 0);
39
+ return (t ^ (t >>> 14)) >>> 0;
40
40
  }
41
41
  /**
42
42
  * Advances the state and returns the next value in `[0, 1)`.
package/lib/Prng.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Range } from './StyleDefinition.js';
1
2
  /**
2
3
  * Key-based pseudorandom number generator.
3
4
  *
@@ -10,14 +11,17 @@ export declare class Prng {
10
11
  constructor(seed: string);
11
12
  /**
12
13
  * Picks a single item from `items` deterministically. Returns `undefined`
13
- * for an empty list. Items are sorted by their string representation
14
- * before picking so that input order does not affect the result.
14
+ * for an empty list. Duplicate values (by string representation) are
15
+ * collapsed before picking so that input order and duplication do not
16
+ * affect the result.
15
17
  */
16
18
  pick<T>(key: string, items: readonly T[]): T | undefined;
17
19
  /**
18
- * Picks an item from `entries` proportional to its weight. When all weights
19
- * are zero, falls back to an unweighted {@link pick}. Returns `undefined`
20
- * for an empty list.
20
+ * Picks an item from `entries` proportional to its weight. Duplicate items
21
+ * (by string representation) are collapsed before picking only the
22
+ * first occurrence's weight is kept. When all weights are zero, falls
23
+ * back to an unweighted {@link pick}. Returns `undefined` for an empty
24
+ * list.
21
25
  */
22
26
  weightedPick<T>(key: string, entries: readonly (readonly [T, number])[]): T | undefined;
23
27
  /**
@@ -25,17 +29,25 @@ export declare class Prng {
25
29
  */
26
30
  bool(key: string, likelihood?: number): boolean;
27
31
  /**
28
- * Returns a deterministic float in the closed range `[min(values), max(values)]`,
29
- * rounded to four decimal places. Returns `undefined` for an empty list.
32
+ * Returns a deterministic float in `range`, rounded to four decimal places.
33
+ * With `range.step > 0`, the result is drawn uniformly from
34
+ * `{ min + i*step | 0 ≤ i ≤ floor((max - min) / step) }`, so both endpoints
35
+ * of an evenly-divisible range are equally likely. Non-positive or absent
36
+ * step means continuous. `min`/`max` are sorted internally, so a reversed
37
+ * pair is tolerated.
30
38
  */
31
- float(key: string, values: readonly number[]): number | undefined;
39
+ float(key: string, range: Range): number;
32
40
  /**
33
- * Returns a deterministic integer in the closed range
34
- * `[min(values), max(values)]`. Returns `undefined` for an empty list.
41
+ * Returns a deterministic integer in `range`. `min`/`max` are sorted
42
+ * internally, so a reversed pair is tolerated. `range.step` is accepted
43
+ * for symmetry with {@link float} but ignored — integers already step by 1.
35
44
  */
36
- integer(key: string, values: readonly number[]): number | undefined;
45
+ integer(key: string, range: Range): number;
37
46
  /**
38
- * Fisher-Yates shuffle with chained Mulberry32 state.
47
+ * Fisher-Yates shuffle with chained Mulberry32 state. Duplicate values
48
+ * (by string representation) are collapsed before shuffling, so a
49
+ * caller's slice off the front cannot accidentally produce a repeated
50
+ * value.
39
51
  */
40
52
  shuffle<T>(key: string, items: readonly T[]): T[];
41
53
  /**
package/lib/Prng.js CHANGED
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _Prng_instances, _Prng_seed, _Prng_compareByCodePoint;
12
+ var _Prng_instances, _Prng_seed, _Prng_uniqueByCodePoint, _Prng_compareByCodePoint;
13
13
  import { Fnv1a } from './Prng/Fnv1a.js';
14
14
  import { Mulberry32 } from './Prng/Mulberry32.js';
15
15
  /**
@@ -27,8 +27,9 @@ export class Prng {
27
27
  }
28
28
  /**
29
29
  * Picks a single item from `items` deterministically. Returns `undefined`
30
- * for an empty list. Items are sorted by their string representation
31
- * before picking so that input order does not affect the result.
30
+ * for an empty list. Duplicate values (by string representation) are
31
+ * collapsed before picking so that input order and duplication do not
32
+ * affect the result.
32
33
  */
33
34
  pick(key, items) {
34
35
  if (items.length === 0) {
@@ -37,20 +38,30 @@ export class Prng {
37
38
  if (items.length === 1) {
38
39
  return items[0];
39
40
  }
40
- const sorted = Array.from(items).sort(__classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint));
41
+ const unique = __classPrivateFieldGet(this, _Prng_instances, "m", _Prng_uniqueByCodePoint).call(this, items);
42
+ if (unique.length === 1) {
43
+ return unique[0];
44
+ }
45
+ const sorted = unique.sort(__classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint));
41
46
  const index = Math.floor(this.getValue(key) * sorted.length);
42
47
  return sorted[index];
43
48
  }
44
49
  /**
45
- * Picks an item from `entries` proportional to its weight. When all weights
46
- * are zero, falls back to an unweighted {@link pick}. Returns `undefined`
47
- * for an empty list.
50
+ * Picks an item from `entries` proportional to its weight. Duplicate items
51
+ * (by string representation) are collapsed before picking only the
52
+ * first occurrence's weight is kept. When all weights are zero, falls
53
+ * back to an unweighted {@link pick}. Returns `undefined` for an empty
54
+ * list.
48
55
  */
49
56
  weightedPick(key, entries) {
50
57
  if (entries.length === 0) {
51
58
  return undefined;
52
59
  }
53
- const sorted = Array.from(entries).sort((a, b) => __classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint).call(this, a[0], b[0]));
60
+ if (entries.length === 1) {
61
+ return entries[0][0];
62
+ }
63
+ const unique = __classPrivateFieldGet(this, _Prng_instances, "m", _Prng_uniqueByCodePoint).call(this, entries, (e) => String(e[0]));
64
+ const sorted = unique.sort((a, b) => __classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint).call(this, a[0], b[0]));
54
65
  const totalWeight = sorted.reduce((sum, e) => sum + e[1], 0);
55
66
  if (totalWeight === 0) {
56
67
  return this.pick(key, sorted.map((e) => e[0]));
@@ -72,34 +83,49 @@ export class Prng {
72
83
  return this.getValue(key) * 100 < likelihood;
73
84
  }
74
85
  /**
75
- * Returns a deterministic float in the closed range `[min(values), max(values)]`,
76
- * rounded to four decimal places. Returns `undefined` for an empty list.
86
+ * Returns a deterministic float in `range`, rounded to four decimal places.
87
+ * With `range.step > 0`, the result is drawn uniformly from
88
+ * `{ min + i*step | 0 ≤ i ≤ floor((max - min) / step) }`, so both endpoints
89
+ * of an evenly-divisible range are equally likely. Non-positive or absent
90
+ * step means continuous. `min`/`max` are sorted internally, so a reversed
91
+ * pair is tolerated.
77
92
  */
78
- float(key, values) {
79
- if (values.length === 0) {
80
- return undefined;
93
+ float(key, range) {
94
+ const min = Math.min(range.min, range.max);
95
+ const max = Math.max(range.min, range.max);
96
+ const step = range.step ?? 0;
97
+ let value;
98
+ if (step > 0) {
99
+ const buckets = Math.floor((max - min) / step) + 1;
100
+ const i = Math.floor(this.getValue(key) * buckets);
101
+ value = min + i * step;
81
102
  }
82
- const min = Math.min(...values);
83
- const max = Math.max(...values);
84
- return Math.round((min + this.getValue(key) * (max - min)) * 10000) / 10000;
103
+ else {
104
+ value = min + this.getValue(key) * (max - min);
105
+ }
106
+ return Math.round(value * 10000) / 10000;
85
107
  }
86
108
  /**
87
- * Returns a deterministic integer in the closed range
88
- * `[min(values), max(values)]`. Returns `undefined` for an empty list.
109
+ * Returns a deterministic integer in `range`. `min`/`max` are sorted
110
+ * internally, so a reversed pair is tolerated. `range.step` is accepted
111
+ * for symmetry with {@link float} but ignored — integers already step by 1.
89
112
  */
90
- integer(key, values) {
91
- if (values.length === 0) {
92
- return undefined;
93
- }
94
- const min = Math.min(...values);
95
- const max = Math.max(...values);
113
+ integer(key, range) {
114
+ const min = Math.min(range.min, range.max);
115
+ const max = Math.max(range.min, range.max);
96
116
  return Math.floor(this.getValue(key) * (max - min + 1)) + min;
97
117
  }
98
118
  /**
99
- * Fisher-Yates shuffle with chained Mulberry32 state.
119
+ * Fisher-Yates shuffle with chained Mulberry32 state. Duplicate values
120
+ * (by string representation) are collapsed before shuffling, so a
121
+ * caller's slice off the front cannot accidentally produce a repeated
122
+ * value.
100
123
  */
101
124
  shuffle(key, items) {
102
- const result = Array.from(items).sort(__classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint));
125
+ if (items.length <= 1) {
126
+ return [...items];
127
+ }
128
+ const result = __classPrivateFieldGet(this, _Prng_instances, "m", _Prng_uniqueByCodePoint).call(this, items).sort(__classPrivateFieldGet(this, _Prng_instances, "m", _Prng_compareByCodePoint));
103
129
  const prng = new Mulberry32(Fnv1a.hash(__classPrivateFieldGet(this, _Prng_seed, "f") + ':' + key));
104
130
  for (let i = result.length - 1; i > 0; i--) {
105
131
  const j = Math.floor(prng.nextFloat() * (i + 1));
@@ -117,7 +143,18 @@ export class Prng {
117
143
  return new Mulberry32(Fnv1a.hash(__classPrivateFieldGet(this, _Prng_seed, "f") + ':' + key)).nextFloat();
118
144
  }
119
145
  }
120
- _Prng_seed = new WeakMap(), _Prng_instances = new WeakSet(), _Prng_compareByCodePoint = function _Prng_compareByCodePoint(a, b) {
146
+ _Prng_seed = new WeakMap(), _Prng_instances = new WeakSet(), _Prng_uniqueByCodePoint = function _Prng_uniqueByCodePoint(items, keyFn = String) {
147
+ const seen = new Set();
148
+ const result = [];
149
+ for (const item of items) {
150
+ const repr = keyFn(item);
151
+ if (!seen.has(repr)) {
152
+ seen.add(repr);
153
+ result.push(item);
154
+ }
155
+ }
156
+ return result;
157
+ }, _Prng_compareByCodePoint = function _Prng_compareByCodePoint(a, b) {
121
158
  const sa = String(a);
122
159
  const sb = String(b);
123
160
  if (sa < sb) {
package/lib/Renderer.js CHANGED
@@ -208,8 +208,17 @@ _Renderer_style = new WeakMap(), _Renderer_resolver = new WeakMap(), _Renderer_d
208
208
  __classPrivateFieldGet(this, _Renderer_defs, "f").set(id, `<g id="${id}">${body}</g>`);
209
209
  }
210
210
  const transforms = __classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_buildTransforms).call(this, component);
211
- const transformAttr = transforms.length > 0 ? ` transform="${transforms.join(' ')}"` : '';
212
- return `<use${transformAttr} href="#${id}"/>`;
211
+ const userAttributes = element.attributes();
212
+ let mergedAttributes = userAttributes;
213
+ if (transforms.length > 0) {
214
+ const userTransform = userAttributes?.transform;
215
+ const allParts = typeof userTransform === 'string' && userTransform.length > 0
216
+ ? [userTransform, ...transforms]
217
+ : transforms;
218
+ mergedAttributes = { ...userAttributes, transform: allParts.join(' ') };
219
+ }
220
+ const attrs = __classPrivateFieldGet(this, _Renderer_instances, "m", _Renderer_renderAttributes).call(this, mergedAttributes);
221
+ return `<use${attrs} href="#${id}"/>`;
213
222
  }, _Renderer_buildTransforms = function _Renderer_buildTransforms(component) {
214
223
  const { rotate, translateX, translateY, scale } = __classPrivateFieldGet(this, _Renderer_resolver, "f").componentTransform(component.name());
215
224
  if (translateX === 0 && translateY === 0 && rotate === 0 && scale === 1) {
package/lib/Resolver.d.ts CHANGED
@@ -5,8 +5,10 @@ import type { StyleOptionsFlipValue, StyleOptionsColorFillValue, StyleOptions }
5
5
  * Bundles the three inputs needed to derive any deterministic value for an
6
6
  * avatar — the {@link Style}, the validated user {@link Options}, and a
7
7
  * seeded {@link Prng} — and exposes them as named accessors. Each accessor
8
- * memoizes its result so that repeated calls cannot drift, and the memo
9
- * doubles as the snapshot returned by {@link resolved}.
8
+ * memoizes its result so that repeated calls cannot drift. The memo also
9
+ * serves as the informational snapshot returned by {@link resolved} — every
10
+ * value the resolver picks during one resolution lands there, except for
11
+ * the raw seed.
10
12
  */
11
13
  export declare class Resolver<D = unknown> {
12
14
  #private;
@@ -38,9 +40,10 @@ export declare class Resolver<D = unknown> {
38
40
  colorFill(name: string): StyleOptionsColorFillValue;
39
41
  colorAngle(name: string): number;
40
42
  /**
41
- * Per-component transform values are render-time decorations derived per
42
- * `<use>` reference, not user-input options that should round-trip. They
43
- * are intentionally not memoized and so never appear in {@link resolved}.
43
+ * Picks the rotate/translateX/translateY/scale values for a single
44
+ * component. Memoized per `name`, so the four values land in
45
+ * {@link resolved} as `${name}Rotate` / `${name}TranslateX` /
46
+ * `${name}TranslateY` / `${name}Scale` for downstream introspection.
44
47
  */
45
48
  componentTransform(name: string): {
46
49
  rotate: number;
@@ -49,11 +52,16 @@ export declare class Resolver<D = unknown> {
49
52
  scale: number;
50
53
  };
51
54
  /**
52
- * Returns every value that has been touched during this resolution. Only
53
- * memoized entries are included; unset options remain `undefined` and
54
- * disappear on `JSON.stringify()`. Per-component transform values from
55
- * {@link componentTransform} and the user-supplied {@link seed} are
56
- * intentionally not memoized and therefore do not appear in the snapshot.
55
+ * Returns an informational snapshot of every value the resolver picked.
56
+ * Includes top-level options (scale/rotate/translate/…), per-component
57
+ * variants/probabilities/colors, and per-component transform picks. The
58
+ * raw seed is deliberately excluded.
59
+ *
60
+ * The snapshot is NOT a round-trip-able options object — extra keys like
61
+ * `${name}Rotate` are not part of {@link StyleOptions} and feeding the
62
+ * snapshot back into a new {@link Avatar} is not supported. Callers that
63
+ * need to reproduce an avatar should pass the original `seed` and
64
+ * user-supplied options.
57
65
  *
58
66
  * The returned object aliases the internal cache; callers that need
59
67
  * isolation (e.g. {@link Avatar.toJSON}) clone it themselves.
package/lib/Resolver.js CHANGED
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _Resolver_instances, _Resolver_style, _Resolver_options, _Resolver_prng, _Resolver_colorResolving, _Resolver_result, _Resolver_probability, _Resolver_isVisible, _Resolver_resolveColor, _Resolver_colorFillStops, _Resolver_pickFloat, _Resolver_memo;
12
+ var _Resolver_instances, _Resolver_style, _Resolver_options, _Resolver_prng, _Resolver_colorResolving, _Resolver_result, _Resolver_probability, _Resolver_isVisible, _Resolver_resolveColor, _Resolver_colorFillStops, _Resolver_memoFloat, _Resolver_memo;
13
13
  import { Prng } from './Prng.js';
14
14
  import { Color } from './Utils/Color.js';
15
15
  import { CircularColorReferenceError } from './Error/CircularColorReferenceError.js';
@@ -17,8 +17,10 @@ import { CircularColorReferenceError } from './Error/CircularColorReferenceError
17
17
  * Bundles the three inputs needed to derive any deterministic value for an
18
18
  * avatar — the {@link Style}, the validated user {@link Options}, and a
19
19
  * seeded {@link Prng} — and exposes them as named accessors. Each accessor
20
- * memoizes its result so that repeated calls cannot drift, and the memo
21
- * doubles as the snapshot returned by {@link resolved}.
20
+ * memoizes its result so that repeated calls cannot drift. The memo also
21
+ * serves as the informational snapshot returned by {@link resolved} — every
22
+ * value the resolver picks during one resolution lands there, except for
23
+ * the raw seed.
22
24
  */
23
25
  export class Resolver {
24
26
  constructor(style, options) {
@@ -33,7 +35,8 @@ export class Resolver {
33
35
  __classPrivateFieldSet(this, _Resolver_prng, new Prng(this.seed()), "f");
34
36
  }
35
37
  seed() {
36
- // Not memoized so the raw seed stays out of {@link resolved}.
38
+ // Deliberately not memoized the seed is the only input we keep out of
39
+ // the {@link resolved} snapshot, so a serialized avatar never leaks it.
37
40
  return __classPrivateFieldGet(this, _Resolver_options, "f").seed() ?? '';
38
41
  }
39
42
  size() {
@@ -49,25 +52,26 @@ export class Resolver {
49
52
  return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'flip', () => __classPrivateFieldGet(this, _Resolver_prng, "f").pick('flip', __classPrivateFieldGet(this, _Resolver_options, "f").flip()) ?? 'none');
50
53
  }
51
54
  fontFamily() {
52
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'fontFamily', () => __classPrivateFieldGet(this, _Resolver_prng, "f").pick('fontFamily', __classPrivateFieldGet(this, _Resolver_options, "f").fontFamily()) ?? 'system-ui');
55
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'fontFamily', () => __classPrivateFieldGet(this, _Resolver_prng, "f").pick('fontFamily', __classPrivateFieldGet(this, _Resolver_options, "f").fontFamily()) ??
56
+ 'system-ui');
53
57
  }
54
58
  fontWeight() {
55
59
  return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'fontWeight', () => __classPrivateFieldGet(this, _Resolver_prng, "f").pick('fontWeight', __classPrivateFieldGet(this, _Resolver_options, "f").fontWeight()) ?? 400);
56
60
  }
57
61
  scale() {
58
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'scale', () => __classPrivateFieldGet(this, _Resolver_prng, "f").float('scale', __classPrivateFieldGet(this, _Resolver_options, "f").scale()) ?? 1);
62
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, 'scale', __classPrivateFieldGet(this, _Resolver_options, "f").scale(), 1);
59
63
  }
60
64
  borderRadius() {
61
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'borderRadius', () => __classPrivateFieldGet(this, _Resolver_prng, "f").float('borderRadius', __classPrivateFieldGet(this, _Resolver_options, "f").borderRadius()) ?? 0);
65
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, 'borderRadius', __classPrivateFieldGet(this, _Resolver_options, "f").borderRadius(), 0);
62
66
  }
63
67
  rotate() {
64
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'rotate', () => __classPrivateFieldGet(this, _Resolver_prng, "f").float('rotate', __classPrivateFieldGet(this, _Resolver_options, "f").rotate()) ?? 0);
68
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, 'rotate', __classPrivateFieldGet(this, _Resolver_options, "f").rotate(), 0);
65
69
  }
66
70
  translateX() {
67
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'translateX', () => __classPrivateFieldGet(this, _Resolver_prng, "f").float('translateX', __classPrivateFieldGet(this, _Resolver_options, "f").translateX()) ?? 0);
71
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, 'translateX', __classPrivateFieldGet(this, _Resolver_options, "f").translateX(), 0);
68
72
  }
69
73
  translateY() {
70
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, 'translateY', () => __classPrivateFieldGet(this, _Resolver_prng, "f").float('translateY', __classPrivateFieldGet(this, _Resolver_options, "f").translateY()) ?? 0);
74
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, 'translateY', __classPrivateFieldGet(this, _Resolver_options, "f").translateY(), 0);
71
75
  }
72
76
  /**
73
77
  * Selects a variant for the given component. Depending on what was passed
@@ -113,28 +117,34 @@ export class Resolver {
113
117
  'solid');
114
118
  }
115
119
  colorAngle(name) {
116
- return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, `${name}ColorAngle`, () => __classPrivateFieldGet(this, _Resolver_prng, "f").float(`${name}ColorAngle`, __classPrivateFieldGet(this, _Resolver_options, "f").colorAngle(name)) ?? 0);
120
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, `${name}ColorAngle`, __classPrivateFieldGet(this, _Resolver_options, "f").colorAngle(name), 0);
117
121
  }
118
122
  /**
119
- * Per-component transform values are render-time decorations derived per
120
- * `<use>` reference, not user-input options that should round-trip. They
121
- * are intentionally not memoized and so never appear in {@link resolved}.
123
+ * Picks the rotate/translateX/translateY/scale values for a single
124
+ * component. Memoized per `name`, so the four values land in
125
+ * {@link resolved} as `${name}Rotate` / `${name}TranslateX` /
126
+ * `${name}TranslateY` / `${name}Scale` for downstream introspection.
122
127
  */
123
128
  componentTransform(name) {
124
129
  const component = __classPrivateFieldGet(this, _Resolver_style, "f").components().get(name);
125
130
  return {
126
- rotate: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_pickFloat).call(this, name, 'Rotate', component?.rotate() ?? [], 0),
127
- translateX: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_pickFloat).call(this, name, 'TranslateX', component?.translate().x() ?? [], 0),
128
- translateY: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_pickFloat).call(this, name, 'TranslateY', component?.translate().y() ?? [], 0),
129
- scale: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_pickFloat).call(this, name, 'Scale', component?.scale() ?? [], 1),
131
+ rotate: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, `${name}Rotate`, component?.rotate(), 0),
132
+ translateX: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, `${name}TranslateX`, component?.translate().x(), 0),
133
+ translateY: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, `${name}TranslateY`, component?.translate().y(), 0),
134
+ scale: __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memoFloat).call(this, `${name}Scale`, component?.scale(), 1),
130
135
  };
131
136
  }
132
137
  /**
133
- * Returns every value that has been touched during this resolution. Only
134
- * memoized entries are included; unset options remain `undefined` and
135
- * disappear on `JSON.stringify()`. Per-component transform values from
136
- * {@link componentTransform} and the user-supplied {@link seed} are
137
- * intentionally not memoized and therefore do not appear in the snapshot.
138
+ * Returns an informational snapshot of every value the resolver picked.
139
+ * Includes top-level options (scale/rotate/translate/…), per-component
140
+ * variants/probabilities/colors, and per-component transform picks. The
141
+ * raw seed is deliberately excluded.
142
+ *
143
+ * The snapshot is NOT a round-trip-able options object — extra keys like
144
+ * `${name}Rotate` are not part of {@link StyleOptions} and feeding the
145
+ * snapshot back into a new {@link Avatar} is not supported. Callers that
146
+ * need to reproduce an avatar should pass the original `seed` and
147
+ * user-supplied options.
138
148
  *
139
149
  * The returned object aliases the internal cache; callers that need
140
150
  * isolation (e.g. {@link Avatar.toJSON}) clone it themselves.
@@ -191,9 +201,10 @@ _Resolver_style = new WeakMap(), _Resolver_options = new WeakMap(), _Resolver_pr
191
201
  : __classPrivateFieldGet(this, _Resolver_prng, "f").shuffle(`${name}Color`, candidates);
192
202
  return ordered.slice(0, stops);
193
203
  }, _Resolver_colorFillStops = function _Resolver_colorFillStops(name) {
194
- return (__classPrivateFieldGet(this, _Resolver_prng, "f").integer(`${name}ColorFillStops`, __classPrivateFieldGet(this, _Resolver_options, "f").colorFillStops(name)) ?? 2);
195
- }, _Resolver_pickFloat = function _Resolver_pickFloat(name, suffix, range, fallback) {
196
- return __classPrivateFieldGet(this, _Resolver_prng, "f").float(`${name}${suffix}`, range) ?? fallback;
204
+ const range = __classPrivateFieldGet(this, _Resolver_options, "f").colorFillStops(name);
205
+ return range ? __classPrivateFieldGet(this, _Resolver_prng, "f").integer(`${name}ColorFillStops`, range) : 2;
206
+ }, _Resolver_memoFloat = function _Resolver_memoFloat(key, range, fallback) {
207
+ return __classPrivateFieldGet(this, _Resolver_instances, "m", _Resolver_memo).call(this, key, () => range ? __classPrivateFieldGet(this, _Resolver_prng, "f").float(key, range) : fallback);
197
208
  }, _Resolver_memo = function _Resolver_memo(key, compute) {
198
209
  if (key in __classPrivateFieldGet(this, _Resolver_result, "f")) {
199
210
  return __classPrivateFieldGet(this, _Resolver_result, "f")[key];
@@ -1,4 +1,4 @@
1
- import type { StyleDefinitionComponent } from '../StyleDefinition.js';
1
+ import type { Range, StyleDefinitionComponent } from '../StyleDefinition.js';
2
2
  import { ComponentTranslate } from './ComponentTranslate.js';
3
3
  import { ComponentVariant } from './ComponentVariant.js';
4
4
  /**
@@ -43,13 +43,15 @@ export declare class Component {
43
43
  */
44
44
  probability(): number;
45
45
  /**
46
- * Returns the rotation range definition. Aliases delegate to the source.
46
+ * Returns the rotation range, or `undefined` when unset.
47
+ * Aliases delegate to the source.
47
48
  */
48
- rotate(): readonly number[];
49
+ rotate(): Range | undefined;
49
50
  /**
50
- * Returns the scale range definition. Aliases delegate to the source.
51
+ * Returns the scale range, or `undefined` when unset.
52
+ * Aliases delegate to the source.
51
53
  */
52
- scale(): readonly number[];
54
+ scale(): Range | undefined;
53
55
  /**
54
56
  * Returns the translate descriptor. Aliases delegate to the source.
55
57
  */
@@ -78,22 +78,18 @@ export class Component {
78
78
  return __classPrivateFieldGet(this, _Component_instances, "m", _Component_asBase).call(this).probability ?? 100;
79
79
  }
80
80
  /**
81
- * Returns the rotation range definition. Aliases delegate to the source.
81
+ * Returns the rotation range, or `undefined` when unset.
82
+ * Aliases delegate to the source.
82
83
  */
83
84
  rotate() {
84
- if (__classPrivateFieldGet(this, _Component_source, "f")) {
85
- return __classPrivateFieldGet(this, _Component_source, "f").rotate();
86
- }
87
- return __classPrivateFieldGet(this, _Component_instances, "m", _Component_asBase).call(this).rotate ?? [];
85
+ return __classPrivateFieldGet(this, _Component_source, "f") ? __classPrivateFieldGet(this, _Component_source, "f").rotate() : __classPrivateFieldGet(this, _Component_instances, "m", _Component_asBase).call(this).rotate;
88
86
  }
89
87
  /**
90
- * Returns the scale range definition. Aliases delegate to the source.
88
+ * Returns the scale range, or `undefined` when unset.
89
+ * Aliases delegate to the source.
91
90
  */
92
91
  scale() {
93
- if (__classPrivateFieldGet(this, _Component_source, "f")) {
94
- return __classPrivateFieldGet(this, _Component_source, "f").scale();
95
- }
96
- return __classPrivateFieldGet(this, _Component_instances, "m", _Component_asBase).call(this).scale ?? [];
92
+ return __classPrivateFieldGet(this, _Component_source, "f") ? __classPrivateFieldGet(this, _Component_source, "f").scale() : __classPrivateFieldGet(this, _Component_instances, "m", _Component_asBase).call(this).scale;
97
93
  }
98
94
  /**
99
95
  * Returns the translate descriptor. Aliases delegate to the source.
@@ -1,4 +1,4 @@
1
- import type { StyleDefinitionComponentTranslate } from '../StyleDefinition.js';
1
+ import type { Range, StyleDefinitionComponentTranslate } from '../StyleDefinition.js';
2
2
  /**
3
3
  * Read-only view over a component's `translate` block, providing the X and Y
4
4
  * offset ranges.
@@ -6,12 +6,6 @@ import type { StyleDefinitionComponentTranslate } from '../StyleDefinition.js';
6
6
  export declare class ComponentTranslate {
7
7
  #private;
8
8
  constructor(data: StyleDefinitionComponentTranslate);
9
- /**
10
- * Returns the horizontal offset range, or an empty list when unset.
11
- */
12
- x(): readonly number[];
13
- /**
14
- * Returns the vertical offset range, or an empty list when unset.
15
- */
16
- y(): readonly number[];
9
+ x(): Range | undefined;
10
+ y(): Range | undefined;
17
11
  }
@@ -19,17 +19,11 @@ export class ComponentTranslate {
19
19
  _ComponentTranslate_data.set(this, void 0);
20
20
  __classPrivateFieldSet(this, _ComponentTranslate_data, data, "f");
21
21
  }
22
- /**
23
- * Returns the horizontal offset range, or an empty list when unset.
24
- */
25
22
  x() {
26
- return __classPrivateFieldGet(this, _ComponentTranslate_data, "f").x ?? [];
23
+ return __classPrivateFieldGet(this, _ComponentTranslate_data, "f").x;
27
24
  }
28
- /**
29
- * Returns the vertical offset range, or an empty list when unset.
30
- */
31
25
  y() {
32
- return __classPrivateFieldGet(this, _ComponentTranslate_data, "f").y ?? [];
26
+ return __classPrivateFieldGet(this, _ComponentTranslate_data, "f").y;
33
27
  }
34
28
  }
35
29
  _ComponentTranslate_data = new WeakMap();