@dxos/keys 0.8.4-main.67995b8 → 0.8.4-main.a4bbb77
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/dist/lib/browser/index.mjs +377 -191
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +377 -191
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/dxn.d.ts +29 -12
- package/dist/types/src/dxn.d.ts.map +1 -1
- package/dist/types/src/object-id.d.ts +49 -0
- package/dist/types/src/object-id.d.ts.map +1 -1
- package/dist/types/src/prng.d.ts +32 -0
- package/dist/types/src/prng.d.ts.map +1 -0
- package/dist/types/src/public-key.d.ts +2 -2
- package/dist/types/src/public-key.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -10
- package/src/dxn.ts +72 -52
- package/src/object-id.ts +93 -4
- package/src/prng.ts +54 -0
- package/src/public-key.ts +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/keys",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.a4bbb77",
|
|
4
4
|
"description": "Key utils and definitions.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -10,12 +10,10 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": "./src/index.ts",
|
|
14
|
+
"types": "./dist/types/src/index.d.ts",
|
|
13
15
|
"browser": "./dist/lib/browser/index.mjs",
|
|
14
|
-
"node":
|
|
15
|
-
"require": "./dist/lib/node/index.cjs",
|
|
16
|
-
"default": "./dist/lib/node-esm/index.mjs"
|
|
17
|
-
},
|
|
18
|
-
"types": "./dist/types/src/index.d.ts"
|
|
16
|
+
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
17
|
}
|
|
20
18
|
},
|
|
21
19
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -27,11 +25,11 @@
|
|
|
27
25
|
"src"
|
|
28
26
|
],
|
|
29
27
|
"dependencies": {
|
|
30
|
-
"effect": "3.
|
|
28
|
+
"effect": "3.18.3",
|
|
31
29
|
"ulidx": "^2.3.0",
|
|
32
|
-
"@dxos/debug": "0.8.4-main.
|
|
33
|
-
"@dxos/invariant": "0.8.4-main.
|
|
34
|
-
"@dxos/node-std": "0.8.4-main.
|
|
30
|
+
"@dxos/debug": "0.8.4-main.a4bbb77",
|
|
31
|
+
"@dxos/invariant": "0.8.4-main.a4bbb77",
|
|
32
|
+
"@dxos/node-std": "0.8.4-main.a4bbb77"
|
|
35
33
|
},
|
|
36
34
|
"devDependencies": {
|
|
37
35
|
"base32-decode": "^1.0.0",
|
package/src/dxn.ts
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import type { InspectOptionsStylized, inspect } from 'node:util';
|
|
6
|
+
|
|
5
7
|
import { Schema } from 'effect';
|
|
6
|
-
import type { inspect, InspectOptionsStylized } from 'node:util';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
+
import { type DevtoolsFormatter, devtoolsFormatter, inspectCustom } from '@dxos/debug';
|
|
9
10
|
import { assertArgument, invariant } from '@dxos/invariant';
|
|
10
11
|
|
|
11
12
|
import { ObjectId } from './object-id';
|
|
@@ -18,6 +19,8 @@ import { SpaceId } from './space-id';
|
|
|
18
19
|
// TODO(dmaretskyi): "@" is a separator character in the URI spec.
|
|
19
20
|
export const LOCAL_SPACE_TAG = '@';
|
|
20
21
|
|
|
22
|
+
export const DXN_ECHO_REGEXP = /@(dxn:[a-zA-Z0-p:@]+)/;
|
|
23
|
+
|
|
21
24
|
// TODO(burdon): Namespace for.
|
|
22
25
|
export const QueueSubspaceTags = Object.freeze({
|
|
23
26
|
DATA: 'data',
|
|
@@ -26,6 +29,12 @@ export const QueueSubspaceTags = Object.freeze({
|
|
|
26
29
|
|
|
27
30
|
export type QueueSubspaceTag = (typeof QueueSubspaceTags)[keyof typeof QueueSubspaceTags];
|
|
28
31
|
|
|
32
|
+
// TODO(burdon): Refactor.
|
|
33
|
+
// Consider: https://github.com/multiformats/multiaddr
|
|
34
|
+
// dxn:echo:[<space-id>:[<queue-id>:]]<object-id>
|
|
35
|
+
// dxn:echo:[S/<space-id>:[Q/<queue-id>:]]<object-id>
|
|
36
|
+
// dxn:type:dxos.org/markdown/Contact
|
|
37
|
+
|
|
29
38
|
/**
|
|
30
39
|
* DXN unambiguously names a resource like an ECHO object, schema definition, plugin, etc.
|
|
31
40
|
* Each DXN starts with a dxn prefix, followed by a resource kind.
|
|
@@ -43,6 +52,7 @@ export type QueueSubspaceTag = (typeof QueueSubspaceTags)[keyof typeof QueueSubs
|
|
|
43
52
|
* ```
|
|
44
53
|
*/
|
|
45
54
|
export class DXN {
|
|
55
|
+
// TODO(burdon): Rename to DXN (i.e., DXN.DXN).
|
|
46
56
|
// TODO(dmaretskyi): Should this be a transformation into the DXN type?
|
|
47
57
|
static Schema = Schema.NonEmptyString.pipe(
|
|
48
58
|
Schema.pattern(/^dxn:([^:]+):(?:[^:]+:?)+[^:]$/),
|
|
@@ -64,16 +74,16 @@ export class DXN {
|
|
|
64
74
|
*/
|
|
65
75
|
static kind = Object.freeze({
|
|
66
76
|
/**
|
|
67
|
-
* dxn:type:<
|
|
77
|
+
* dxn:type:<type_name>[:<version>]
|
|
68
78
|
*/
|
|
69
79
|
TYPE: 'type',
|
|
70
80
|
|
|
71
81
|
/**
|
|
72
|
-
* dxn:echo:<
|
|
73
|
-
* dxn:echo:@:<
|
|
82
|
+
* dxn:echo:<space_id>:<echo_id>
|
|
83
|
+
* dxn:echo:@:<echo_id>
|
|
74
84
|
*/
|
|
75
|
-
// TODO(burdon): Rename to OBJECT? (BREAKING CHANGE).
|
|
76
|
-
// TODO(burdon): Add separate Kind for space
|
|
85
|
+
// TODO(burdon): Rename to OBJECT? (BREAKING CHANGE to update "echo").
|
|
86
|
+
// TODO(burdon): Add separate Kind for space?
|
|
77
87
|
ECHO: 'echo',
|
|
78
88
|
|
|
79
89
|
/**
|
|
@@ -85,14 +95,19 @@ export class DXN {
|
|
|
85
95
|
QUEUE: 'queue',
|
|
86
96
|
});
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Exactly equals.
|
|
100
|
+
*/
|
|
92
101
|
static equals(a: DXN, b: DXN): boolean {
|
|
93
102
|
return a.kind === b.kind && a.parts.length === b.parts.length && a.parts.every((part, i) => part === b.parts[i]);
|
|
94
103
|
}
|
|
95
104
|
|
|
105
|
+
static equalsEchoId(a: DXN, b: DXN): boolean {
|
|
106
|
+
const a1 = a.asEchoDXN();
|
|
107
|
+
const b1 = b.asEchoDXN();
|
|
108
|
+
return !!a1 && !!b1 && a1.echoId === b1.echoId;
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
// TODO(burdon): Rename isValid.
|
|
97
112
|
static isDXNString(dxn: string): boolean {
|
|
98
113
|
return dxn.startsWith('dxn:');
|
|
@@ -119,7 +134,7 @@ export class DXN {
|
|
|
119
134
|
static tryParse(dxn: string): DXN | undefined {
|
|
120
135
|
try {
|
|
121
136
|
return DXN.parse(dxn);
|
|
122
|
-
} catch
|
|
137
|
+
} catch {
|
|
123
138
|
return undefined;
|
|
124
139
|
}
|
|
125
140
|
}
|
|
@@ -139,18 +154,27 @@ export class DXN {
|
|
|
139
154
|
return new DXN(DXN.kind.TYPE, [typename, version]);
|
|
140
155
|
}
|
|
141
156
|
|
|
157
|
+
/**
|
|
158
|
+
* @example `dxn:echo:BA25QRC2FEWCSAMRP4RZL65LWJ7352CKE:01J00J9B45YHYSGZQTQMSKMGJ6`
|
|
159
|
+
*/
|
|
160
|
+
static fromSpaceAndObjectId(spaceId: SpaceId, objectId: ObjectId): DXN {
|
|
161
|
+
assertArgument(SpaceId.isValid(spaceId), `Invalid space ID: ${spaceId}`);
|
|
162
|
+
assertArgument(ObjectId.isValid(objectId), 'objectId', `Invalid object ID: ${objectId}`);
|
|
163
|
+
return new DXN(DXN.kind.ECHO, [spaceId, objectId]);
|
|
164
|
+
}
|
|
165
|
+
|
|
142
166
|
/**
|
|
143
167
|
* @example `dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6`
|
|
144
168
|
*/
|
|
145
169
|
static fromLocalObjectId(id: string): DXN {
|
|
146
|
-
assertArgument(ObjectId.isValid(id), `Invalid object ID: ${id}`);
|
|
170
|
+
assertArgument(ObjectId.isValid(id), 'id', `Invalid object ID: ${id}`);
|
|
147
171
|
return new DXN(DXN.kind.ECHO, [LOCAL_SPACE_TAG, id]);
|
|
148
172
|
}
|
|
149
173
|
|
|
150
174
|
static fromQueue(subspaceTag: QueueSubspaceTag, spaceId: SpaceId, queueId: ObjectId, objectId?: ObjectId) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
175
|
+
assertArgument(SpaceId.isValid(spaceId), `Invalid space ID: ${spaceId}`);
|
|
176
|
+
assertArgument(ObjectId.isValid(queueId), 'queueId', `Invalid queue ID: ${queueId}`);
|
|
177
|
+
assertArgument(!objectId || ObjectId.isValid(objectId), 'objectId', `Invalid object ID: ${objectId}`);
|
|
154
178
|
|
|
155
179
|
return new DXN(DXN.kind.QUEUE, [subspaceTag, spaceId, queueId, ...(objectId ? [objectId] : [])]);
|
|
156
180
|
}
|
|
@@ -159,19 +183,23 @@ export class DXN {
|
|
|
159
183
|
#parts: string[];
|
|
160
184
|
|
|
161
185
|
constructor(kind: string, parts: string[]) {
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
assertArgument(parts.length > 0, 'parts', `Invalid DXN: ${parts}`);
|
|
187
|
+
assertArgument(
|
|
188
|
+
parts.every((part) => typeof part === 'string' && part.length > 0 && part.indexOf(':') === -1),
|
|
189
|
+
'parts',
|
|
190
|
+
`Invalid DXN: ${parts}`,
|
|
191
|
+
);
|
|
164
192
|
|
|
165
193
|
// Per-type validation.
|
|
166
194
|
switch (kind) {
|
|
167
195
|
case DXN.kind.TYPE:
|
|
168
196
|
if (parts.length > 2) {
|
|
169
|
-
throw new Error('Invalid
|
|
197
|
+
throw new Error('Invalid DXN.kind.TYPE');
|
|
170
198
|
}
|
|
171
199
|
break;
|
|
172
200
|
case DXN.kind.ECHO:
|
|
173
201
|
if (parts.length !== 2) {
|
|
174
|
-
throw new Error('Invalid
|
|
202
|
+
throw new Error('Invalid DXN.kind.ECHO');
|
|
175
203
|
}
|
|
176
204
|
break;
|
|
177
205
|
}
|
|
@@ -209,6 +237,10 @@ export class DXN {
|
|
|
209
237
|
};
|
|
210
238
|
}
|
|
211
239
|
|
|
240
|
+
get kind() {
|
|
241
|
+
return this.#kind;
|
|
242
|
+
}
|
|
243
|
+
|
|
212
244
|
get parts() {
|
|
213
245
|
return this.#parts;
|
|
214
246
|
}
|
|
@@ -219,6 +251,10 @@ export class DXN {
|
|
|
219
251
|
return this.#parts[0];
|
|
220
252
|
}
|
|
221
253
|
|
|
254
|
+
equals(other: DXN): boolean {
|
|
255
|
+
return DXN.equals(this, other);
|
|
256
|
+
}
|
|
257
|
+
|
|
222
258
|
hasTypenameOf(typename: string): boolean {
|
|
223
259
|
return this.#kind === DXN.kind.TYPE && this.#parts.length === 1 && this.#parts[0] === typename;
|
|
224
260
|
}
|
|
@@ -248,6 +284,7 @@ export class DXN {
|
|
|
248
284
|
const [spaceId, echoId] = this.#parts;
|
|
249
285
|
return {
|
|
250
286
|
spaceId: spaceId === LOCAL_SPACE_TAG ? undefined : (spaceId as SpaceId | undefined),
|
|
287
|
+
// TODO(burdon): objectId.
|
|
251
288
|
echoId,
|
|
252
289
|
};
|
|
253
290
|
}
|
|
@@ -269,35 +306,27 @@ export class DXN {
|
|
|
269
306
|
objectId: objectId as string | undefined,
|
|
270
307
|
};
|
|
271
308
|
}
|
|
272
|
-
}
|
|
273
309
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
...
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
interface EchoDXN extends DXN {
|
|
285
|
-
objectId: ObjectId;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
declare const dxn: DXN;
|
|
289
|
-
|
|
290
|
-
dxn.objectId
|
|
291
|
-
|
|
292
|
-
if(dxn.isEchoDXN()) {
|
|
293
|
-
dxn.objectId
|
|
310
|
+
/**
|
|
311
|
+
* Produces a new DXN with the given parts appended.
|
|
312
|
+
*/
|
|
313
|
+
extend(parts: string[]): DXN {
|
|
314
|
+
return new DXN(this.#kind, [...this.#parts, ...parts]);
|
|
315
|
+
}
|
|
294
316
|
}
|
|
295
|
-
```
|
|
296
317
|
|
|
297
318
|
/**
|
|
298
319
|
* API namespace.
|
|
299
320
|
*/
|
|
300
321
|
export declare namespace DXN {
|
|
322
|
+
/**
|
|
323
|
+
* DXN represented as a javascript string.
|
|
324
|
+
*/
|
|
325
|
+
// TODO(burdon): Use Effect branded string?
|
|
326
|
+
// export const String = S.String.pipe(S.brand('DXN'));
|
|
327
|
+
// export type String = S.To(typoeof String);
|
|
328
|
+
export type String = string & { __DXNString: never };
|
|
329
|
+
|
|
301
330
|
export type TypeDXN = {
|
|
302
331
|
type: string;
|
|
303
332
|
version?: string;
|
|
@@ -305,8 +334,7 @@ export declare namespace DXN {
|
|
|
305
334
|
|
|
306
335
|
export type EchoDXN = {
|
|
307
336
|
spaceId?: SpaceId;
|
|
308
|
-
// TODO(
|
|
309
|
-
echoId: string; // TODO(dmaretskyi): ObjectId.
|
|
337
|
+
echoId: string; // TODO(dmaretskyi): Rename to `objectId` and use `ObjectId` for the type.
|
|
310
338
|
};
|
|
311
339
|
|
|
312
340
|
export type QueueDXN = {
|
|
@@ -315,12 +343,4 @@ export declare namespace DXN {
|
|
|
315
343
|
queueId: string; // TODO(dmaretskyi): ObjectId.
|
|
316
344
|
objectId?: string; // TODO(dmaretskyi): ObjectId.
|
|
317
345
|
};
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* DXN represented as a javascript string.
|
|
321
|
-
*/
|
|
322
|
-
export type String = string & { __DXNString: never };
|
|
323
|
-
// TODO(burdon): Make brand.
|
|
324
|
-
// export const String = S.String.pipe(S.brand('DXN'));
|
|
325
|
-
// export type String = S.To(typoeof String);
|
|
326
346
|
}
|
package/src/object-id.ts
CHANGED
|
@@ -3,22 +3,52 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
|
-
import {
|
|
6
|
+
import { type PRNG, type ULIDFactory, monotonicFactory } from 'ulidx';
|
|
7
7
|
|
|
8
8
|
// TODO(dmaretskyi): Make brand.
|
|
9
9
|
// export const ObjectIdBrand: unique symbol = Symbol('@dxos/echo/ObjectId');
|
|
10
10
|
// export const ObjectIdSchema = Schema.ULID.pipe(S.brand(ObjectIdBrand));
|
|
11
11
|
const ObjectIdSchema = Schema.String.pipe(Schema.pattern(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i)).annotations({
|
|
12
|
-
description: '
|
|
12
|
+
description: 'A Universally Unique Lexicographically Sortable Identifier',
|
|
13
13
|
pattern: '^[0-7][0-9A-HJKMNP-TV-Z]{25}$',
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
export type ObjectId = typeof ObjectIdSchema.Type;
|
|
17
17
|
|
|
18
18
|
export interface ObjectIdClass extends Schema.SchemaClass<ObjectId, string> {
|
|
19
|
+
/**
|
|
20
|
+
* @returns true if the string is a valid ObjectId.
|
|
21
|
+
*/
|
|
19
22
|
isValid(id: string): id is ObjectId;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates an ObjectId from a string validating the format.
|
|
26
|
+
*/
|
|
20
27
|
make(id: string): ObjectId;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generates a random ObjectId.
|
|
31
|
+
*/
|
|
21
32
|
random(): ObjectId;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* WARNING: To be used only within tests.
|
|
36
|
+
*
|
|
37
|
+
* Disables randomness in ObjectId generation, causing the same sequence of IDs to be generated.
|
|
38
|
+
* Do not use in production code as this will cause data collisions.
|
|
39
|
+
* Place this at the top of the test file to ensure that the same sequence of IDs is generated.
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* ObjectId.dangerouslyDisableRandomness();
|
|
43
|
+
*
|
|
44
|
+
* describe('suite', () => {
|
|
45
|
+
* // ...
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* NOTE: The generated IDs depend on the order of ObjectId.random() calls, which might be affected by test order, scheduling, etc.
|
|
50
|
+
*/
|
|
51
|
+
dangerouslyDisableRandomness(): void;
|
|
22
52
|
}
|
|
23
53
|
|
|
24
54
|
/**
|
|
@@ -27,16 +57,75 @@ export interface ObjectIdClass extends Schema.SchemaClass<ObjectId, string> {
|
|
|
27
57
|
* Follows ULID spec.
|
|
28
58
|
*/
|
|
29
59
|
export const ObjectId: ObjectIdClass = class extends ObjectIdSchema {
|
|
60
|
+
static #factory: ULIDFactory = monotonicFactory();
|
|
61
|
+
static #seedTime: number | undefined = undefined;
|
|
62
|
+
|
|
30
63
|
static isValid(id: string): id is ObjectId {
|
|
31
64
|
try {
|
|
32
65
|
Schema.decodeSync(ObjectId)(id);
|
|
33
66
|
return true;
|
|
34
|
-
} catch
|
|
67
|
+
} catch {
|
|
35
68
|
return false;
|
|
36
69
|
}
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
static random(): ObjectId {
|
|
40
|
-
return
|
|
73
|
+
return this.#factory(this.#seedTime) as ObjectId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static dangerouslyDisableRandomness() {
|
|
77
|
+
this.#factory = monotonicFactory(makeTestPRNG());
|
|
78
|
+
this.#seedTime = new Date('2025-01-01').getTime();
|
|
41
79
|
}
|
|
42
80
|
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Test PRNG that always starts with the same seed and produces the same sequence.
|
|
84
|
+
*/
|
|
85
|
+
const makeTestPRNG = (): PRNG => {
|
|
86
|
+
const rng = new SimplePRNG();
|
|
87
|
+
return () => {
|
|
88
|
+
return rng.next();
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Simple Linear Congruential Generator (LCG) for pseudo-random number generation.
|
|
94
|
+
* Returns numbers in the range [0, 1) (0 inclusive, 1 exclusive).
|
|
95
|
+
*/
|
|
96
|
+
export class SimplePRNG {
|
|
97
|
+
#seed: number;
|
|
98
|
+
|
|
99
|
+
// LCG parameters (from Numerical Recipes)
|
|
100
|
+
static readonly #a = 1664525;
|
|
101
|
+
static readonly #c = 1013904223;
|
|
102
|
+
static readonly #m = Math.pow(2, 32);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates a new PRNG instance.
|
|
106
|
+
* @param seed - Initial seed value. If not provided, uses 0.
|
|
107
|
+
*/
|
|
108
|
+
constructor(seed: number = 0) {
|
|
109
|
+
this.#seed = seed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generates the next pseudo-random number in the range [0, 1).
|
|
114
|
+
* @returns A pseudo-random number between 0 (inclusive) and 1 (exclusive).
|
|
115
|
+
*/
|
|
116
|
+
next(): number {
|
|
117
|
+
// Update seed using LCG formula: (a * seed + c) mod m
|
|
118
|
+
this.#seed = (SimplePRNG.#a * this.#seed + SimplePRNG.#c) % SimplePRNG.#m;
|
|
119
|
+
|
|
120
|
+
// Normalize to [0, 1) range
|
|
121
|
+
return this.#seed / SimplePRNG.#m;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resets the generator with a new seed.
|
|
126
|
+
* @param seed - New seed value.
|
|
127
|
+
*/
|
|
128
|
+
reset(seed: number): void {
|
|
129
|
+
this.#seed = seed;
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/prng.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Simple Linear Congruential Generator (LCG) for pseudo-random number generation.
|
|
7
|
+
* Returns numbers in the range [0, 1) (0 inclusive, 1 exclusive).
|
|
8
|
+
*/
|
|
9
|
+
export class SimplePRNG {
|
|
10
|
+
private _seed: number;
|
|
11
|
+
|
|
12
|
+
// LCG parameters (from Numerical Recipes)
|
|
13
|
+
private static readonly _a = 1664525;
|
|
14
|
+
private static readonly _c = 1013904223;
|
|
15
|
+
private static readonly _m = Math.pow(2, 32);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new PRNG instance.
|
|
19
|
+
* @param seed - Initial seed value. If not provided, uses current timestamp.
|
|
20
|
+
*/
|
|
21
|
+
constructor(seed?: number) {
|
|
22
|
+
this._seed = seed ?? Date.now();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generates the next pseudo-random number in the range [0, 1).
|
|
27
|
+
* @returns A pseudo-random number between 0 (inclusive) and 1 (exclusive).
|
|
28
|
+
*/
|
|
29
|
+
next(): number {
|
|
30
|
+
// Update seed using LCG formula: (a * seed + c) mod m
|
|
31
|
+
this._seed = (SimplePRNG._a * this._seed + SimplePRNG._c) % SimplePRNG._m;
|
|
32
|
+
|
|
33
|
+
// Normalize to [0, 1) range
|
|
34
|
+
return this._seed / SimplePRNG._m;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resets the generator with a new seed.
|
|
39
|
+
* @param seed - New seed value.
|
|
40
|
+
*/
|
|
41
|
+
reset(seed: number): void {
|
|
42
|
+
this._seed = seed;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a simple PRNG function with optional seed.
|
|
48
|
+
* @param seed - Optional seed value.
|
|
49
|
+
* @returns A function that returns pseudo-random numbers in [0, 1).
|
|
50
|
+
*/
|
|
51
|
+
export const createPRNG = (seed?: number): (() => number) => {
|
|
52
|
+
const prng = new SimplePRNG(seed);
|
|
53
|
+
return () => prng.next();
|
|
54
|
+
};
|
package/src/public-key.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type InspectOptionsStylized, type inspect } from 'node:util';
|
|
6
|
+
|
|
5
7
|
import base32Decode from 'base32-decode';
|
|
6
8
|
import base32Encode from 'base32-encode';
|
|
7
|
-
import { type inspect, type InspectOptionsStylized } from 'node:util';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
|
-
devtoolsFormatter,
|
|
11
11
|
type DevtoolsFormatter,
|
|
12
|
-
equalsSymbol,
|
|
13
12
|
type Equatable,
|
|
13
|
+
devtoolsFormatter,
|
|
14
|
+
equalsSymbol,
|
|
14
15
|
inspectCustom,
|
|
15
16
|
truncateKey,
|
|
16
17
|
} from '@dxos/debug';
|