@naturalcycles/js-lib 14.256.0 → 14.258.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/cfg/frontend/tsconfig.json +67 -0
- package/dist/browser/adminService.d.ts +69 -0
- package/dist/browser/adminService.js +98 -0
- package/dist/browser/analytics.util.d.ts +12 -0
- package/dist/browser/analytics.util.js +59 -0
- package/dist/browser/i18n/fetchTranslationLoader.d.ts +13 -0
- package/dist/browser/i18n/fetchTranslationLoader.js +17 -0
- package/dist/browser/i18n/translation.service.d.ts +53 -0
- package/dist/browser/i18n/translation.service.js +61 -0
- package/dist/browser/imageFitter.d.ts +60 -0
- package/dist/browser/imageFitter.js +69 -0
- package/dist/browser/script.util.d.ts +14 -0
- package/dist/browser/script.util.js +50 -0
- package/dist/browser/topbar.d.ts +23 -0
- package/dist/browser/topbar.js +137 -0
- package/dist/decorators/memo.util.d.ts +2 -1
- package/dist/decorators/memo.util.js +8 -6
- package/dist/decorators/swarmSafe.decorator.d.ts +9 -0
- package/dist/decorators/swarmSafe.decorator.js +42 -0
- package/dist/deviceIdService.d.ts +65 -0
- package/dist/deviceIdService.js +109 -0
- package/dist/error/assert.d.ts +2 -1
- package/dist/error/assert.js +15 -13
- package/dist/error/error.util.js +9 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/nanoid.d.ts +7 -0
- package/dist/nanoid.js +61 -0
- package/dist/number/createDeterministicRandom.d.ts +6 -1
- package/dist/number/createDeterministicRandom.js +1 -2
- package/dist/string/hash.util.d.ts +1 -1
- package/dist/string/hash.util.js +1 -1
- package/dist/web.d.ts +6 -0
- package/dist/web.js +6 -0
- package/dist/zod/zod.util.d.ts +1 -1
- package/dist-esm/browser/adminService.js +94 -0
- package/dist-esm/browser/analytics.util.js +54 -0
- package/dist-esm/browser/i18n/fetchTranslationLoader.js +13 -0
- package/dist-esm/browser/i18n/translation.service.js +56 -0
- package/dist-esm/browser/imageFitter.js +65 -0
- package/dist-esm/browser/script.util.js +46 -0
- package/dist-esm/browser/topbar.js +134 -0
- package/dist-esm/decorators/memo.util.js +3 -1
- package/dist-esm/decorators/swarmSafe.decorator.js +38 -0
- package/dist-esm/deviceIdService.js +105 -0
- package/dist-esm/error/assert.js +3 -1
- package/dist-esm/error/error.util.js +4 -1
- package/dist-esm/index.js +9 -0
- package/dist-esm/nanoid.js +57 -0
- package/dist-esm/number/createDeterministicRandom.js +1 -2
- package/dist-esm/string/hash.util.js +1 -1
- package/dist-esm/web.js +6 -0
- package/package.json +2 -1
- package/src/browser/adminService.ts +157 -0
- package/src/browser/analytics.util.ts +68 -0
- package/src/browser/i18n/fetchTranslationLoader.ts +16 -0
- package/src/browser/i18n/translation.service.ts +102 -0
- package/src/browser/imageFitter.ts +128 -0
- package/src/browser/script.util.ts +52 -0
- package/src/browser/topbar.ts +147 -0
- package/src/datetime/localDate.ts +16 -0
- package/src/datetime/localTime.ts +39 -0
- package/src/decorators/debounce.ts +1 -0
- package/src/decorators/memo.util.ts +4 -1
- package/src/decorators/swarmSafe.decorator.ts +47 -0
- package/src/deviceIdService.ts +137 -0
- package/src/error/assert.ts +5 -11
- package/src/error/error.util.ts +4 -1
- package/src/index.ts +9 -0
- package/src/json-schema/jsonSchemaBuilder.ts +20 -0
- package/src/nanoid.ts +79 -0
- package/src/number/createDeterministicRandom.ts +7 -2
- package/src/semver.ts +2 -0
- package/src/string/hash.util.ts +1 -1
- package/src/web.ts +6 -0
- package/src/zod/zod.util.ts +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/// <reference lib="dom" preserve="true" />
|
|
2
|
+
|
|
3
|
+
import { isServerSide } from './env'
|
|
4
|
+
import { nanoidBrowser } from './nanoid'
|
|
5
|
+
import { hashCode } from './string/hash.util'
|
|
6
|
+
|
|
7
|
+
// This is in sync with the default length in Nanoid.
|
|
8
|
+
const deviceIdLength = 21
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Service to generate, maintain, persist a stable "device id".
|
|
12
|
+
*
|
|
13
|
+
* It's called "device id" and not userId/visitorId, to indicate that it only identifies a device,
|
|
14
|
+
* and has nothing to do with user identification.
|
|
15
|
+
* User might be logged in or not.
|
|
16
|
+
* User id can be the same on multiple devices.
|
|
17
|
+
* DeviceId is unique per device, same User or not.
|
|
18
|
+
*
|
|
19
|
+
* Service provides methods to deterministically select fraction of devices.
|
|
20
|
+
* For example, select 10% of devices that visit the website to be tracked by Analytics
|
|
21
|
+
* (to reduce Analytics quota usage).
|
|
22
|
+
* DeviceId persistence will ensure that recurring visits from the same device will yield the same
|
|
23
|
+
* DeviceId, and same "selection assignment" (like an assignment in an AB test).
|
|
24
|
+
*
|
|
25
|
+
* @experimental
|
|
26
|
+
*/
|
|
27
|
+
export class DeviceIdService {
|
|
28
|
+
constructor(cfg: DeviceIdServiceCfg = {}) {
|
|
29
|
+
this.cfg = {
|
|
30
|
+
localStorageKey: 'deviceId',
|
|
31
|
+
debug: false,
|
|
32
|
+
...cfg,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.init()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
cfg: Required<DeviceIdServiceCfg>
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `deviceId` is null only in anomalous cases, e.g when localStorage is not available (due to e.g "out of disk space" on device).
|
|
42
|
+
* In all other cases it should be defined and stable (persisted indefinitely between multiple visits).
|
|
43
|
+
*
|
|
44
|
+
* It is null if the service is run on the server side.
|
|
45
|
+
*/
|
|
46
|
+
deviceId!: string | null
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Selects this device based on "deterministic random selection", according to the defined `rate`.
|
|
50
|
+
* Rate is a floating number between 0 and 1.
|
|
51
|
+
* E.g rate of 0.1 means 10% chance of being selected.
|
|
52
|
+
*
|
|
53
|
+
* Selection is based on deviceId, which is generated random and persisted between visits.
|
|
54
|
+
* Persistence ensures that the selection (similar to an AB-test assignment) "sticks" to the device.
|
|
55
|
+
*
|
|
56
|
+
* If deviceId failed to be generated, e.g due to Device running out-of-space to save a string to localStorage,
|
|
57
|
+
* it will NOT be selected.
|
|
58
|
+
*
|
|
59
|
+
* @returns true if the device is selected.
|
|
60
|
+
*/
|
|
61
|
+
select(rate: number): boolean {
|
|
62
|
+
if (!this.deviceId) {
|
|
63
|
+
this.debug(`deviceId is null, skipping selection`)
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const mod = Math.trunc(rate * 1000)
|
|
68
|
+
// console.log('hash: ', hashCode(this.deviceId)) // todo
|
|
69
|
+
|
|
70
|
+
return hashCode(this.deviceId) % 1000 < mod
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Deletes the persisted deviceId.
|
|
75
|
+
* Keeps it in the service.
|
|
76
|
+
* To remove it from the service, assign deviceIdService.deviceId = null.
|
|
77
|
+
*/
|
|
78
|
+
clearPersistence(): void {
|
|
79
|
+
try {
|
|
80
|
+
globalThis.localStorage.removeItem(this.cfg.localStorageKey)
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.log(err)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Generates a stable Device id if it wasn't previously generated on this device.
|
|
88
|
+
* Otherwise, reads a Device id from persistent storage.
|
|
89
|
+
*/
|
|
90
|
+
private init(): void {
|
|
91
|
+
this.deviceId = null
|
|
92
|
+
if (isServerSide()) return
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
this.deviceId = globalThis.localStorage.getItem(this.cfg.localStorageKey)
|
|
96
|
+
if (this.deviceId) this.debug(`loaded deviceId: ${this.deviceId}`)
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.log(err)
|
|
99
|
+
this.deviceId = null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.deviceId && this.deviceId.length !== deviceIdLength) {
|
|
103
|
+
console.warn(
|
|
104
|
+
`[DeviceIdService] unexpected deviceIdLength (${this.deviceId.length}), will re-generate the id`,
|
|
105
|
+
{ deviceId: this.deviceId },
|
|
106
|
+
)
|
|
107
|
+
this.deviceId = null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!this.deviceId) {
|
|
111
|
+
try {
|
|
112
|
+
this.deviceId = nanoidBrowser(deviceIdLength)
|
|
113
|
+
this.debug(`generated new deviceId: ${this.deviceId}`)
|
|
114
|
+
globalThis.localStorage.setItem(this.cfg.localStorageKey, this.deviceId)
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.log(err)
|
|
117
|
+
this.deviceId = null
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private debug(...args: any[]): void {
|
|
123
|
+
if (this.cfg.debug) console.log('[DeviceIdService]', ...args)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface DeviceIdServiceCfg {
|
|
128
|
+
/**
|
|
129
|
+
* Default: deviceId
|
|
130
|
+
*/
|
|
131
|
+
localStorageKey?: string
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Set to true to enable debug logging.
|
|
135
|
+
*/
|
|
136
|
+
debug?: boolean
|
|
137
|
+
}
|
package/src/error/assert.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
AssertionError,
|
|
7
|
-
BackendErrorResponseObject,
|
|
8
|
-
Class,
|
|
9
|
-
ErrorData,
|
|
10
|
-
ErrorObject,
|
|
11
|
-
} from '..'
|
|
1
|
+
import { _deepEquals } from '../object/deepEquals'
|
|
2
|
+
import { _stringify } from '../string/stringify'
|
|
3
|
+
import type { Class } from '../typeFest'
|
|
4
|
+
import type { BackendErrorResponseObject, ErrorData, ErrorObject } from './error.model'
|
|
5
|
+
import { _isBackendErrorResponseObject, _isErrorObject, AssertionError } from './error.util'
|
|
12
6
|
|
|
13
7
|
/**
|
|
14
8
|
* Evaluates the `condition` (casts it to Boolean).
|
package/src/error/error.util.ts
CHANGED
|
@@ -6,7 +6,10 @@ import type {
|
|
|
6
6
|
ErrorObject,
|
|
7
7
|
HttpRequestErrorData,
|
|
8
8
|
} from '..'
|
|
9
|
-
import {
|
|
9
|
+
import { isServerSide } from '../env'
|
|
10
|
+
import { _jsonParseIfPossible } from '../string/json.util'
|
|
11
|
+
import { _truncate, _truncateMiddle } from '../string/string.util'
|
|
12
|
+
import { _stringify } from '../string/stringify'
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Useful to ensure that error in `catch (err) { ... }`
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,13 @@ export * from './abort'
|
|
|
2
2
|
export * from './array/array.util'
|
|
3
3
|
export * from './array/range'
|
|
4
4
|
export * from './bot'
|
|
5
|
+
export * from './browser/adminService'
|
|
6
|
+
export * from './browser/analytics.util'
|
|
7
|
+
export * from './browser/i18n/fetchTranslationLoader'
|
|
8
|
+
export * from './browser/i18n/translation.service'
|
|
9
|
+
export * from './browser/imageFitter'
|
|
10
|
+
export * from './browser/script.util'
|
|
11
|
+
export * from './browser/topbar'
|
|
5
12
|
export * from './datetime/dateInterval'
|
|
6
13
|
export * from './datetime/localDate'
|
|
7
14
|
export * from './datetime/localTime'
|
|
@@ -20,6 +27,7 @@ export * from './decorators/memoFnAsync'
|
|
|
20
27
|
export * from './decorators/retry.decorator'
|
|
21
28
|
export * from './decorators/timeout.decorator'
|
|
22
29
|
export * from './define'
|
|
30
|
+
export * from './deviceIdService'
|
|
23
31
|
export * from './enum.util'
|
|
24
32
|
export * from './env'
|
|
25
33
|
export * from './env/buildInfo'
|
|
@@ -45,6 +53,7 @@ export * from './log/commonLogger'
|
|
|
45
53
|
export * from './math/math.util'
|
|
46
54
|
export * from './math/sma'
|
|
47
55
|
export * from './math/stack.util'
|
|
56
|
+
export * from './nanoid'
|
|
48
57
|
export * from './number/createDeterministicRandom'
|
|
49
58
|
export * from './number/number.util'
|
|
50
59
|
export * from './object/deepEquals'
|
|
@@ -131,38 +131,47 @@ export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T>
|
|
|
131
131
|
Object.assign(this.schema, { $schema })
|
|
132
132
|
return this
|
|
133
133
|
}
|
|
134
|
+
|
|
134
135
|
$schemaDraft7(): this {
|
|
135
136
|
this.$schema('http://json-schema.org/draft-07/schema#')
|
|
136
137
|
return this
|
|
137
138
|
}
|
|
139
|
+
|
|
138
140
|
$id($id: string): this {
|
|
139
141
|
Object.assign(this.schema, { $id })
|
|
140
142
|
return this
|
|
141
143
|
}
|
|
144
|
+
|
|
142
145
|
title(title: string): this {
|
|
143
146
|
Object.assign(this.schema, { title })
|
|
144
147
|
return this
|
|
145
148
|
}
|
|
149
|
+
|
|
146
150
|
description(description: string): this {
|
|
147
151
|
Object.assign(this.schema, { description })
|
|
148
152
|
return this
|
|
149
153
|
}
|
|
154
|
+
|
|
150
155
|
deprecated(deprecated = true): this {
|
|
151
156
|
Object.assign(this.schema, { deprecated })
|
|
152
157
|
return this
|
|
153
158
|
}
|
|
159
|
+
|
|
154
160
|
type(type: string): this {
|
|
155
161
|
Object.assign(this.schema, { type })
|
|
156
162
|
return this
|
|
157
163
|
}
|
|
164
|
+
|
|
158
165
|
default(v: any): this {
|
|
159
166
|
Object.assign(this.schema, { default: v })
|
|
160
167
|
return this
|
|
161
168
|
}
|
|
169
|
+
|
|
162
170
|
oneOf(schemas: JsonSchema[]): this {
|
|
163
171
|
Object.assign(this.schema, { oneOf: schemas })
|
|
164
172
|
return this
|
|
165
173
|
}
|
|
174
|
+
|
|
166
175
|
allOf(schemas: JsonSchema[]): this {
|
|
167
176
|
Object.assign(this.schema, { allOf: schemas })
|
|
168
177
|
return this
|
|
@@ -211,18 +220,22 @@ export class JsonSchemaNumberBuilder extends JsonSchemaAnyBuilder<number, JsonSc
|
|
|
211
220
|
Object.assign(this.schema, { multipleOf })
|
|
212
221
|
return this
|
|
213
222
|
}
|
|
223
|
+
|
|
214
224
|
min(minimum: number): this {
|
|
215
225
|
Object.assign(this.schema, { minimum })
|
|
216
226
|
return this
|
|
217
227
|
}
|
|
228
|
+
|
|
218
229
|
exclusiveMin(exclusiveMinimum: number): this {
|
|
219
230
|
Object.assign(this.schema, { exclusiveMinimum })
|
|
220
231
|
return this
|
|
221
232
|
}
|
|
233
|
+
|
|
222
234
|
max(maximum: number): this {
|
|
223
235
|
Object.assign(this.schema, { maximum })
|
|
224
236
|
return this
|
|
225
237
|
}
|
|
238
|
+
|
|
226
239
|
exclusiveMax(exclusiveMaximum: number): this {
|
|
227
240
|
Object.assign(this.schema, { exclusiveMaximum })
|
|
228
241
|
return this
|
|
@@ -264,14 +277,17 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder<string, JsonSc
|
|
|
264
277
|
Object.assign(this.schema, { pattern })
|
|
265
278
|
return this
|
|
266
279
|
}
|
|
280
|
+
|
|
267
281
|
min(minLength: number): this {
|
|
268
282
|
Object.assign(this.schema, { minLength })
|
|
269
283
|
return this
|
|
270
284
|
}
|
|
285
|
+
|
|
271
286
|
max(maxLength: number): this {
|
|
272
287
|
Object.assign(this.schema, { maxLength })
|
|
273
288
|
return this
|
|
274
289
|
}
|
|
290
|
+
|
|
275
291
|
length(minLength: number, maxLength: number): this {
|
|
276
292
|
Object.assign(this.schema, { minLength, maxLength })
|
|
277
293
|
return this
|
|
@@ -358,10 +374,12 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
|
|
|
358
374
|
Object.assign(this.schema, { minProperties })
|
|
359
375
|
return this
|
|
360
376
|
}
|
|
377
|
+
|
|
361
378
|
maxProps(maxProperties: number): this {
|
|
362
379
|
Object.assign(this.schema, { maxProperties })
|
|
363
380
|
return this
|
|
364
381
|
}
|
|
382
|
+
|
|
365
383
|
additionalProps(additionalProperties: boolean): this {
|
|
366
384
|
Object.assign(this.schema, { additionalProperties })
|
|
367
385
|
return this
|
|
@@ -400,10 +418,12 @@ export class JsonSchemaArrayBuilder<ITEM> extends JsonSchemaAnyBuilder<
|
|
|
400
418
|
Object.assign(this.schema, { minItems })
|
|
401
419
|
return this
|
|
402
420
|
}
|
|
421
|
+
|
|
403
422
|
max(maxItems: number): this {
|
|
404
423
|
Object.assign(this.schema, { maxItems })
|
|
405
424
|
return this
|
|
406
425
|
}
|
|
426
|
+
|
|
407
427
|
unique(uniqueItems: number): this {
|
|
408
428
|
Object.assign(this.schema, { uniqueItems })
|
|
409
429
|
return this
|
package/src/nanoid.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Vendored from https://github.com/ai/nanoid/blob/main/index.browser.js
|
|
2
|
+
// All credit to nanoid authors: https://github.com/ai/nanoid
|
|
3
|
+
// Reason for vendoring: (still) cannot import esm, and Nanoid went ESM-only since 4.0
|
|
4
|
+
|
|
5
|
+
/// <reference lib="dom" preserve="true" />
|
|
6
|
+
|
|
7
|
+
/* eslint-disable no-bitwise */
|
|
8
|
+
|
|
9
|
+
// "0-9a-zA-Z-_", same as base64url alphabet
|
|
10
|
+
const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Function that takes a length (defaults to 21) and generates a random string id of that length.
|
|
14
|
+
*/
|
|
15
|
+
export type NanoidFunction = (length?: number) => string
|
|
16
|
+
|
|
17
|
+
type NanoidRandomFunction = (bytes: number) => Uint8Array
|
|
18
|
+
|
|
19
|
+
export function nanoidBrowser(length = 21): string {
|
|
20
|
+
let id = ''
|
|
21
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(length))
|
|
22
|
+
while (length--) {
|
|
23
|
+
// Using the bitwise AND operator to "cap" the value of
|
|
24
|
+
// the random byte from 255 to 63, in that way we can make sure
|
|
25
|
+
// that the value will be a valid index for the "chars" string.
|
|
26
|
+
id += urlAlphabet[bytes[length]! & 63]
|
|
27
|
+
}
|
|
28
|
+
return id
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const defaultRandomFunction: NanoidRandomFunction = (bytes: number) =>
|
|
32
|
+
globalThis.crypto.getRandomValues(new Uint8Array(bytes))
|
|
33
|
+
|
|
34
|
+
export function nanoidBrowserCustomAlphabet(alphabet: string, length = 21): NanoidFunction {
|
|
35
|
+
return customRandom(alphabet, length, defaultRandomFunction)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function customRandom(
|
|
39
|
+
alphabet: string,
|
|
40
|
+
defaultSize: number,
|
|
41
|
+
getRandom: NanoidRandomFunction,
|
|
42
|
+
): NanoidFunction {
|
|
43
|
+
// First, a bitmask is necessary to generate the ID. The bitmask makes bytes
|
|
44
|
+
// values closer to the alphabet size. The bitmask calculates the closest
|
|
45
|
+
// `2^31 - 1` number, which exceeds the alphabet size.
|
|
46
|
+
// For example, the bitmask for the alphabet size 30 is 31 (00011111).
|
|
47
|
+
// `Math.clz32` is not used, because it is not available in browsers.
|
|
48
|
+
const mask = (2 << Math.log2(alphabet.length - 1)) - 1
|
|
49
|
+
// Though, the bitmask solution is not perfect since the bytes exceeding
|
|
50
|
+
// the alphabet size are refused. Therefore, to reliably generate the ID,
|
|
51
|
+
// the random bytes redundancy has to be satisfied.
|
|
52
|
+
|
|
53
|
+
// Note: every hardware random generator call is performance expensive,
|
|
54
|
+
// because the system call for entropy collection takes a lot of time.
|
|
55
|
+
// So, to avoid additional system calls, extra bytes are requested in advance.
|
|
56
|
+
|
|
57
|
+
// Next, a step determines how many random bytes to generate.
|
|
58
|
+
// The number of random bytes gets decided upon the ID size, mask,
|
|
59
|
+
// alphabet size, and magic number 1.6 (using 1.6 peaks at performance
|
|
60
|
+
// according to benchmarks).
|
|
61
|
+
|
|
62
|
+
// `-~f => Math.ceil(f)` if f is a float
|
|
63
|
+
// `-~i => i + 1` if i is an integer
|
|
64
|
+
const step = -~((1.6 * mask * defaultSize) / alphabet.length)
|
|
65
|
+
|
|
66
|
+
return (size = defaultSize) => {
|
|
67
|
+
let id = ''
|
|
68
|
+
while (true) {
|
|
69
|
+
const bytes = getRandom(step)
|
|
70
|
+
// A compact alternative for `for (var i = 0; i < step; i++)`.
|
|
71
|
+
let j = step
|
|
72
|
+
while (j--) {
|
|
73
|
+
// Adding `|| ''` refuses a random byte that exceeds the alphabet size.
|
|
74
|
+
id += alphabet[bytes[j]! & mask] || ''
|
|
75
|
+
if (id.length === size) return id
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/* eslint-disable no-bitwise */
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Function that returns a random number between 0 and 1.
|
|
5
|
+
* Exactly same signature as Math.random function.
|
|
6
|
+
*/
|
|
7
|
+
export type RandomFunction = () => number
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* Returns a "deterministic Math.random() function"
|
|
5
11
|
*
|
|
6
12
|
* Based on: https://gist.github.com/mathiasbynens/5670917
|
|
7
13
|
*/
|
|
8
|
-
export function _createDeterministicRandom():
|
|
9
|
-
let seed = 0x2f6e2b1
|
|
14
|
+
export function _createDeterministicRandom(seed = 0x2f6e2b1): RandomFunction {
|
|
10
15
|
return () => {
|
|
11
16
|
// Robert Jenkins’ 32 bit integer hash function
|
|
12
17
|
seed = (seed + 0x7ed55d16 + (seed << 12)) & 0xffffffff
|
package/src/semver.ts
CHANGED
package/src/string/hash.util.ts
CHANGED
|
@@ -11,7 +11,7 @@ const BASE64URL = BASE62 + '-_'
|
|
|
11
11
|
*
|
|
12
12
|
* 1. Performance
|
|
13
13
|
* 2. For non-cryptographic use (where accidental collision is not the end-of-the-world)
|
|
14
|
-
* 3. Compact size (32 bits max, versus 128 in md5; presented in
|
|
14
|
+
* 3. Compact size (32 bits max, versus 128 in md5; presented in smaller number of string json-safe characters)
|
|
15
15
|
*
|
|
16
16
|
* Basically, these functions are as simple as they can be, but still "random enough" for
|
|
17
17
|
* normal non-cryptographic use cases.
|
package/src/web.ts
CHANGED
|
@@ -7,6 +7,12 @@ import { StringMap } from './types'
|
|
|
7
7
|
* Implements WebStorage API by using in-memory storage.
|
|
8
8
|
* Can be useful in SSR environment or unit tests.
|
|
9
9
|
*
|
|
10
|
+
* This is how localStorage can be mocked in Node:
|
|
11
|
+
*
|
|
12
|
+
* Object.assign(globalThis, {
|
|
13
|
+
* localStorage: new InMemoryWebStorage(),
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
10
16
|
* @experimental
|
|
11
17
|
*/
|
|
12
18
|
export class InMemoryWebStorage implements Storage {
|
package/src/zod/zod.util.ts
CHANGED