@crashlab/random 0.1.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/README.md +26 -0
- package/dist/SeededRandom.d.ts +28 -0
- package/dist/SeededRandom.d.ts.map +1 -0
- package/dist/index.cjs +120 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/mulberry32.d.ts +6 -0
- package/dist/mulberry32.d.ts.map +1 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @crashlab/random
|
|
2
|
+
|
|
3
|
+
Deterministic seeded PRNG for Node.js, backed by the Mulberry32 algorithm.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { mulberry32, SeededRandom, patchMathRandom } from '@crashlab/random';
|
|
9
|
+
|
|
10
|
+
// Low-level
|
|
11
|
+
const rng = mulberry32(42);
|
|
12
|
+
rng(); // always the same first value for seed 42
|
|
13
|
+
|
|
14
|
+
// Class API
|
|
15
|
+
const sr = new SeededRandom(42);
|
|
16
|
+
sr.next(); // [0, 1)
|
|
17
|
+
sr.intBetween(1, 6); // dice roll
|
|
18
|
+
sr.pick(['a', 'b']); // random element
|
|
19
|
+
sr.shuffle([1, 2, 3]); // Fisher-Yates
|
|
20
|
+
|
|
21
|
+
// Scoped Math.random patching
|
|
22
|
+
const values = patchMathRandom(42, () => {
|
|
23
|
+
return [Math.random(), Math.random(), Math.random()];
|
|
24
|
+
});
|
|
25
|
+
// Same seed → same values every time
|
|
26
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic random number generator backed by mulberry32.
|
|
3
|
+
*
|
|
4
|
+
* Can patch `Math.random` within a scope or globally.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SeededRandom {
|
|
7
|
+
private readonly _next;
|
|
8
|
+
private _originalRandom;
|
|
9
|
+
private _originalRandomBytes;
|
|
10
|
+
constructor(seed: number);
|
|
11
|
+
/** Next float in [0, 1). */
|
|
12
|
+
next(): number;
|
|
13
|
+
/** Integer in [min, max] (inclusive). */
|
|
14
|
+
intBetween(min: number, max: number): number;
|
|
15
|
+
/** Pick a random element from `arr`. */
|
|
16
|
+
pick<T>(arr: readonly T[]): T;
|
|
17
|
+
/** Return a new array with Fisher-Yates shuffle. */
|
|
18
|
+
shuffle<T>(arr: readonly T[]): T[];
|
|
19
|
+
/** Replace `Math.random` and `crypto.randomBytes` with this PRNG. */
|
|
20
|
+
install(): void;
|
|
21
|
+
/** Restore the originals. */
|
|
22
|
+
uninstall(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Run `fn` with `Math.random` temporarily replaced by a seeded PRNG.
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchMathRandom<T>(seed: number, fn: () => T): T;
|
|
28
|
+
//# sourceMappingURL=SeededRandom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeededRandom.d.ts","sourceRoot":"","sources":["../src/SeededRandom.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,oBAAoB,CAAa;gBAE7B,IAAI,EAAE,MAAM;IAIxB,4BAA4B;IAC5B,IAAI,IAAI,MAAM;IAId,yCAAyC;IACzC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAI5C,wCAAwC;IACxC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC;IAI7B,oDAAoD;IACpD,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE;IASlC,qEAAqE;IACrE,OAAO,IAAI,IAAI;IAkBf,6BAA6B;IAC7B,SAAS,IAAI,IAAI;CAUlB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ/D"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SeededRandom: () => SeededRandom,
|
|
24
|
+
mulberry32: () => mulberry32,
|
|
25
|
+
patchMathRandom: () => patchMathRandom
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// ../../node_modules/tsup/assets/cjs_shims.js
|
|
30
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
31
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
32
|
+
|
|
33
|
+
// src/mulberry32.ts
|
|
34
|
+
function mulberry32(seed) {
|
|
35
|
+
let s = seed | 0;
|
|
36
|
+
return function next() {
|
|
37
|
+
s = s + 1831565813 | 0;
|
|
38
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
39
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
40
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/SeededRandom.ts
|
|
45
|
+
var import_node_module = require("module");
|
|
46
|
+
var _require = (0, import_node_module.createRequire)(importMetaUrl);
|
|
47
|
+
var cryptoCjs = _require("node:crypto");
|
|
48
|
+
var SeededRandom = class {
|
|
49
|
+
_next;
|
|
50
|
+
_originalRandom = null;
|
|
51
|
+
_originalRandomBytes = null;
|
|
52
|
+
constructor(seed) {
|
|
53
|
+
this._next = mulberry32(seed);
|
|
54
|
+
}
|
|
55
|
+
/** Next float in [0, 1). */
|
|
56
|
+
next() {
|
|
57
|
+
return this._next();
|
|
58
|
+
}
|
|
59
|
+
/** Integer in [min, max] (inclusive). */
|
|
60
|
+
intBetween(min, max) {
|
|
61
|
+
return min + Math.floor(this._next() * (max - min + 1));
|
|
62
|
+
}
|
|
63
|
+
/** Pick a random element from `arr`. */
|
|
64
|
+
pick(arr) {
|
|
65
|
+
return arr[Math.floor(this._next() * arr.length)];
|
|
66
|
+
}
|
|
67
|
+
/** Return a new array with Fisher-Yates shuffle. */
|
|
68
|
+
shuffle(arr) {
|
|
69
|
+
const out = [...arr];
|
|
70
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
71
|
+
const j = Math.floor(this._next() * (i + 1));
|
|
72
|
+
[out[i], out[j]] = [out[j], out[i]];
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/** Replace `Math.random` and `crypto.randomBytes` with this PRNG. */
|
|
77
|
+
install() {
|
|
78
|
+
this._originalRandom = Math.random;
|
|
79
|
+
Math.random = () => this._next();
|
|
80
|
+
this._originalRandomBytes = cryptoCjs.randomBytes;
|
|
81
|
+
cryptoCjs.randomBytes = (size, cb) => {
|
|
82
|
+
const buf = Buffer.alloc(size);
|
|
83
|
+
for (let i = 0; i < size; i++) {
|
|
84
|
+
buf[i] = Math.floor(this._next() * 256);
|
|
85
|
+
}
|
|
86
|
+
if (cb) {
|
|
87
|
+
process.nextTick(() => cb(null, buf));
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
return buf;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Restore the originals. */
|
|
94
|
+
uninstall() {
|
|
95
|
+
if (this._originalRandom) {
|
|
96
|
+
Math.random = this._originalRandom;
|
|
97
|
+
this._originalRandom = null;
|
|
98
|
+
}
|
|
99
|
+
if (this._originalRandomBytes) {
|
|
100
|
+
cryptoCjs.randomBytes = this._originalRandomBytes;
|
|
101
|
+
this._originalRandomBytes = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function patchMathRandom(seed, fn) {
|
|
106
|
+
const rng = new SeededRandom(seed);
|
|
107
|
+
rng.install();
|
|
108
|
+
try {
|
|
109
|
+
return fn();
|
|
110
|
+
} finally {
|
|
111
|
+
rng.uninstall();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
115
|
+
0 && (module.exports = {
|
|
116
|
+
SeededRandom,
|
|
117
|
+
mulberry32,
|
|
118
|
+
patchMathRandom
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../../node_modules/tsup/assets/cjs_shims.js","../src/mulberry32.ts","../src/SeededRandom.ts"],"sourcesContent":["export { mulberry32 } from './mulberry32.js';\nexport { SeededRandom, patchMathRandom } from './SeededRandom.js';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * Mulberry32 — a fast 32-bit seeded PRNG with good distribution.\n * Returns a function that yields numbers in [0, 1).\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n","import { mulberry32 } from './mulberry32.js';\nimport { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\nconst cryptoCjs = _require('node:crypto') as typeof import('node:crypto');\n\n/**\n * Deterministic random number generator backed by mulberry32.\n *\n * Can patch `Math.random` within a scope or globally.\n */\nexport class SeededRandom {\n private readonly _next: () => number;\n private _originalRandom: (() => number) | null = null;\n private _originalRandomBytes: any = null;\n\n constructor(seed: number) {\n this._next = mulberry32(seed);\n }\n\n /** Next float in [0, 1). */\n next(): number {\n return this._next();\n }\n\n /** Integer in [min, max] (inclusive). */\n intBetween(min: number, max: number): number {\n return min + Math.floor(this._next() * (max - min + 1));\n }\n\n /** Pick a random element from `arr`. */\n pick<T>(arr: readonly T[]): T {\n return arr[Math.floor(this._next() * arr.length)];\n }\n\n /** Return a new array with Fisher-Yates shuffle. */\n shuffle<T>(arr: readonly T[]): T[] {\n const out = [...arr];\n for (let i = out.length - 1; i > 0; i--) {\n const j = Math.floor(this._next() * (i + 1));\n [out[i], out[j]] = [out[j], out[i]];\n }\n return out;\n }\n\n /** Replace `Math.random` and `crypto.randomBytes` with this PRNG. */\n install(): void {\n this._originalRandom = Math.random;\n Math.random = () => this._next();\n\n this._originalRandomBytes = cryptoCjs.randomBytes;\n (cryptoCjs as any).randomBytes = (size: number, cb?: any) => {\n const buf = Buffer.alloc(size);\n for (let i = 0; i < size; i++) {\n buf[i] = Math.floor(this._next() * 256);\n }\n if (cb) {\n process.nextTick(() => cb(null, buf));\n return undefined as any;\n }\n return buf;\n };\n }\n\n /** Restore the originals. */\n uninstall(): void {\n if (this._originalRandom) {\n Math.random = this._originalRandom;\n this._originalRandom = null;\n }\n if (this._originalRandomBytes) {\n (cryptoCjs as any).randomBytes = this._originalRandomBytes;\n this._originalRandomBytes = null;\n }\n }\n}\n\n/**\n * Run `fn` with `Math.random` temporarily replaced by a seeded PRNG.\n */\nexport function patchMathRandom<T>(seed: number, fn: () => T): T {\n const rng = new SeededRandom(seed);\n rng.install();\n try {\n return fn();\n } finally {\n rng.uninstall();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,mBAAmB,MACvB,OAAO,aAAa,cAChB,IAAI,IAAI,QAAQ,UAAU,EAAE,EAAE,OAC7B,SAAS,iBAAiB,SAAS,cAAc,QAAQ,YAAY,MAAM,WAC1E,SAAS,cAAc,MACvB,IAAI,IAAI,WAAW,SAAS,OAAO,EAAE;AAEtC,IAAM,gBAAgC,iCAAiB;;;ACRvD,SAAS,WAAW,MAA4B;AACrD,MAAI,IAAI,OAAO;AACf,SAAO,SAAS,OAAe;AAC7B,QAAK,IAAI,aAAc;AACvB,QAAI,IAAI,KAAK,KAAK,IAAK,MAAM,IAAK,IAAI,CAAC;AACvC,QAAK,IAAI,KAAK,KAAK,IAAK,MAAM,GAAI,KAAK,CAAC,IAAK;AAC7C,aAAS,IAAK,MAAM,QAAS,KAAK;AAAA,EACpC;AACF;;;ACXA,yBAA8B;AAE9B,IAAM,eAAW,kCAAc,aAAe;AAC9C,IAAM,YAAY,SAAS,aAAa;AAOjC,IAAM,eAAN,MAAmB;AAAA,EACP;AAAA,EACT,kBAAyC;AAAA,EACzC,uBAA4B;AAAA,EAEpC,YAAY,MAAc;AACxB,SAAK,QAAQ,WAAW,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,WAAW,KAAa,KAAqB;AAC3C,WAAO,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,EAAE;AAAA,EACxD;AAAA;AAAA,EAGA,KAAQ,KAAsB;AAC5B,WAAO,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC;AAAA,EAClD;AAAA;AAAA,EAGA,QAAW,KAAwB;AACjC,UAAM,MAAM,CAAC,GAAG,GAAG;AACnB,aAAS,IAAI,IAAI,SAAS,GAAG,IAAI,GAAG,KAAK;AACvC,YAAM,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,EAAE;AAC3C,OAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,kBAAkB,KAAK;AAC5B,SAAK,SAAS,MAAM,KAAK,MAAM;AAE/B,SAAK,uBAAuB,UAAU;AACtC,IAAC,UAAkB,cAAc,CAAC,MAAc,OAAa;AAC3D,YAAM,MAAM,OAAO,MAAM,IAAI;AAC7B,eAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,YAAI,CAAC,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,GAAG;AAAA,MACxC;AACA,UAAI,IAAI;AACN,gBAAQ,SAAS,MAAM,GAAG,MAAM,GAAG,CAAC;AACpC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,YAAkB;AAChB,QAAI,KAAK,iBAAiB;AACxB,WAAK,SAAS,KAAK;AACnB,WAAK,kBAAkB;AAAA,IACzB;AACA,QAAI,KAAK,sBAAsB;AAC7B,MAAC,UAAkB,cAAc,KAAK;AACtC,WAAK,uBAAuB;AAAA,IAC9B;AAAA,EACF;AACF;AAKO,SAAS,gBAAmB,MAAc,IAAgB;AAC/D,QAAM,MAAM,IAAI,aAAa,IAAI;AACjC,MAAI,QAAQ;AACZ,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,UAAE;AACA,QAAI,UAAU;AAAA,EAChB;AACF;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/mulberry32.ts
|
|
2
|
+
function mulberry32(seed) {
|
|
3
|
+
let s = seed | 0;
|
|
4
|
+
return function next() {
|
|
5
|
+
s = s + 1831565813 | 0;
|
|
6
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
7
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
8
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/SeededRandom.ts
|
|
13
|
+
import { createRequire } from "module";
|
|
14
|
+
var _require = createRequire(import.meta.url);
|
|
15
|
+
var cryptoCjs = _require("node:crypto");
|
|
16
|
+
var SeededRandom = class {
|
|
17
|
+
_next;
|
|
18
|
+
_originalRandom = null;
|
|
19
|
+
_originalRandomBytes = null;
|
|
20
|
+
constructor(seed) {
|
|
21
|
+
this._next = mulberry32(seed);
|
|
22
|
+
}
|
|
23
|
+
/** Next float in [0, 1). */
|
|
24
|
+
next() {
|
|
25
|
+
return this._next();
|
|
26
|
+
}
|
|
27
|
+
/** Integer in [min, max] (inclusive). */
|
|
28
|
+
intBetween(min, max) {
|
|
29
|
+
return min + Math.floor(this._next() * (max - min + 1));
|
|
30
|
+
}
|
|
31
|
+
/** Pick a random element from `arr`. */
|
|
32
|
+
pick(arr) {
|
|
33
|
+
return arr[Math.floor(this._next() * arr.length)];
|
|
34
|
+
}
|
|
35
|
+
/** Return a new array with Fisher-Yates shuffle. */
|
|
36
|
+
shuffle(arr) {
|
|
37
|
+
const out = [...arr];
|
|
38
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
39
|
+
const j = Math.floor(this._next() * (i + 1));
|
|
40
|
+
[out[i], out[j]] = [out[j], out[i]];
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
/** Replace `Math.random` and `crypto.randomBytes` with this PRNG. */
|
|
45
|
+
install() {
|
|
46
|
+
this._originalRandom = Math.random;
|
|
47
|
+
Math.random = () => this._next();
|
|
48
|
+
this._originalRandomBytes = cryptoCjs.randomBytes;
|
|
49
|
+
cryptoCjs.randomBytes = (size, cb) => {
|
|
50
|
+
const buf = Buffer.alloc(size);
|
|
51
|
+
for (let i = 0; i < size; i++) {
|
|
52
|
+
buf[i] = Math.floor(this._next() * 256);
|
|
53
|
+
}
|
|
54
|
+
if (cb) {
|
|
55
|
+
process.nextTick(() => cb(null, buf));
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
return buf;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Restore the originals. */
|
|
62
|
+
uninstall() {
|
|
63
|
+
if (this._originalRandom) {
|
|
64
|
+
Math.random = this._originalRandom;
|
|
65
|
+
this._originalRandom = null;
|
|
66
|
+
}
|
|
67
|
+
if (this._originalRandomBytes) {
|
|
68
|
+
cryptoCjs.randomBytes = this._originalRandomBytes;
|
|
69
|
+
this._originalRandomBytes = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
function patchMathRandom(seed, fn) {
|
|
74
|
+
const rng = new SeededRandom(seed);
|
|
75
|
+
rng.install();
|
|
76
|
+
try {
|
|
77
|
+
return fn();
|
|
78
|
+
} finally {
|
|
79
|
+
rng.uninstall();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
SeededRandom,
|
|
84
|
+
mulberry32,
|
|
85
|
+
patchMathRandom
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mulberry32.ts","../src/SeededRandom.ts"],"sourcesContent":["/**\n * Mulberry32 — a fast 32-bit seeded PRNG with good distribution.\n * Returns a function that yields numbers in [0, 1).\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n","import { mulberry32 } from './mulberry32.js';\nimport { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\nconst cryptoCjs = _require('node:crypto') as typeof import('node:crypto');\n\n/**\n * Deterministic random number generator backed by mulberry32.\n *\n * Can patch `Math.random` within a scope or globally.\n */\nexport class SeededRandom {\n private readonly _next: () => number;\n private _originalRandom: (() => number) | null = null;\n private _originalRandomBytes: any = null;\n\n constructor(seed: number) {\n this._next = mulberry32(seed);\n }\n\n /** Next float in [0, 1). */\n next(): number {\n return this._next();\n }\n\n /** Integer in [min, max] (inclusive). */\n intBetween(min: number, max: number): number {\n return min + Math.floor(this._next() * (max - min + 1));\n }\n\n /** Pick a random element from `arr`. */\n pick<T>(arr: readonly T[]): T {\n return arr[Math.floor(this._next() * arr.length)];\n }\n\n /** Return a new array with Fisher-Yates shuffle. */\n shuffle<T>(arr: readonly T[]): T[] {\n const out = [...arr];\n for (let i = out.length - 1; i > 0; i--) {\n const j = Math.floor(this._next() * (i + 1));\n [out[i], out[j]] = [out[j], out[i]];\n }\n return out;\n }\n\n /** Replace `Math.random` and `crypto.randomBytes` with this PRNG. */\n install(): void {\n this._originalRandom = Math.random;\n Math.random = () => this._next();\n\n this._originalRandomBytes = cryptoCjs.randomBytes;\n (cryptoCjs as any).randomBytes = (size: number, cb?: any) => {\n const buf = Buffer.alloc(size);\n for (let i = 0; i < size; i++) {\n buf[i] = Math.floor(this._next() * 256);\n }\n if (cb) {\n process.nextTick(() => cb(null, buf));\n return undefined as any;\n }\n return buf;\n };\n }\n\n /** Restore the originals. */\n uninstall(): void {\n if (this._originalRandom) {\n Math.random = this._originalRandom;\n this._originalRandom = null;\n }\n if (this._originalRandomBytes) {\n (cryptoCjs as any).randomBytes = this._originalRandomBytes;\n this._originalRandomBytes = null;\n }\n }\n}\n\n/**\n * Run `fn` with `Math.random` temporarily replaced by a seeded PRNG.\n */\nexport function patchMathRandom<T>(seed: number, fn: () => T): T {\n const rng = new SeededRandom(seed);\n rng.install();\n try {\n return fn();\n } finally {\n rng.uninstall();\n }\n}\n"],"mappings":";AAIO,SAAS,WAAW,MAA4B;AACrD,MAAI,IAAI,OAAO;AACf,SAAO,SAAS,OAAe;AAC7B,QAAK,IAAI,aAAc;AACvB,QAAI,IAAI,KAAK,KAAK,IAAK,MAAM,IAAK,IAAI,CAAC;AACvC,QAAK,IAAI,KAAK,KAAK,IAAK,MAAM,GAAI,KAAK,CAAC,IAAK;AAC7C,aAAS,IAAK,MAAM,QAAS,KAAK;AAAA,EACpC;AACF;;;ACXA,SAAS,qBAAqB;AAE9B,IAAM,WAAW,cAAc,YAAY,GAAG;AAC9C,IAAM,YAAY,SAAS,aAAa;AAOjC,IAAM,eAAN,MAAmB;AAAA,EACP;AAAA,EACT,kBAAyC;AAAA,EACzC,uBAA4B;AAAA,EAEpC,YAAY,MAAc;AACxB,SAAK,QAAQ,WAAW,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,WAAW,KAAa,KAAqB;AAC3C,WAAO,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,EAAE;AAAA,EACxD;AAAA;AAAA,EAGA,KAAQ,KAAsB;AAC5B,WAAO,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC;AAAA,EAClD;AAAA;AAAA,EAGA,QAAW,KAAwB;AACjC,UAAM,MAAM,CAAC,GAAG,GAAG;AACnB,aAAS,IAAI,IAAI,SAAS,GAAG,IAAI,GAAG,KAAK;AACvC,YAAM,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,EAAE;AAC3C,OAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,kBAAkB,KAAK;AAC5B,SAAK,SAAS,MAAM,KAAK,MAAM;AAE/B,SAAK,uBAAuB,UAAU;AACtC,IAAC,UAAkB,cAAc,CAAC,MAAc,OAAa;AAC3D,YAAM,MAAM,OAAO,MAAM,IAAI;AAC7B,eAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,YAAI,CAAC,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,GAAG;AAAA,MACxC;AACA,UAAI,IAAI;AACN,gBAAQ,SAAS,MAAM,GAAG,MAAM,GAAG,CAAC;AACpC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,YAAkB;AAChB,QAAI,KAAK,iBAAiB;AACxB,WAAK,SAAS,KAAK;AACnB,WAAK,kBAAkB;AAAA,IACzB;AACA,QAAI,KAAK,sBAAsB;AAC7B,MAAC,UAAkB,cAAc,KAAK;AACtC,WAAK,uBAAuB;AAAA,IAC9B;AAAA,EACF;AACF;AAKO,SAAS,gBAAmB,MAAc,IAAgB;AAC/D,QAAM,MAAM,IAAI,aAAa,IAAI;AACjC,MAAI,QAAQ;AACZ,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,UAAE;AACA,QAAI,UAAU;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mulberry32.d.ts","sourceRoot":"","sources":["../src/mulberry32.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAQrD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crashlab/random",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup && tsc --build tsconfig.json --force",
|
|
21
|
+
"clean": "rimraf dist",
|
|
22
|
+
"prepack": "npm run build",
|
|
23
|
+
"build:pkg": "tsup && tsc --build tsconfig.json --force"
|
|
24
|
+
}
|
|
25
|
+
}
|