@usenavii/core 0.5.0 → 0.7.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/CHANGELOG.md +178 -5
- package/README.md +54 -8
- package/dist/index.cjs +187 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -4
- package/dist/index.d.ts +57 -4
- package/dist/index.js +185 -6
- package/dist/index.js.map +1 -1
- package/dist/parts.d.cts +1 -1
- package/dist/parts.d.ts +1 -1
- package/dist/{types-CF0rfKly.d.cts → types-BtX6LIyn.d.cts} +20 -1
- package/dist/{types-CF0rfKly.d.ts → types-BtX6LIyn.d.ts} +20 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as AvatarOptions, c as AvatarSpec, d as BodyShapeId, E as EyeStyleId,
|
|
2
|
-
export { S as StyleHint } from './types-
|
|
1
|
+
import { b as AvatarOptions, c as AvatarSpec, d as BodyShapeId, E as EyeStyleId, e as MouthStyleId, a as AntennaStyleId, A as AccessoryId, B as BackgroundId, T as TopperId, O as OutfitId, P as Palette } from './types-BtX6LIyn.cjs';
|
|
2
|
+
export { M as MoodId, S as StyleHint } from './types-BtX6LIyn.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Seed → stable PRNG stream.
|
|
@@ -82,6 +82,28 @@ declare function renderGroup(seeds: string[], options?: GroupOptions): string;
|
|
|
82
82
|
* it with `createdAt` makes the result globally unique while remaining
|
|
83
83
|
* stable across renders (assuming `createdAt` is set once at signup).
|
|
84
84
|
*/
|
|
85
|
+
/**
|
|
86
|
+
* Normalize an email the same way Gravatar does — trim + lowercase, NFC.
|
|
87
|
+
* Exported so callers can reproduce the canonical form before hashing.
|
|
88
|
+
*/
|
|
89
|
+
declare function normalizeEmail(email: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Turn an email into a stable, opaque seed using Gravatar's scheme:
|
|
92
|
+
* `sha256(trim(lowercase(email)))` → lowercase hex.
|
|
93
|
+
*
|
|
94
|
+
* Why: passing raw emails as seeds leaks them through URLs (server logs,
|
|
95
|
+
* Referer headers, browser history, CDN cache keys, analytics). The hash
|
|
96
|
+
* is stable across systems that normalize the same way, so two products
|
|
97
|
+
* looking up the same person get the same avatar.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const s = seedFromEmail(user.email);
|
|
102
|
+
* createAvatar(s); // safe to log
|
|
103
|
+
* // or hit the API: `/avatar/${s}.svg` // no plaintext email on the wire
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
declare function seedFromEmail(email: string): string;
|
|
85
107
|
interface SeedFields {
|
|
86
108
|
/** Stable primary key (database id, UUID, OAuth sub). Best choice. */
|
|
87
109
|
id?: string | number | null | undefined;
|
|
@@ -92,6 +114,18 @@ interface SeedFields {
|
|
|
92
114
|
/** Account creation time. Combined with `name` to bake uniqueness in at signup. */
|
|
93
115
|
createdAt?: string | number | Date | null | undefined;
|
|
94
116
|
}
|
|
117
|
+
interface SeedOptions {
|
|
118
|
+
/**
|
|
119
|
+
* When the email branch is used, hash the email instead of returning it
|
|
120
|
+
* raw. Hashing keeps the seed stable but stops the address from leaking
|
|
121
|
+
* into URLs, server logs, and Referer headers. Default `true` from v1.
|
|
122
|
+
*
|
|
123
|
+
* Set to `false` to opt back into the legacy plaintext-email behavior —
|
|
124
|
+
* useful for migrations where existing avatars are keyed off the raw
|
|
125
|
+
* email and you don't want every user's face to change.
|
|
126
|
+
*/
|
|
127
|
+
hashEmail?: boolean;
|
|
128
|
+
}
|
|
95
129
|
/**
|
|
96
130
|
* Compose a stable seed string from the most unique field available.
|
|
97
131
|
*
|
|
@@ -107,9 +141,27 @@ interface SeedFields {
|
|
|
107
141
|
* // → "Alice|1700000000000"
|
|
108
142
|
* ```
|
|
109
143
|
*
|
|
144
|
+
* @example Opt out of email hashing (legacy behavior):
|
|
145
|
+
* ```ts
|
|
146
|
+
* seed({ email: 'a@b.c' }, { hashEmail: false });
|
|
147
|
+
* // → "a@b.c" — avoid; only for migrating off the old default.
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
110
150
|
* @throws if no usable field is provided.
|
|
111
151
|
*/
|
|
112
|
-
declare function seed(fields: SeedFields): string;
|
|
152
|
+
declare function seed(fields: SeedFields, options?: SeedOptions): string;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Sync SHA-256 (FIPS 180-4) → lowercase hex.
|
|
156
|
+
*
|
|
157
|
+
* Used by `seedFromEmail()` to match Gravatar's seed format
|
|
158
|
+
* (sha256 of trimmed, lowercased email). Sync API on purpose —
|
|
159
|
+
* a seed helper that returns a Promise is a footgun in render code.
|
|
160
|
+
*
|
|
161
|
+
* ~50 lines, no deps, zero allocations after init. Not for crypto
|
|
162
|
+
* use beyond opaque-identifier hashing.
|
|
163
|
+
*/
|
|
164
|
+
declare function sha256Hex(input: string): string;
|
|
113
165
|
|
|
114
166
|
/**
|
|
115
167
|
* Direct construction of an avatar from explicit part choices — no seed.
|
|
@@ -382,7 +434,8 @@ declare const Navii: {
|
|
|
382
434
|
readonly select: typeof selectAvatar;
|
|
383
435
|
readonly group: typeof renderGroup;
|
|
384
436
|
readonly seed: typeof seed;
|
|
437
|
+
readonly seedFromEmail: typeof seedFromEmail;
|
|
385
438
|
readonly build: typeof build;
|
|
386
439
|
};
|
|
387
440
|
|
|
388
|
-
export { AccessoryId, AntennaStyleId, AvatarOptions, AvatarSpec, BUILT_IN_PACKS, BackgroundId, BodyShapeId, type BuildSpec, EyeStyleId, type GroupOptions, MouthStyleId, Navii, OutfitId, PACK_REGISTRY, type Pack, type PackRegistry, Palette, type SeedFields, TopperId, build, createAvatar, createRng, cyrb53, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, selectAvatar };
|
|
441
|
+
export { AccessoryId, AntennaStyleId, AvatarOptions, AvatarSpec, BUILT_IN_PACKS, BackgroundId, BodyShapeId, type BuildSpec, EyeStyleId, type GroupOptions, MouthStyleId, Navii, OutfitId, PACK_REGISTRY, type Pack, type PackRegistry, Palette, type SeedFields, type SeedOptions, TopperId, build, createAvatar, createRng, cyrb53, normalizeEmail, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, seedFromEmail, selectAvatar, sha256Hex };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as AvatarOptions, c as AvatarSpec, d as BodyShapeId, E as EyeStyleId,
|
|
2
|
-
export { S as StyleHint } from './types-
|
|
1
|
+
import { b as AvatarOptions, c as AvatarSpec, d as BodyShapeId, E as EyeStyleId, e as MouthStyleId, a as AntennaStyleId, A as AccessoryId, B as BackgroundId, T as TopperId, O as OutfitId, P as Palette } from './types-BtX6LIyn.js';
|
|
2
|
+
export { M as MoodId, S as StyleHint } from './types-BtX6LIyn.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Seed → stable PRNG stream.
|
|
@@ -82,6 +82,28 @@ declare function renderGroup(seeds: string[], options?: GroupOptions): string;
|
|
|
82
82
|
* it with `createdAt` makes the result globally unique while remaining
|
|
83
83
|
* stable across renders (assuming `createdAt` is set once at signup).
|
|
84
84
|
*/
|
|
85
|
+
/**
|
|
86
|
+
* Normalize an email the same way Gravatar does — trim + lowercase, NFC.
|
|
87
|
+
* Exported so callers can reproduce the canonical form before hashing.
|
|
88
|
+
*/
|
|
89
|
+
declare function normalizeEmail(email: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Turn an email into a stable, opaque seed using Gravatar's scheme:
|
|
92
|
+
* `sha256(trim(lowercase(email)))` → lowercase hex.
|
|
93
|
+
*
|
|
94
|
+
* Why: passing raw emails as seeds leaks them through URLs (server logs,
|
|
95
|
+
* Referer headers, browser history, CDN cache keys, analytics). The hash
|
|
96
|
+
* is stable across systems that normalize the same way, so two products
|
|
97
|
+
* looking up the same person get the same avatar.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const s = seedFromEmail(user.email);
|
|
102
|
+
* createAvatar(s); // safe to log
|
|
103
|
+
* // or hit the API: `/avatar/${s}.svg` // no plaintext email on the wire
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
declare function seedFromEmail(email: string): string;
|
|
85
107
|
interface SeedFields {
|
|
86
108
|
/** Stable primary key (database id, UUID, OAuth sub). Best choice. */
|
|
87
109
|
id?: string | number | null | undefined;
|
|
@@ -92,6 +114,18 @@ interface SeedFields {
|
|
|
92
114
|
/** Account creation time. Combined with `name` to bake uniqueness in at signup. */
|
|
93
115
|
createdAt?: string | number | Date | null | undefined;
|
|
94
116
|
}
|
|
117
|
+
interface SeedOptions {
|
|
118
|
+
/**
|
|
119
|
+
* When the email branch is used, hash the email instead of returning it
|
|
120
|
+
* raw. Hashing keeps the seed stable but stops the address from leaking
|
|
121
|
+
* into URLs, server logs, and Referer headers. Default `true` from v1.
|
|
122
|
+
*
|
|
123
|
+
* Set to `false` to opt back into the legacy plaintext-email behavior —
|
|
124
|
+
* useful for migrations where existing avatars are keyed off the raw
|
|
125
|
+
* email and you don't want every user's face to change.
|
|
126
|
+
*/
|
|
127
|
+
hashEmail?: boolean;
|
|
128
|
+
}
|
|
95
129
|
/**
|
|
96
130
|
* Compose a stable seed string from the most unique field available.
|
|
97
131
|
*
|
|
@@ -107,9 +141,27 @@ interface SeedFields {
|
|
|
107
141
|
* // → "Alice|1700000000000"
|
|
108
142
|
* ```
|
|
109
143
|
*
|
|
144
|
+
* @example Opt out of email hashing (legacy behavior):
|
|
145
|
+
* ```ts
|
|
146
|
+
* seed({ email: 'a@b.c' }, { hashEmail: false });
|
|
147
|
+
* // → "a@b.c" — avoid; only for migrating off the old default.
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
110
150
|
* @throws if no usable field is provided.
|
|
111
151
|
*/
|
|
112
|
-
declare function seed(fields: SeedFields): string;
|
|
152
|
+
declare function seed(fields: SeedFields, options?: SeedOptions): string;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Sync SHA-256 (FIPS 180-4) → lowercase hex.
|
|
156
|
+
*
|
|
157
|
+
* Used by `seedFromEmail()` to match Gravatar's seed format
|
|
158
|
+
* (sha256 of trimmed, lowercased email). Sync API on purpose —
|
|
159
|
+
* a seed helper that returns a Promise is a footgun in render code.
|
|
160
|
+
*
|
|
161
|
+
* ~50 lines, no deps, zero allocations after init. Not for crypto
|
|
162
|
+
* use beyond opaque-identifier hashing.
|
|
163
|
+
*/
|
|
164
|
+
declare function sha256Hex(input: string): string;
|
|
113
165
|
|
|
114
166
|
/**
|
|
115
167
|
* Direct construction of an avatar from explicit part choices — no seed.
|
|
@@ -382,7 +434,8 @@ declare const Navii: {
|
|
|
382
434
|
readonly select: typeof selectAvatar;
|
|
383
435
|
readonly group: typeof renderGroup;
|
|
384
436
|
readonly seed: typeof seed;
|
|
437
|
+
readonly seedFromEmail: typeof seedFromEmail;
|
|
385
438
|
readonly build: typeof build;
|
|
386
439
|
};
|
|
387
440
|
|
|
388
|
-
export { AccessoryId, AntennaStyleId, AvatarOptions, AvatarSpec, BUILT_IN_PACKS, BackgroundId, BodyShapeId, type BuildSpec, EyeStyleId, type GroupOptions, MouthStyleId, Navii, OutfitId, PACK_REGISTRY, type Pack, type PackRegistry, Palette, type SeedFields, TopperId, build, createAvatar, createRng, cyrb53, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, selectAvatar };
|
|
441
|
+
export { AccessoryId, AntennaStyleId, AvatarOptions, AvatarSpec, BUILT_IN_PACKS, BackgroundId, BodyShapeId, type BuildSpec, EyeStyleId, type GroupOptions, MouthStyleId, Navii, OutfitId, PACK_REGISTRY, type Pack, type PackRegistry, Palette, type SeedFields, type SeedOptions, TopperId, build, createAvatar, createRng, cyrb53, normalizeEmail, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, seedFromEmail, selectAvatar, sha256Hex };
|
package/dist/index.js
CHANGED
|
@@ -1337,6 +1337,18 @@ function resolvePacks(ids) {
|
|
|
1337
1337
|
}
|
|
1338
1338
|
|
|
1339
1339
|
// src/select.ts
|
|
1340
|
+
var MOOD_EYES = {
|
|
1341
|
+
happy: "wide",
|
|
1342
|
+
serious: "squint",
|
|
1343
|
+
sleepy: "sleepy",
|
|
1344
|
+
wink: "wink"
|
|
1345
|
+
};
|
|
1346
|
+
var MOOD_MOUTH = {
|
|
1347
|
+
happy: "smile",
|
|
1348
|
+
serious: "flat",
|
|
1349
|
+
sleepy: "dot",
|
|
1350
|
+
wink: "smirk"
|
|
1351
|
+
};
|
|
1340
1352
|
function applyStyleHint(pool, packs, hint, partKey) {
|
|
1341
1353
|
for (const pack of packs) {
|
|
1342
1354
|
const subset = pack.styleHints?.[hint]?.[partKey];
|
|
@@ -1398,10 +1410,13 @@ function selectAvatar(seed2, options = {}) {
|
|
|
1398
1410
|
topperPool = applyStyleHint(topperPool, enabledPacks, styleHint, "topper");
|
|
1399
1411
|
}
|
|
1400
1412
|
const body = rng.pick(bodyPool);
|
|
1401
|
-
const
|
|
1402
|
-
const
|
|
1413
|
+
const eyesPicked = rng.pick(eyesPool);
|
|
1414
|
+
const mouthPicked = rng.pick(mouthPool);
|
|
1403
1415
|
const antenna = rng.pick(antennaPool);
|
|
1404
1416
|
const accessory = rng.pick(accessoryPool);
|
|
1417
|
+
const mood = options.mood;
|
|
1418
|
+
const eyes = mood && mood !== "neutral" ? MOOD_EYES[mood] : eyesPicked;
|
|
1419
|
+
const mouth = mood && mood !== "neutral" ? MOOD_MOUTH[mood] : mouthPicked;
|
|
1405
1420
|
let background;
|
|
1406
1421
|
if (typeof options.background === "string") {
|
|
1407
1422
|
background = options.background;
|
|
@@ -1651,13 +1666,176 @@ function clamp(n, lo, hi) {
|
|
|
1651
1666
|
return Math.max(lo, Math.min(hi, n));
|
|
1652
1667
|
}
|
|
1653
1668
|
|
|
1669
|
+
// src/sha256.ts
|
|
1670
|
+
var K = new Uint32Array([
|
|
1671
|
+
1116352408,
|
|
1672
|
+
1899447441,
|
|
1673
|
+
3049323471,
|
|
1674
|
+
3921009573,
|
|
1675
|
+
961987163,
|
|
1676
|
+
1508970993,
|
|
1677
|
+
2453635748,
|
|
1678
|
+
2870763221,
|
|
1679
|
+
3624381080,
|
|
1680
|
+
310598401,
|
|
1681
|
+
607225278,
|
|
1682
|
+
1426881987,
|
|
1683
|
+
1925078388,
|
|
1684
|
+
2162078206,
|
|
1685
|
+
2614888103,
|
|
1686
|
+
3248222580,
|
|
1687
|
+
3835390401,
|
|
1688
|
+
4022224774,
|
|
1689
|
+
264347078,
|
|
1690
|
+
604807628,
|
|
1691
|
+
770255983,
|
|
1692
|
+
1249150122,
|
|
1693
|
+
1555081692,
|
|
1694
|
+
1996064986,
|
|
1695
|
+
2554220882,
|
|
1696
|
+
2821834349,
|
|
1697
|
+
2952996808,
|
|
1698
|
+
3210313671,
|
|
1699
|
+
3336571891,
|
|
1700
|
+
3584528711,
|
|
1701
|
+
113926993,
|
|
1702
|
+
338241895,
|
|
1703
|
+
666307205,
|
|
1704
|
+
773529912,
|
|
1705
|
+
1294757372,
|
|
1706
|
+
1396182291,
|
|
1707
|
+
1695183700,
|
|
1708
|
+
1986661051,
|
|
1709
|
+
2177026350,
|
|
1710
|
+
2456956037,
|
|
1711
|
+
2730485921,
|
|
1712
|
+
2820302411,
|
|
1713
|
+
3259730800,
|
|
1714
|
+
3345764771,
|
|
1715
|
+
3516065817,
|
|
1716
|
+
3600352804,
|
|
1717
|
+
4094571909,
|
|
1718
|
+
275423344,
|
|
1719
|
+
430227734,
|
|
1720
|
+
506948616,
|
|
1721
|
+
659060556,
|
|
1722
|
+
883997877,
|
|
1723
|
+
958139571,
|
|
1724
|
+
1322822218,
|
|
1725
|
+
1537002063,
|
|
1726
|
+
1747873779,
|
|
1727
|
+
1955562222,
|
|
1728
|
+
2024104815,
|
|
1729
|
+
2227730452,
|
|
1730
|
+
2361852424,
|
|
1731
|
+
2428436474,
|
|
1732
|
+
2756734187,
|
|
1733
|
+
3204031479,
|
|
1734
|
+
3329325298
|
|
1735
|
+
]);
|
|
1736
|
+
function rotr(x, n) {
|
|
1737
|
+
return (x >>> n | x << 32 - n) >>> 0;
|
|
1738
|
+
}
|
|
1739
|
+
function utf8Encode(str) {
|
|
1740
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(str);
|
|
1741
|
+
const out = [];
|
|
1742
|
+
for (let i = 0; i < str.length; i++) {
|
|
1743
|
+
let c = str.charCodeAt(i);
|
|
1744
|
+
if (c < 128) out.push(c);
|
|
1745
|
+
else if (c < 2048) {
|
|
1746
|
+
out.push(192 | c >> 6, 128 | c & 63);
|
|
1747
|
+
} else if (c < 55296 || c >= 57344) {
|
|
1748
|
+
out.push(224 | c >> 12, 128 | c >> 6 & 63, 128 | c & 63);
|
|
1749
|
+
} else {
|
|
1750
|
+
i++;
|
|
1751
|
+
c = 65536 + ((c & 1023) << 10 | str.charCodeAt(i) & 1023);
|
|
1752
|
+
out.push(
|
|
1753
|
+
240 | c >> 18,
|
|
1754
|
+
128 | c >> 12 & 63,
|
|
1755
|
+
128 | c >> 6 & 63,
|
|
1756
|
+
128 | c & 63
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return new Uint8Array(out);
|
|
1761
|
+
}
|
|
1762
|
+
function sha256Hex(input) {
|
|
1763
|
+
const msg = utf8Encode(input);
|
|
1764
|
+
const bitLen = msg.length * 8;
|
|
1765
|
+
const padLen = (msg.length + 9 + 63 & -64) - msg.length;
|
|
1766
|
+
const buf = new Uint8Array(msg.length + padLen);
|
|
1767
|
+
buf.set(msg);
|
|
1768
|
+
buf[msg.length] = 128;
|
|
1769
|
+
const dv = new DataView(buf.buffer);
|
|
1770
|
+
dv.setUint32(buf.length - 4, bitLen >>> 0, false);
|
|
1771
|
+
dv.setUint32(buf.length - 8, Math.floor(bitLen / 4294967296), false);
|
|
1772
|
+
const H = new Uint32Array([
|
|
1773
|
+
1779033703,
|
|
1774
|
+
3144134277,
|
|
1775
|
+
1013904242,
|
|
1776
|
+
2773480762,
|
|
1777
|
+
1359893119,
|
|
1778
|
+
2600822924,
|
|
1779
|
+
528734635,
|
|
1780
|
+
1541459225
|
|
1781
|
+
]);
|
|
1782
|
+
const W = new Uint32Array(64);
|
|
1783
|
+
for (let chunk = 0; chunk < buf.length; chunk += 64) {
|
|
1784
|
+
for (let i = 0; i < 16; i++) W[i] = dv.getUint32(chunk + i * 4, false);
|
|
1785
|
+
for (let i = 16; i < 64; i++) {
|
|
1786
|
+
const s0 = rotr(W[i - 15], 7) ^ rotr(W[i - 15], 18) ^ W[i - 15] >>> 3;
|
|
1787
|
+
const s1 = rotr(W[i - 2], 17) ^ rotr(W[i - 2], 19) ^ W[i - 2] >>> 10;
|
|
1788
|
+
W[i] = W[i - 16] + s0 + W[i - 7] + s1 >>> 0;
|
|
1789
|
+
}
|
|
1790
|
+
let a = H[0], b = H[1], c = H[2], d = H[3];
|
|
1791
|
+
let e = H[4], f = H[5], g = H[6], h = H[7];
|
|
1792
|
+
for (let i = 0; i < 64; i++) {
|
|
1793
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1794
|
+
const ch = e & f ^ ~e & g;
|
|
1795
|
+
const t1 = h + S1 + ch + K[i] + W[i] >>> 0;
|
|
1796
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1797
|
+
const mj = a & b ^ a & c ^ b & c;
|
|
1798
|
+
const t2 = S0 + mj >>> 0;
|
|
1799
|
+
h = g;
|
|
1800
|
+
g = f;
|
|
1801
|
+
f = e;
|
|
1802
|
+
e = d + t1 >>> 0;
|
|
1803
|
+
d = c;
|
|
1804
|
+
c = b;
|
|
1805
|
+
b = a;
|
|
1806
|
+
a = t1 + t2 >>> 0;
|
|
1807
|
+
}
|
|
1808
|
+
H[0] = H[0] + a >>> 0;
|
|
1809
|
+
H[1] = H[1] + b >>> 0;
|
|
1810
|
+
H[2] = H[2] + c >>> 0;
|
|
1811
|
+
H[3] = H[3] + d >>> 0;
|
|
1812
|
+
H[4] = H[4] + e >>> 0;
|
|
1813
|
+
H[5] = H[5] + f >>> 0;
|
|
1814
|
+
H[6] = H[6] + g >>> 0;
|
|
1815
|
+
H[7] = H[7] + h >>> 0;
|
|
1816
|
+
}
|
|
1817
|
+
let out = "";
|
|
1818
|
+
for (let i = 0; i < 8; i++) out += H[i].toString(16).padStart(8, "0");
|
|
1819
|
+
return out;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1654
1822
|
// src/seed.ts
|
|
1655
|
-
function
|
|
1823
|
+
function normalizeEmail(email) {
|
|
1824
|
+
return email.trim().toLowerCase().normalize("NFC");
|
|
1825
|
+
}
|
|
1826
|
+
function seedFromEmail(email) {
|
|
1827
|
+
if (typeof email !== "string" || email.length === 0) {
|
|
1828
|
+
throw new Error("navii: seedFromEmail() requires a non-empty string");
|
|
1829
|
+
}
|
|
1830
|
+
return sha256Hex(normalizeEmail(email));
|
|
1831
|
+
}
|
|
1832
|
+
function seed(fields, options = {}) {
|
|
1833
|
+
const hashEmail = options.hashEmail ?? true;
|
|
1656
1834
|
if (fields.id !== null && fields.id !== void 0 && String(fields.id).length > 0) {
|
|
1657
1835
|
return String(fields.id);
|
|
1658
1836
|
}
|
|
1659
1837
|
if (fields.email && fields.email.length > 0) {
|
|
1660
|
-
return fields.email;
|
|
1838
|
+
return hashEmail ? seedFromEmail(fields.email) : fields.email;
|
|
1661
1839
|
}
|
|
1662
1840
|
if (fields.name && fields.name.length > 0) {
|
|
1663
1841
|
if (fields.createdAt !== null && fields.createdAt !== void 0) {
|
|
@@ -1672,7 +1850,7 @@ function seed(fields) {
|
|
|
1672
1850
|
|
|
1673
1851
|
// src/build.ts
|
|
1674
1852
|
function build(spec = {}, options = {}) {
|
|
1675
|
-
const palette = spec.palette ? PALETTE_BY_ID[spec.palette] ?? PALETTES[0] : PALETTES[0];
|
|
1853
|
+
const palette = options.palette ?? (spec.palette ? PALETTE_BY_ID[spec.palette] ?? PALETTES[0] : PALETTES[0]);
|
|
1676
1854
|
const resolved = {
|
|
1677
1855
|
seed: "__build__",
|
|
1678
1856
|
palette,
|
|
@@ -1716,9 +1894,10 @@ var Navii = {
|
|
|
1716
1894
|
select: selectAvatar,
|
|
1717
1895
|
group: renderGroup,
|
|
1718
1896
|
seed,
|
|
1897
|
+
seedFromEmail,
|
|
1719
1898
|
build
|
|
1720
1899
|
};
|
|
1721
1900
|
|
|
1722
|
-
export { BUILT_IN_PACKS, Navii, PACK_REGISTRY, build, createAvatar, createRng, cyrb53, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, selectAvatar };
|
|
1901
|
+
export { BUILT_IN_PACKS, Navii, PACK_REGISTRY, build, createAvatar, createRng, cyrb53, normalizeEmail, random, renderAvatar, renderAvatarInner, renderGroup, resolvePacks, seed, seedFromEmail, selectAvatar, sha256Hex };
|
|
1723
1902
|
//# sourceMappingURL=index.js.map
|
|
1724
1903
|
//# sourceMappingURL=index.js.map
|