@kikorin/util 1.0.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/dist/index.cjs ADDED
@@ -0,0 +1,383 @@
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
+ Crono: () => Crono,
24
+ blockKeywords: () => blockKeywords,
25
+ clamp: () => clamp,
26
+ colorFrmRange: () => colorFrmRange,
27
+ createChillUpdater: () => createChillUpdater,
28
+ createChronoTrigger: () => createChronoTrigger,
29
+ createRingBuffer: () => createRingBuffer,
30
+ currentLogLevel: () => currentLogLevel,
31
+ filterKeywords: () => filterKeywords,
32
+ getColorPair: () => getColorPair,
33
+ getContrastingColor: () => getContrastingColor,
34
+ getRndColor: () => getRndColor,
35
+ log: () => log,
36
+ logLevels: () => logLevels,
37
+ randomItem: () => randomItem,
38
+ rng: () => rng
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/logging.ts
43
+ var logLevels = {
44
+ off: 0,
45
+ error: 1,
46
+ warning: 2,
47
+ debug: 3
48
+ };
49
+ var currentLogLevel = { value: logLevels.off };
50
+ var filterKeywords = [];
51
+ var blockKeywords = ["getRandomWord", "applyCooldowns", "calculateBoundingBox", "rollingAverage"];
52
+ function log(level = logLevels.debug, message, keywords = [], ...data) {
53
+ if (!Object.values(logLevels).includes(level)) {
54
+ console.error(`[LOG ERROR] Invalid log level: ${level}`);
55
+ return;
56
+ }
57
+ if (currentLogLevel.value < level) return;
58
+ if (blockKeywords.some((keyword) => keywords.includes(keyword))) return;
59
+ if (filterKeywords.length > 0 && !filterKeywords.some((keyword) => keywords.includes(keyword))) return;
60
+ const copiedData = data.map((item) => {
61
+ try {
62
+ return JSON.parse(JSON.stringify(item));
63
+ } catch {
64
+ return item;
65
+ }
66
+ });
67
+ const method = level === logLevels.error ? "error" : level === logLevels.warning ? "warn" : "log";
68
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
69
+ const keywordInfo = keywords.length > 0 ? ` [Keywords: ${keywords.join(", ")}]` : "";
70
+ console[method](`[${timestamp}] ${message}
71
+ ${keywordInfo}
72
+ `, ...copiedData);
73
+ }
74
+
75
+ // src/ringBuffer.ts
76
+ function createRingBuffer(capacity) {
77
+ if (capacity <= 0) {
78
+ throw new Error("RingBuffer capacity must be > 0");
79
+ }
80
+ const buffer = new Float64Array(capacity);
81
+ let head = 0;
82
+ let length = 0;
83
+ let total = 0;
84
+ return {
85
+ push(value) {
86
+ if (length < capacity) {
87
+ buffer[(head + length) % capacity] = value;
88
+ total += value;
89
+ length++;
90
+ } else {
91
+ const old = buffer[head];
92
+ total -= old;
93
+ buffer[head] = value;
94
+ total += value;
95
+ head = (head + 1) % capacity;
96
+ }
97
+ },
98
+ average() {
99
+ return length === 0 ? 0 : total / length;
100
+ },
101
+ sum() {
102
+ return total;
103
+ },
104
+ size() {
105
+ return length;
106
+ },
107
+ capacity() {
108
+ return capacity;
109
+ },
110
+ clear() {
111
+ head = 0;
112
+ length = 0;
113
+ total = 0;
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/chillUpdate.ts
119
+ function createChillUpdater() {
120
+ const records = /* @__PURE__ */ new Map();
121
+ const pendingKeys = /* @__PURE__ */ new Set();
122
+ const getNow = () => {
123
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
124
+ return performance.now();
125
+ }
126
+ return Date.now();
127
+ };
128
+ const check = () => {
129
+ if (records.size === 0 || pendingKeys.size === 0) return false;
130
+ const now = getNow();
131
+ let didSend = false;
132
+ for (const updateKey of pendingKeys) {
133
+ const record = records.get(updateKey);
134
+ if (record === void 0) {
135
+ pendingKeys.delete(updateKey);
136
+ continue;
137
+ }
138
+ if (record.lastSentAt !== 0 && now - record.lastSentAt < record.minMS) continue;
139
+ record.lastSentAt = now;
140
+ pendingKeys.delete(updateKey);
141
+ try {
142
+ record.updateFunction(record.value);
143
+ } catch (e) {
144
+ log(logLevels.error, "chillUpdater update failed", ["chillUpdate"], updateKey, e);
145
+ }
146
+ didSend = true;
147
+ }
148
+ return didSend;
149
+ };
150
+ const setUpdate = ({
151
+ updateKey,
152
+ updateFunction,
153
+ value,
154
+ minMS = 0
155
+ }) => {
156
+ const safeMinMS = minMS > 0 ? minMS : 0;
157
+ const existing = records.get(updateKey);
158
+ if (existing !== void 0) {
159
+ existing.updateFunction = updateFunction;
160
+ existing.value = value;
161
+ if (safeMinMS !== 0) existing.minMS = safeMinMS;
162
+ pendingKeys.add(updateKey);
163
+ return;
164
+ }
165
+ pendingKeys.add(updateKey);
166
+ records.set(updateKey, {
167
+ updateFunction,
168
+ value,
169
+ minMS: safeMinMS,
170
+ lastSentAt: 0
171
+ });
172
+ };
173
+ return {
174
+ check,
175
+ setUpdate
176
+ };
177
+ }
178
+
179
+ // src/chronoTrigger.ts
180
+ function createChronoTrigger() {
181
+ const scheduledTasks = [];
182
+ const taskIndexById = /* @__PURE__ */ new Map();
183
+ let nextTaskId = 1;
184
+ const maxTicks = 5;
185
+ const catchupWarningThrottleMs = 5e3;
186
+ let running = false;
187
+ let rafId = 0;
188
+ let fps = 0;
189
+ const fpsHist = createRingBuffer(100);
190
+ let lastFrameTime = 0;
191
+ const Start = () => {
192
+ if (running) return;
193
+ running = true;
194
+ const frame = (time) => {
195
+ if (!running) return;
196
+ let delta = 0;
197
+ if (lastFrameTime > 0) {
198
+ delta = time - lastFrameTime;
199
+ if (delta > 0) {
200
+ const newFps = Math.round(1e3 / Math.max(delta, 1));
201
+ fps = newFps !== 1e3 ? newFps : fps;
202
+ fpsHist.push(fps);
203
+ }
204
+ }
205
+ lastFrameTime = time;
206
+ for (let i = 0; i < scheduledTasks.length; i++) {
207
+ const task = scheduledTasks[i];
208
+ if (task.intervalMs <= 0) {
209
+ task.callback(delta);
210
+ continue;
211
+ }
212
+ task.accumulatorMs += delta;
213
+ if (task.accumulatorMs > task.intervalMs * maxTicks) {
214
+ task.accumulatorMs = task.intervalMs * maxTicks;
215
+ if (time - task.lastCatchupWarningMs >= catchupWarningThrottleMs) {
216
+ task.lastCatchupWarningMs = time;
217
+ log(
218
+ logLevels.warning,
219
+ `${task.callbackName} fell behind and attempted catch-up ticks, but catch-up was capped at ${maxTicks}.`,
220
+ ["chronoTrigger"]
221
+ );
222
+ }
223
+ }
224
+ const ticks = task.accumulatorMs / task.intervalMs | 0;
225
+ if (ticks <= 0) continue;
226
+ task.accumulatorMs -= ticks * task.intervalMs;
227
+ for (let t = 0; t < ticks; t++) {
228
+ task.callback(task.intervalMs);
229
+ }
230
+ }
231
+ rafId = requestAnimationFrame(frame);
232
+ };
233
+ rafId = requestAnimationFrame(frame);
234
+ };
235
+ const Stop = () => {
236
+ running = false;
237
+ lastFrameTime = 0;
238
+ if (rafId) {
239
+ cancelAnimationFrame(rafId);
240
+ rafId = 0;
241
+ }
242
+ };
243
+ const dispose = (id) => {
244
+ const index = taskIndexById.get(id);
245
+ if (index === void 0) return false;
246
+ const lastIndex = scheduledTasks.length - 1;
247
+ if (index !== lastIndex) {
248
+ const lastTask = scheduledTasks[lastIndex];
249
+ scheduledTasks[index] = lastTask;
250
+ taskIndexById.set(lastTask.id, index);
251
+ }
252
+ scheduledTasks.pop();
253
+ taskIndexById.delete(id);
254
+ return true;
255
+ };
256
+ const runAt = ({
257
+ name,
258
+ fpsTarget,
259
+ callback
260
+ }) => {
261
+ if (typeof callback !== "function") {
262
+ throw new Error("runAt requires a callback function.");
263
+ }
264
+ let intervalMs = 0;
265
+ if (fpsTarget !== void 0) {
266
+ if (!Number.isFinite(fpsTarget) || fpsTarget <= 0) {
267
+ throw new Error("fpsTarget must be a positive number when provided.");
268
+ }
269
+ intervalMs = 1e3 / fpsTarget;
270
+ }
271
+ const id = nextTaskId++;
272
+ const callbackName = name || callback.name || "anonymous";
273
+ const accumulatorMs = intervalMs > 0 ? intervalMs : 0;
274
+ scheduledTasks.push({
275
+ id,
276
+ callback,
277
+ callbackName,
278
+ intervalMs,
279
+ accumulatorMs,
280
+ lastCatchupWarningMs: -Infinity
281
+ });
282
+ taskIndexById.set(id, scheduledTasks.length - 1);
283
+ return id;
284
+ };
285
+ const CurrentFPS = () => fps;
286
+ const AverageFPS = () => Math.round(fpsHist.average());
287
+ return { Start, Stop, runAt, dispose, CurrentFPS, AverageFPS };
288
+ }
289
+ var Crono = createChronoTrigger();
290
+
291
+ // src/random.ts
292
+ function rng(low, high, decimals = null) {
293
+ const min = low < high ? low : high;
294
+ const max = low < high ? high : low;
295
+ const lowDecimals = (Math.abs(low).toString().split(".")[1] || "").length;
296
+ const highDecimals = (Math.abs(high).toString().split(".")[1] || "").length;
297
+ const derivedDecimals = Math.max(lowDecimals, highDecimals);
298
+ const precision = decimals !== null ? decimals : derivedDecimals;
299
+ const factor = 10 ** precision;
300
+ const randomValue = Math.random() * (max - min) + min;
301
+ return Math.round(randomValue * factor) / factor;
302
+ }
303
+ function clamp(value, low, high) {
304
+ return Math.max(low, Math.min(high, value));
305
+ }
306
+ function randomItem(collection) {
307
+ if (collection.length === 0) {
308
+ log(logLevels.error, "Cannot select a random item from an empty array.", ["randomItem"], collection);
309
+ }
310
+ return collection[Math.floor(rng(0, collection.length - 1))];
311
+ }
312
+ function getRndColor() {
313
+ const red = Math.round(Math.random() * 234 + 10);
314
+ const green = Math.round(Math.random() * 234 + 20);
315
+ const blue = Math.round(Math.random() * 234 + 20);
316
+ const color = "#" + red.toString(16) + green.toString(16) + blue.toString(16);
317
+ return color;
318
+ }
319
+ function getColorPair() {
320
+ const red = Math.round(Math.random() * 234 + 10);
321
+ const antiRed = Math.abs(red - 234) + 20;
322
+ const green = Math.round(Math.random() * 234 + 20);
323
+ const antiGreen = Math.abs(green - 234) + 20;
324
+ const blue = Math.round(Math.random() * 234 + 20);
325
+ const antiblue = Math.abs(blue - 234) + 20;
326
+ const color = "#" + red.toString(16) + green.toString(16) + blue.toString(16);
327
+ const antiColor = "#" + antiRed.toString(16) + antiGreen.toString(16) + antiblue.toString(16);
328
+ return { c1: color, c2: antiColor };
329
+ }
330
+ function colorFrmRange(c1, c2, percent) {
331
+ percent = percent / 100;
332
+ const c1R = parseInt(c1.slice(1, 3), 16);
333
+ const c1G = parseInt(c1.slice(3, 5), 16);
334
+ const c1B = parseInt(c1.slice(5, 7), 16);
335
+ const c2R = parseInt(c2.slice(1, 3), 16);
336
+ const c2G = parseInt(c2.slice(3, 5), 16);
337
+ const c2B = parseInt(c2.slice(5, 7), 16);
338
+ const rDif = c1R - c2R;
339
+ const gDif = c1G - c2G;
340
+ const bDif = c1B - c2B;
341
+ let red = Math.round(c1R - percent * rDif).toString(16);
342
+ let green = Math.round(c1G - percent * gDif).toString(16);
343
+ let blue = Math.round(c1B - percent * bDif).toString(16);
344
+ if (red.length < 2) {
345
+ red = "0" + red;
346
+ }
347
+ if (green.length < 2) {
348
+ green = "0" + green;
349
+ }
350
+ if (blue.length < 2) {
351
+ blue = "0" + blue;
352
+ }
353
+ const color = "#" + red + green + blue;
354
+ return color;
355
+ }
356
+ function getContrastingColor(hexColor) {
357
+ const normalizedHex = hexColor.replace(/^#/, "");
358
+ const r = parseInt(normalizedHex.slice(0, 2), 16);
359
+ const g = parseInt(normalizedHex.slice(2, 4), 16);
360
+ const b = parseInt(normalizedHex.slice(4, 6), 16);
361
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
362
+ return luminance > 0.5 ? "#000000" : "#FFFFFF";
363
+ }
364
+ // Annotate the CommonJS export names for ESM import in node:
365
+ 0 && (module.exports = {
366
+ Crono,
367
+ blockKeywords,
368
+ clamp,
369
+ colorFrmRange,
370
+ createChillUpdater,
371
+ createChronoTrigger,
372
+ createRingBuffer,
373
+ currentLogLevel,
374
+ filterKeywords,
375
+ getColorPair,
376
+ getContrastingColor,
377
+ getRndColor,
378
+ log,
379
+ logLevels,
380
+ randomItem,
381
+ rng
382
+ });
383
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/logging.ts","../src/ringBuffer.ts","../src/chillUpdate.ts","../src/chronoTrigger.ts","../src/random.ts"],"sourcesContent":["export * from './logging'\nexport * from './ringBuffer'\nexport * from './chillUpdate'\nexport * from './chronoTrigger'\nexport * from './random'\n","export const logLevels = {\n off: 0,\n error: 1,\n warning: 2,\n debug: 3,\n};\n\n// Mutable object so any module can update the level at runtime.\n// Off by default so production builds are quiet; set to logLevels.debug to enable.\nexport const currentLogLevel = { value: logLevels.off };\n\n// filterKeywords: when non-empty, only messages tagged with one of these pass through.\n// blockKeywords: messages tagged with any of these are always suppressed.\nexport const filterKeywords: string[] = [];\nexport const blockKeywords: string[] = [\"getRandomWord\", \"applyCooldowns\", \"calculateBoundingBox\", \"rollingAverage\"];\n\nexport function log(\n level: number = logLevels.debug,\n message: string,\n keywords: string[] = [],\n ...data: unknown[]\n ): void {\n if (!Object.values(logLevels).includes(level)) {\n console.error(`[LOG ERROR] Invalid log level: ${level}`);\n return;\n }\n\n if (currentLogLevel.value < level) return;\n\n if (blockKeywords.some(keyword => keywords.includes(keyword))) return;\n\n if (filterKeywords.length > 0 && !filterKeywords.some(keyword => keywords.includes(keyword))) return;\n\n // Deep copy prevents logged objects from mutating after the call.\n const copiedData = data.map(item => {\n try {\n return JSON.parse(JSON.stringify(item));\n } catch {\n return item;\n }\n });\n\n const method = level === logLevels.error ? \"error\"\n : level === logLevels.warning ? \"warn\"\n : \"log\";\n\n const timestamp = new Date().toISOString();\n const keywordInfo = keywords.length > 0 ? ` [Keywords: ${keywords.join(\", \")}]` : \"\";\n\n console[method](`[${timestamp}] ${message}\\n${keywordInfo}\\n`, ...copiedData);\n }\n","export interface RingBuffer {\n push(value: number): void\n average(): number\n sum(): number\n size(): number\n capacity(): number\n clear(): void\n}\n\nexport function createRingBuffer(capacity: number): RingBuffer {\n if (capacity <= 0) {\n throw new Error(\"RingBuffer capacity must be > 0\")\n }\n\n const buffer = new Float64Array(capacity)\n let head = 0 // points to oldest value\n let length = 0\n let total = 0\n\n return {\n push(value: number) {\n if (length < capacity) {\n // still filling\n buffer[(head + length) % capacity] = value\n total += value\n length++\n } else {\n // overwrite oldest\n const old = buffer[head]\n total -= old\n buffer[head] = value\n total += value\n head = (head + 1) % capacity\n }\n },\n\n average() {\n return length === 0 ? 0 : total / length\n },\n\n sum() {\n return total\n },\n\n size() {\n return length\n },\n\n capacity() {\n return capacity\n },\n\n clear() {\n head = 0\n length = 0\n total = 0\n }\n }\n}\n","import { log, logLevels } from \"./logging\";\n\nexport type ChillUpdateFn<TValue> = (value: TValue) => void;\n\nexport type ChillUpdaterSetParams<TValue> = {\n updateKey: string;\n updateFunction: ChillUpdateFn<TValue>;\n value: TValue;\n minMS?: number;\n};\n\nexport type ChillUpdater = {\n check: () => boolean;\n setUpdate: <TValue>(params: ChillUpdaterSetParams<TValue>) => void;\n};\n\ntype UpdateRecord = {\n updateFunction: ChillUpdateFn<unknown>;\n value: unknown;\n minMS: number;\n lastSentAt: number;\n};\n\nfunction createChillUpdater(): ChillUpdater {\n const records = new Map<string, UpdateRecord>();\n const pendingKeys = new Set<string>();\n const getNow = (): number => {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n };\n\n const check = (): boolean => {\n if (records.size === 0 || pendingKeys.size === 0) return false;\n\n const now = getNow();\n let didSend = false;\n\n for (const updateKey of pendingKeys) {\n const record = records.get(updateKey);\n if (record === undefined) {\n pendingKeys.delete(updateKey);\n continue;\n }\n if (record.lastSentAt !== 0 && now - record.lastSentAt < record.minMS) continue;\n\n record.lastSentAt = now;\n pendingKeys.delete(updateKey);\n try {\n record.updateFunction(record.value);\n }\n catch (e) {\n log(logLevels.error, \"chillUpdater update failed\", [\"chillUpdate\"], updateKey, e);\n }\n\n didSend = true;\n }\n\n return didSend;\n };\n\n const setUpdate = <TValue>({\n updateKey,\n updateFunction,\n value,\n minMS = 0,\n }: ChillUpdaterSetParams<TValue>): void => {\n const safeMinMS = minMS > 0 ? minMS : 0;\n const existing = records.get(updateKey);\n\n if (existing !== undefined) {\n existing.updateFunction = updateFunction as ChillUpdateFn<unknown>;\n existing.value = value;\n if (safeMinMS !== 0) existing.minMS = safeMinMS;\n pendingKeys.add(updateKey);\n return;\n }\n\n pendingKeys.add(updateKey);\n records.set(updateKey, {\n updateFunction: updateFunction as ChillUpdateFn<unknown>,\n value,\n minMS: safeMinMS,\n lastSentAt: 0\n });\n };\n\n return {\n check,\n setUpdate\n };\n}\n\nexport { createChillUpdater };\n","// ChronoTrigger Library (CT) using Factory Functions with FPS Tracking\nimport { log, logLevels } from './logging';\nimport { createRingBuffer } from './ringBuffer';\n\ninterface ScheduledTask {\n id: number;\n callback: (deltaMs: number) => void;\n callbackName: string;\n intervalMs: number;\n accumulatorMs: number;\n lastCatchupWarningMs: number;\n}\n\nexport interface ChronoTrigger {\n Start: () => void;\n Stop: () => void;\n runAt: (options: { name?: string; callback: (deltaMs: number) => void; fpsTarget?: number }) => number;\n dispose: (id: number) => boolean;\n CurrentFPS: () => number;\n AverageFPS: () => number;\n}\n\nexport function createChronoTrigger(): ChronoTrigger {\n const scheduledTasks: ScheduledTask[] = [];\n const taskIndexById = new Map<number, number>();\n let nextTaskId = 1;\n const maxTicks = 5;\n const catchupWarningThrottleMs = 5000;\n let running = false;\n let rafId = 0;\n let fps = 0; // Tracks the current running FPS\n const fpsHist = createRingBuffer(100);\n let lastFrameTime = 0;\n\n const Start = (): void => {\n if (running) return;\n running = true;\n const frame = (time: number): void => {\n if (!running) return;\n\n let delta = 0;\n if (lastFrameTime > 0) {\n delta = time - lastFrameTime;\n if (delta > 0) {\n // using Math.max to avoid divide by 0\n const newFps = Math.round(1000 / Math.max(delta, 1));\n fps = newFps !== 1000 ? newFps : fps;\n fpsHist.push(fps);\n }\n }\n lastFrameTime = time;\n\n for (let i = 0; i < scheduledTasks.length; i++) {\n const task = scheduledTasks[i];\n if (task.intervalMs <= 0) {\n task.callback(delta);\n continue;\n }\n\n task.accumulatorMs += delta;\n if (task.accumulatorMs > task.intervalMs * maxTicks) {\n task.accumulatorMs = task.intervalMs * maxTicks;\n if (time - task.lastCatchupWarningMs >= catchupWarningThrottleMs) {\n task.lastCatchupWarningMs = time;\n log(\n logLevels.warning,\n `${task.callbackName} fell behind and attempted catch-up ticks, but catch-up was capped at ${maxTicks}.`,\n [\"chronoTrigger\"],\n );\n }\n }\n\n const ticks = (task.accumulatorMs / task.intervalMs) | 0;\n if (ticks <= 0) continue;\n\n task.accumulatorMs -= ticks * task.intervalMs;\n for (let t = 0; t < ticks; t++) {\n task.callback(task.intervalMs);\n }\n }\n\n rafId = requestAnimationFrame(frame);\n };\n rafId = requestAnimationFrame(frame);\n };\n\n const Stop = (): void => {\n running = false;\n lastFrameTime = 0;\n if (rafId) {\n cancelAnimationFrame(rafId);\n rafId = 0;\n }\n };\n\n const dispose = (id: number): boolean => {\n const index = taskIndexById.get(id);\n if (index === undefined) return false;\n\n const lastIndex = scheduledTasks.length - 1;\n if (index !== lastIndex) {\n const lastTask = scheduledTasks[lastIndex];\n scheduledTasks[index] = lastTask;\n taskIndexById.set(lastTask.id, index);\n }\n\n scheduledTasks.pop();\n taskIndexById.delete(id);\n return true;\n };\n\n const runAt = ({\n name,\n fpsTarget,\n callback,\n }: {\n name?: string;\n callback: (deltaMs: number) => void;\n fpsTarget?: number;\n }): number => {\n if (typeof callback !== \"function\") {\n throw new Error(\"runAt requires a callback function.\");\n }\n\n let intervalMs = 0;\n if (fpsTarget !== undefined) {\n if (!Number.isFinite(fpsTarget) || fpsTarget <= 0) {\n throw new Error(\"fpsTarget must be a positive number when provided.\");\n }\n\n intervalMs = 1000 / fpsTarget;\n }\n\n const id = nextTaskId++;\n const callbackName = name || callback.name || \"anonymous\";\n const accumulatorMs = intervalMs > 0 ? intervalMs : 0;\n scheduledTasks.push({\n id,\n callback,\n callbackName,\n intervalMs,\n accumulatorMs,\n lastCatchupWarningMs: -Infinity,\n });\n taskIndexById.set(id, scheduledTasks.length - 1);\n return id;\n };\n\n const CurrentFPS = (): number => fps;\n const AverageFPS = (): number => Math.round(fpsHist.average());\n\n return { Start, Stop, runAt, dispose, CurrentFPS, AverageFPS };\n}\n\nexport const Crono = createChronoTrigger();\n","import { log, logLevels } from \"./logging\";\n\n/**\n * Generates a random number within the specified range, supporting precision.\n *\n * - If `decimals` is provided, the result is rounded to that many decimal places.\n * - If `decimals` is not provided, the precision is derived from the decimal places of the `low` and `high` inputs.\n *\n * @param {number} low - The minimum value (inclusive) of the range.\n * @param {number} high - The maximum value (exclusive) of the range.\n * @param {number | null} [decimals=null] - The number of decimal places to round to. If not provided, precision is derived automatically.\n * @returns {number} - A random number between `low` and `high`, rounded to the specified or derived precision.\n *\n * @example\n * // Example 1: Whole number output\n * rng(1, 10); // Could return 7\n *\n * @example\n * // Example 2: Fixed decimal places\n * rng(1.5, 3.5, 2); // Could return 2.78\n *\n * @example\n * // Example 3: Automatically derived precision\n * rng(0.001, 0.1); // Could return 0.023 (3 decimal places derived)\n */\n\nexport function rng(low: number, high: number, decimals: number | null = null): number {\n const min = low < high ? low : high;\n const max = low < high ? high : low;\n const lowDecimals = (Math.abs(low).toString().split(\".\")[1] || \"\").length;\n const highDecimals = (Math.abs(high).toString().split(\".\")[1] || \"\").length;\n const derivedDecimals = Math.max(lowDecimals, highDecimals); // Derive max precision\n const precision = decimals !== null ? decimals : derivedDecimals;\n const factor = 10 ** precision;\n\n const randomValue = Math.random() * (max - min) + min;\n\n return Math.round(randomValue * factor) / factor;\n}\n\nexport function clamp(value: number, low: number, high: number) {\n return Math.max(low, Math.min(high, value));\n}\n\n/**\n * Selects a random item from an array of a specific type.\n *\n * Utilizes the `rng` function to generate a random index within the bounds of the array.\n *\n * @template T - The type of elements in the array.\n * @param {T[]} collection - The array to select a random item from.\n * @returns {T} - A randomly selected item from the array.\n *\n * @throws {Error} Throws an error if the collection is empty.\n *\n * @example\n * // Example 1: Random item from a number array\n * const numbers = [1, 2, 3, 4, 5];\n * const randomNum = randomItem(numbers); // TypeScript infers: number\n *\n * @example\n * // Example 2: Random item from a string array\n * const fruits = [\"apple\", \"banana\", \"cherry\"];\n * const randomFruit = randomItem(fruits); // TypeScript infers: string\n */\nexport function randomItem<T>(collection: T[]): T {\n if (collection.length === 0) {\n log(logLevels.error, \"Cannot select a random item from an empty array.\", ['randomItem'], collection);\n }\n return collection[Math.floor(rng(0, collection.length-1))];\n}\n\nexport function getRndColor(){\n\n const red = Math.round(Math.random() * (234)+10);\n\tconst green= Math.round(Math.random() * (234)+20);\n\tconst blue= Math.round(Math.random() * (234)+20);\n\n\tconst color =\"#\"+red.toString(16)+green.toString(16)+blue.toString(16);\n\n\treturn color;\n}\n\nexport function getColorPair() {\n\n const red = Math.round(Math.random() * (234) + 10);\n const antiRed = Math.abs(red - 234) + 20;\n const green = Math.round(Math.random() * (234) + 20);\n const antiGreen = Math.abs(green - 234) + 20;\n const blue = Math.round(Math.random() * (234) + 20);\n const antiblue = Math.abs(blue - 234) + 20;\n\n const color = \"#\" + red.toString(16) + green.toString(16) + blue.toString(16);\n const antiColor = \"#\" + antiRed.toString(16) + antiGreen.toString(16) + antiblue.toString(16);\n\n return { c1: color, c2: antiColor };\n }\n\n export function colorFrmRange(c1: string, c2: string, percent: number) {\n percent = percent / 100;\n\n const c1R = parseInt(c1.slice(1, 3), 16);\n const c1G = parseInt(c1.slice(3, 5), 16);\n const c1B = parseInt(c1.slice(5, 7), 16);\n\n const c2R = parseInt(c2.slice(1, 3), 16);\n const c2G = parseInt(c2.slice(3, 5), 16);\n const c2B = parseInt(c2.slice(5, 7), 16);\n\n const rDif = c1R - c2R;\n const gDif = c1G - c2G;\n const bDif = c1B - c2B;\n\n let red = Math.round(c1R - percent * rDif).toString(16);\n let green = Math.round(c1G - percent * gDif).toString(16);\n let blue = Math.round(c1B - percent * bDif).toString(16);\n\n if (red.length < 2) { red = \"0\" + red; }\n if (green.length < 2) { green = \"0\" + green; }\n if (blue.length < 2) { blue = \"0\" + blue; }\n\n const color = \"#\" + red + green + blue;\n\n return color;\n }\n\n /**\n * Determines whether a hex color is light or dark and returns a contrasting color (black or white).\n * @param hexColor - The hex color string (e.g., \"#FFFFFF\" or \"FFFFFF\").\n * @returns A string representing the contrasting color (\"#000000\" for black or \"#FFFFFF\" for white).\n */\nexport function getContrastingColor(hexColor: string): string {\n // Remove the '#' if present\n const normalizedHex = hexColor.replace(/^#/, \"\");\n\n // Parse the RGB values\n const r = parseInt(normalizedHex.slice(0, 2), 16);\n const g = parseInt(normalizedHex.slice(2, 4), 16);\n const b = parseInt(normalizedHex.slice(4, 6), 16);\n\n // Calculate the relative luminance (standard formula)\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n\n // Return black for light backgrounds, white for dark backgrounds\n return luminance > 0.5 ? \"#000000\" : \"#FFFFFF\";\n }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,112 @@
1
+ declare const logLevels: {
2
+ off: number;
3
+ error: number;
4
+ warning: number;
5
+ debug: number;
6
+ };
7
+ declare const currentLogLevel: {
8
+ value: number;
9
+ };
10
+ declare const filterKeywords: string[];
11
+ declare const blockKeywords: string[];
12
+ declare function log(level: number | undefined, message: string, keywords?: string[], ...data: unknown[]): void;
13
+
14
+ interface RingBuffer {
15
+ push(value: number): void;
16
+ average(): number;
17
+ sum(): number;
18
+ size(): number;
19
+ capacity(): number;
20
+ clear(): void;
21
+ }
22
+ declare function createRingBuffer(capacity: number): RingBuffer;
23
+
24
+ type ChillUpdateFn<TValue> = (value: TValue) => void;
25
+ type ChillUpdaterSetParams<TValue> = {
26
+ updateKey: string;
27
+ updateFunction: ChillUpdateFn<TValue>;
28
+ value: TValue;
29
+ minMS?: number;
30
+ };
31
+ type ChillUpdater = {
32
+ check: () => boolean;
33
+ setUpdate: <TValue>(params: ChillUpdaterSetParams<TValue>) => void;
34
+ };
35
+ declare function createChillUpdater(): ChillUpdater;
36
+
37
+ interface ChronoTrigger {
38
+ Start: () => void;
39
+ Stop: () => void;
40
+ runAt: (options: {
41
+ name?: string;
42
+ callback: (deltaMs: number) => void;
43
+ fpsTarget?: number;
44
+ }) => number;
45
+ dispose: (id: number) => boolean;
46
+ CurrentFPS: () => number;
47
+ AverageFPS: () => number;
48
+ }
49
+ declare function createChronoTrigger(): ChronoTrigger;
50
+ declare const Crono: ChronoTrigger;
51
+
52
+ /**
53
+ * Generates a random number within the specified range, supporting precision.
54
+ *
55
+ * - If `decimals` is provided, the result is rounded to that many decimal places.
56
+ * - If `decimals` is not provided, the precision is derived from the decimal places of the `low` and `high` inputs.
57
+ *
58
+ * @param {number} low - The minimum value (inclusive) of the range.
59
+ * @param {number} high - The maximum value (exclusive) of the range.
60
+ * @param {number | null} [decimals=null] - The number of decimal places to round to. If not provided, precision is derived automatically.
61
+ * @returns {number} - A random number between `low` and `high`, rounded to the specified or derived precision.
62
+ *
63
+ * @example
64
+ * // Example 1: Whole number output
65
+ * rng(1, 10); // Could return 7
66
+ *
67
+ * @example
68
+ * // Example 2: Fixed decimal places
69
+ * rng(1.5, 3.5, 2); // Could return 2.78
70
+ *
71
+ * @example
72
+ * // Example 3: Automatically derived precision
73
+ * rng(0.001, 0.1); // Could return 0.023 (3 decimal places derived)
74
+ */
75
+ declare function rng(low: number, high: number, decimals?: number | null): number;
76
+ declare function clamp(value: number, low: number, high: number): number;
77
+ /**
78
+ * Selects a random item from an array of a specific type.
79
+ *
80
+ * Utilizes the `rng` function to generate a random index within the bounds of the array.
81
+ *
82
+ * @template T - The type of elements in the array.
83
+ * @param {T[]} collection - The array to select a random item from.
84
+ * @returns {T} - A randomly selected item from the array.
85
+ *
86
+ * @throws {Error} Throws an error if the collection is empty.
87
+ *
88
+ * @example
89
+ * // Example 1: Random item from a number array
90
+ * const numbers = [1, 2, 3, 4, 5];
91
+ * const randomNum = randomItem(numbers); // TypeScript infers: number
92
+ *
93
+ * @example
94
+ * // Example 2: Random item from a string array
95
+ * const fruits = ["apple", "banana", "cherry"];
96
+ * const randomFruit = randomItem(fruits); // TypeScript infers: string
97
+ */
98
+ declare function randomItem<T>(collection: T[]): T;
99
+ declare function getRndColor(): string;
100
+ declare function getColorPair(): {
101
+ c1: string;
102
+ c2: string;
103
+ };
104
+ declare function colorFrmRange(c1: string, c2: string, percent: number): string;
105
+ /**
106
+ * Determines whether a hex color is light or dark and returns a contrasting color (black or white).
107
+ * @param hexColor - The hex color string (e.g., "#FFFFFF" or "FFFFFF").
108
+ * @returns A string representing the contrasting color ("#000000" for black or "#FFFFFF" for white).
109
+ */
110
+ declare function getContrastingColor(hexColor: string): string;
111
+
112
+ export { type ChillUpdateFn, type ChillUpdater, type ChillUpdaterSetParams, type ChronoTrigger, Crono, type RingBuffer, blockKeywords, clamp, colorFrmRange, createChillUpdater, createChronoTrigger, createRingBuffer, currentLogLevel, filterKeywords, getColorPair, getContrastingColor, getRndColor, log, logLevels, randomItem, rng };
@@ -0,0 +1,112 @@
1
+ declare const logLevels: {
2
+ off: number;
3
+ error: number;
4
+ warning: number;
5
+ debug: number;
6
+ };
7
+ declare const currentLogLevel: {
8
+ value: number;
9
+ };
10
+ declare const filterKeywords: string[];
11
+ declare const blockKeywords: string[];
12
+ declare function log(level: number | undefined, message: string, keywords?: string[], ...data: unknown[]): void;
13
+
14
+ interface RingBuffer {
15
+ push(value: number): void;
16
+ average(): number;
17
+ sum(): number;
18
+ size(): number;
19
+ capacity(): number;
20
+ clear(): void;
21
+ }
22
+ declare function createRingBuffer(capacity: number): RingBuffer;
23
+
24
+ type ChillUpdateFn<TValue> = (value: TValue) => void;
25
+ type ChillUpdaterSetParams<TValue> = {
26
+ updateKey: string;
27
+ updateFunction: ChillUpdateFn<TValue>;
28
+ value: TValue;
29
+ minMS?: number;
30
+ };
31
+ type ChillUpdater = {
32
+ check: () => boolean;
33
+ setUpdate: <TValue>(params: ChillUpdaterSetParams<TValue>) => void;
34
+ };
35
+ declare function createChillUpdater(): ChillUpdater;
36
+
37
+ interface ChronoTrigger {
38
+ Start: () => void;
39
+ Stop: () => void;
40
+ runAt: (options: {
41
+ name?: string;
42
+ callback: (deltaMs: number) => void;
43
+ fpsTarget?: number;
44
+ }) => number;
45
+ dispose: (id: number) => boolean;
46
+ CurrentFPS: () => number;
47
+ AverageFPS: () => number;
48
+ }
49
+ declare function createChronoTrigger(): ChronoTrigger;
50
+ declare const Crono: ChronoTrigger;
51
+
52
+ /**
53
+ * Generates a random number within the specified range, supporting precision.
54
+ *
55
+ * - If `decimals` is provided, the result is rounded to that many decimal places.
56
+ * - If `decimals` is not provided, the precision is derived from the decimal places of the `low` and `high` inputs.
57
+ *
58
+ * @param {number} low - The minimum value (inclusive) of the range.
59
+ * @param {number} high - The maximum value (exclusive) of the range.
60
+ * @param {number | null} [decimals=null] - The number of decimal places to round to. If not provided, precision is derived automatically.
61
+ * @returns {number} - A random number between `low` and `high`, rounded to the specified or derived precision.
62
+ *
63
+ * @example
64
+ * // Example 1: Whole number output
65
+ * rng(1, 10); // Could return 7
66
+ *
67
+ * @example
68
+ * // Example 2: Fixed decimal places
69
+ * rng(1.5, 3.5, 2); // Could return 2.78
70
+ *
71
+ * @example
72
+ * // Example 3: Automatically derived precision
73
+ * rng(0.001, 0.1); // Could return 0.023 (3 decimal places derived)
74
+ */
75
+ declare function rng(low: number, high: number, decimals?: number | null): number;
76
+ declare function clamp(value: number, low: number, high: number): number;
77
+ /**
78
+ * Selects a random item from an array of a specific type.
79
+ *
80
+ * Utilizes the `rng` function to generate a random index within the bounds of the array.
81
+ *
82
+ * @template T - The type of elements in the array.
83
+ * @param {T[]} collection - The array to select a random item from.
84
+ * @returns {T} - A randomly selected item from the array.
85
+ *
86
+ * @throws {Error} Throws an error if the collection is empty.
87
+ *
88
+ * @example
89
+ * // Example 1: Random item from a number array
90
+ * const numbers = [1, 2, 3, 4, 5];
91
+ * const randomNum = randomItem(numbers); // TypeScript infers: number
92
+ *
93
+ * @example
94
+ * // Example 2: Random item from a string array
95
+ * const fruits = ["apple", "banana", "cherry"];
96
+ * const randomFruit = randomItem(fruits); // TypeScript infers: string
97
+ */
98
+ declare function randomItem<T>(collection: T[]): T;
99
+ declare function getRndColor(): string;
100
+ declare function getColorPair(): {
101
+ c1: string;
102
+ c2: string;
103
+ };
104
+ declare function colorFrmRange(c1: string, c2: string, percent: number): string;
105
+ /**
106
+ * Determines whether a hex color is light or dark and returns a contrasting color (black or white).
107
+ * @param hexColor - The hex color string (e.g., "#FFFFFF" or "FFFFFF").
108
+ * @returns A string representing the contrasting color ("#000000" for black or "#FFFFFF" for white).
109
+ */
110
+ declare function getContrastingColor(hexColor: string): string;
111
+
112
+ export { type ChillUpdateFn, type ChillUpdater, type ChillUpdaterSetParams, type ChronoTrigger, Crono, type RingBuffer, blockKeywords, clamp, colorFrmRange, createChillUpdater, createChronoTrigger, createRingBuffer, currentLogLevel, filterKeywords, getColorPair, getContrastingColor, getRndColor, log, logLevels, randomItem, rng };
package/dist/index.js ADDED
@@ -0,0 +1,341 @@
1
+ // src/logging.ts
2
+ var logLevels = {
3
+ off: 0,
4
+ error: 1,
5
+ warning: 2,
6
+ debug: 3
7
+ };
8
+ var currentLogLevel = { value: logLevels.off };
9
+ var filterKeywords = [];
10
+ var blockKeywords = ["getRandomWord", "applyCooldowns", "calculateBoundingBox", "rollingAverage"];
11
+ function log(level = logLevels.debug, message, keywords = [], ...data) {
12
+ if (!Object.values(logLevels).includes(level)) {
13
+ console.error(`[LOG ERROR] Invalid log level: ${level}`);
14
+ return;
15
+ }
16
+ if (currentLogLevel.value < level) return;
17
+ if (blockKeywords.some((keyword) => keywords.includes(keyword))) return;
18
+ if (filterKeywords.length > 0 && !filterKeywords.some((keyword) => keywords.includes(keyword))) return;
19
+ const copiedData = data.map((item) => {
20
+ try {
21
+ return JSON.parse(JSON.stringify(item));
22
+ } catch {
23
+ return item;
24
+ }
25
+ });
26
+ const method = level === logLevels.error ? "error" : level === logLevels.warning ? "warn" : "log";
27
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
28
+ const keywordInfo = keywords.length > 0 ? ` [Keywords: ${keywords.join(", ")}]` : "";
29
+ console[method](`[${timestamp}] ${message}
30
+ ${keywordInfo}
31
+ `, ...copiedData);
32
+ }
33
+
34
+ // src/ringBuffer.ts
35
+ function createRingBuffer(capacity) {
36
+ if (capacity <= 0) {
37
+ throw new Error("RingBuffer capacity must be > 0");
38
+ }
39
+ const buffer = new Float64Array(capacity);
40
+ let head = 0;
41
+ let length = 0;
42
+ let total = 0;
43
+ return {
44
+ push(value) {
45
+ if (length < capacity) {
46
+ buffer[(head + length) % capacity] = value;
47
+ total += value;
48
+ length++;
49
+ } else {
50
+ const old = buffer[head];
51
+ total -= old;
52
+ buffer[head] = value;
53
+ total += value;
54
+ head = (head + 1) % capacity;
55
+ }
56
+ },
57
+ average() {
58
+ return length === 0 ? 0 : total / length;
59
+ },
60
+ sum() {
61
+ return total;
62
+ },
63
+ size() {
64
+ return length;
65
+ },
66
+ capacity() {
67
+ return capacity;
68
+ },
69
+ clear() {
70
+ head = 0;
71
+ length = 0;
72
+ total = 0;
73
+ }
74
+ };
75
+ }
76
+
77
+ // src/chillUpdate.ts
78
+ function createChillUpdater() {
79
+ const records = /* @__PURE__ */ new Map();
80
+ const pendingKeys = /* @__PURE__ */ new Set();
81
+ const getNow = () => {
82
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
83
+ return performance.now();
84
+ }
85
+ return Date.now();
86
+ };
87
+ const check = () => {
88
+ if (records.size === 0 || pendingKeys.size === 0) return false;
89
+ const now = getNow();
90
+ let didSend = false;
91
+ for (const updateKey of pendingKeys) {
92
+ const record = records.get(updateKey);
93
+ if (record === void 0) {
94
+ pendingKeys.delete(updateKey);
95
+ continue;
96
+ }
97
+ if (record.lastSentAt !== 0 && now - record.lastSentAt < record.minMS) continue;
98
+ record.lastSentAt = now;
99
+ pendingKeys.delete(updateKey);
100
+ try {
101
+ record.updateFunction(record.value);
102
+ } catch (e) {
103
+ log(logLevels.error, "chillUpdater update failed", ["chillUpdate"], updateKey, e);
104
+ }
105
+ didSend = true;
106
+ }
107
+ return didSend;
108
+ };
109
+ const setUpdate = ({
110
+ updateKey,
111
+ updateFunction,
112
+ value,
113
+ minMS = 0
114
+ }) => {
115
+ const safeMinMS = minMS > 0 ? minMS : 0;
116
+ const existing = records.get(updateKey);
117
+ if (existing !== void 0) {
118
+ existing.updateFunction = updateFunction;
119
+ existing.value = value;
120
+ if (safeMinMS !== 0) existing.minMS = safeMinMS;
121
+ pendingKeys.add(updateKey);
122
+ return;
123
+ }
124
+ pendingKeys.add(updateKey);
125
+ records.set(updateKey, {
126
+ updateFunction,
127
+ value,
128
+ minMS: safeMinMS,
129
+ lastSentAt: 0
130
+ });
131
+ };
132
+ return {
133
+ check,
134
+ setUpdate
135
+ };
136
+ }
137
+
138
+ // src/chronoTrigger.ts
139
+ function createChronoTrigger() {
140
+ const scheduledTasks = [];
141
+ const taskIndexById = /* @__PURE__ */ new Map();
142
+ let nextTaskId = 1;
143
+ const maxTicks = 5;
144
+ const catchupWarningThrottleMs = 5e3;
145
+ let running = false;
146
+ let rafId = 0;
147
+ let fps = 0;
148
+ const fpsHist = createRingBuffer(100);
149
+ let lastFrameTime = 0;
150
+ const Start = () => {
151
+ if (running) return;
152
+ running = true;
153
+ const frame = (time) => {
154
+ if (!running) return;
155
+ let delta = 0;
156
+ if (lastFrameTime > 0) {
157
+ delta = time - lastFrameTime;
158
+ if (delta > 0) {
159
+ const newFps = Math.round(1e3 / Math.max(delta, 1));
160
+ fps = newFps !== 1e3 ? newFps : fps;
161
+ fpsHist.push(fps);
162
+ }
163
+ }
164
+ lastFrameTime = time;
165
+ for (let i = 0; i < scheduledTasks.length; i++) {
166
+ const task = scheduledTasks[i];
167
+ if (task.intervalMs <= 0) {
168
+ task.callback(delta);
169
+ continue;
170
+ }
171
+ task.accumulatorMs += delta;
172
+ if (task.accumulatorMs > task.intervalMs * maxTicks) {
173
+ task.accumulatorMs = task.intervalMs * maxTicks;
174
+ if (time - task.lastCatchupWarningMs >= catchupWarningThrottleMs) {
175
+ task.lastCatchupWarningMs = time;
176
+ log(
177
+ logLevels.warning,
178
+ `${task.callbackName} fell behind and attempted catch-up ticks, but catch-up was capped at ${maxTicks}.`,
179
+ ["chronoTrigger"]
180
+ );
181
+ }
182
+ }
183
+ const ticks = task.accumulatorMs / task.intervalMs | 0;
184
+ if (ticks <= 0) continue;
185
+ task.accumulatorMs -= ticks * task.intervalMs;
186
+ for (let t = 0; t < ticks; t++) {
187
+ task.callback(task.intervalMs);
188
+ }
189
+ }
190
+ rafId = requestAnimationFrame(frame);
191
+ };
192
+ rafId = requestAnimationFrame(frame);
193
+ };
194
+ const Stop = () => {
195
+ running = false;
196
+ lastFrameTime = 0;
197
+ if (rafId) {
198
+ cancelAnimationFrame(rafId);
199
+ rafId = 0;
200
+ }
201
+ };
202
+ const dispose = (id) => {
203
+ const index = taskIndexById.get(id);
204
+ if (index === void 0) return false;
205
+ const lastIndex = scheduledTasks.length - 1;
206
+ if (index !== lastIndex) {
207
+ const lastTask = scheduledTasks[lastIndex];
208
+ scheduledTasks[index] = lastTask;
209
+ taskIndexById.set(lastTask.id, index);
210
+ }
211
+ scheduledTasks.pop();
212
+ taskIndexById.delete(id);
213
+ return true;
214
+ };
215
+ const runAt = ({
216
+ name,
217
+ fpsTarget,
218
+ callback
219
+ }) => {
220
+ if (typeof callback !== "function") {
221
+ throw new Error("runAt requires a callback function.");
222
+ }
223
+ let intervalMs = 0;
224
+ if (fpsTarget !== void 0) {
225
+ if (!Number.isFinite(fpsTarget) || fpsTarget <= 0) {
226
+ throw new Error("fpsTarget must be a positive number when provided.");
227
+ }
228
+ intervalMs = 1e3 / fpsTarget;
229
+ }
230
+ const id = nextTaskId++;
231
+ const callbackName = name || callback.name || "anonymous";
232
+ const accumulatorMs = intervalMs > 0 ? intervalMs : 0;
233
+ scheduledTasks.push({
234
+ id,
235
+ callback,
236
+ callbackName,
237
+ intervalMs,
238
+ accumulatorMs,
239
+ lastCatchupWarningMs: -Infinity
240
+ });
241
+ taskIndexById.set(id, scheduledTasks.length - 1);
242
+ return id;
243
+ };
244
+ const CurrentFPS = () => fps;
245
+ const AverageFPS = () => Math.round(fpsHist.average());
246
+ return { Start, Stop, runAt, dispose, CurrentFPS, AverageFPS };
247
+ }
248
+ var Crono = createChronoTrigger();
249
+
250
+ // src/random.ts
251
+ function rng(low, high, decimals = null) {
252
+ const min = low < high ? low : high;
253
+ const max = low < high ? high : low;
254
+ const lowDecimals = (Math.abs(low).toString().split(".")[1] || "").length;
255
+ const highDecimals = (Math.abs(high).toString().split(".")[1] || "").length;
256
+ const derivedDecimals = Math.max(lowDecimals, highDecimals);
257
+ const precision = decimals !== null ? decimals : derivedDecimals;
258
+ const factor = 10 ** precision;
259
+ const randomValue = Math.random() * (max - min) + min;
260
+ return Math.round(randomValue * factor) / factor;
261
+ }
262
+ function clamp(value, low, high) {
263
+ return Math.max(low, Math.min(high, value));
264
+ }
265
+ function randomItem(collection) {
266
+ if (collection.length === 0) {
267
+ log(logLevels.error, "Cannot select a random item from an empty array.", ["randomItem"], collection);
268
+ }
269
+ return collection[Math.floor(rng(0, collection.length - 1))];
270
+ }
271
+ function getRndColor() {
272
+ const red = Math.round(Math.random() * 234 + 10);
273
+ const green = Math.round(Math.random() * 234 + 20);
274
+ const blue = Math.round(Math.random() * 234 + 20);
275
+ const color = "#" + red.toString(16) + green.toString(16) + blue.toString(16);
276
+ return color;
277
+ }
278
+ function getColorPair() {
279
+ const red = Math.round(Math.random() * 234 + 10);
280
+ const antiRed = Math.abs(red - 234) + 20;
281
+ const green = Math.round(Math.random() * 234 + 20);
282
+ const antiGreen = Math.abs(green - 234) + 20;
283
+ const blue = Math.round(Math.random() * 234 + 20);
284
+ const antiblue = Math.abs(blue - 234) + 20;
285
+ const color = "#" + red.toString(16) + green.toString(16) + blue.toString(16);
286
+ const antiColor = "#" + antiRed.toString(16) + antiGreen.toString(16) + antiblue.toString(16);
287
+ return { c1: color, c2: antiColor };
288
+ }
289
+ function colorFrmRange(c1, c2, percent) {
290
+ percent = percent / 100;
291
+ const c1R = parseInt(c1.slice(1, 3), 16);
292
+ const c1G = parseInt(c1.slice(3, 5), 16);
293
+ const c1B = parseInt(c1.slice(5, 7), 16);
294
+ const c2R = parseInt(c2.slice(1, 3), 16);
295
+ const c2G = parseInt(c2.slice(3, 5), 16);
296
+ const c2B = parseInt(c2.slice(5, 7), 16);
297
+ const rDif = c1R - c2R;
298
+ const gDif = c1G - c2G;
299
+ const bDif = c1B - c2B;
300
+ let red = Math.round(c1R - percent * rDif).toString(16);
301
+ let green = Math.round(c1G - percent * gDif).toString(16);
302
+ let blue = Math.round(c1B - percent * bDif).toString(16);
303
+ if (red.length < 2) {
304
+ red = "0" + red;
305
+ }
306
+ if (green.length < 2) {
307
+ green = "0" + green;
308
+ }
309
+ if (blue.length < 2) {
310
+ blue = "0" + blue;
311
+ }
312
+ const color = "#" + red + green + blue;
313
+ return color;
314
+ }
315
+ function getContrastingColor(hexColor) {
316
+ const normalizedHex = hexColor.replace(/^#/, "");
317
+ const r = parseInt(normalizedHex.slice(0, 2), 16);
318
+ const g = parseInt(normalizedHex.slice(2, 4), 16);
319
+ const b = parseInt(normalizedHex.slice(4, 6), 16);
320
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
321
+ return luminance > 0.5 ? "#000000" : "#FFFFFF";
322
+ }
323
+ export {
324
+ Crono,
325
+ blockKeywords,
326
+ clamp,
327
+ colorFrmRange,
328
+ createChillUpdater,
329
+ createChronoTrigger,
330
+ createRingBuffer,
331
+ currentLogLevel,
332
+ filterKeywords,
333
+ getColorPair,
334
+ getContrastingColor,
335
+ getRndColor,
336
+ log,
337
+ logLevels,
338
+ randomItem,
339
+ rng
340
+ };
341
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logging.ts","../src/ringBuffer.ts","../src/chillUpdate.ts","../src/chronoTrigger.ts","../src/random.ts"],"sourcesContent":["export const logLevels = {\n off: 0,\n error: 1,\n warning: 2,\n debug: 3,\n};\n\n// Mutable object so any module can update the level at runtime.\n// Off by default so production builds are quiet; set to logLevels.debug to enable.\nexport const currentLogLevel = { value: logLevels.off };\n\n// filterKeywords: when non-empty, only messages tagged with one of these pass through.\n// blockKeywords: messages tagged with any of these are always suppressed.\nexport const filterKeywords: string[] = [];\nexport const blockKeywords: string[] = [\"getRandomWord\", \"applyCooldowns\", \"calculateBoundingBox\", \"rollingAverage\"];\n\nexport function log(\n level: number = logLevels.debug,\n message: string,\n keywords: string[] = [],\n ...data: unknown[]\n ): void {\n if (!Object.values(logLevels).includes(level)) {\n console.error(`[LOG ERROR] Invalid log level: ${level}`);\n return;\n }\n\n if (currentLogLevel.value < level) return;\n\n if (blockKeywords.some(keyword => keywords.includes(keyword))) return;\n\n if (filterKeywords.length > 0 && !filterKeywords.some(keyword => keywords.includes(keyword))) return;\n\n // Deep copy prevents logged objects from mutating after the call.\n const copiedData = data.map(item => {\n try {\n return JSON.parse(JSON.stringify(item));\n } catch {\n return item;\n }\n });\n\n const method = level === logLevels.error ? \"error\"\n : level === logLevels.warning ? \"warn\"\n : \"log\";\n\n const timestamp = new Date().toISOString();\n const keywordInfo = keywords.length > 0 ? ` [Keywords: ${keywords.join(\", \")}]` : \"\";\n\n console[method](`[${timestamp}] ${message}\\n${keywordInfo}\\n`, ...copiedData);\n }\n","export interface RingBuffer {\n push(value: number): void\n average(): number\n sum(): number\n size(): number\n capacity(): number\n clear(): void\n}\n\nexport function createRingBuffer(capacity: number): RingBuffer {\n if (capacity <= 0) {\n throw new Error(\"RingBuffer capacity must be > 0\")\n }\n\n const buffer = new Float64Array(capacity)\n let head = 0 // points to oldest value\n let length = 0\n let total = 0\n\n return {\n push(value: number) {\n if (length < capacity) {\n // still filling\n buffer[(head + length) % capacity] = value\n total += value\n length++\n } else {\n // overwrite oldest\n const old = buffer[head]\n total -= old\n buffer[head] = value\n total += value\n head = (head + 1) % capacity\n }\n },\n\n average() {\n return length === 0 ? 0 : total / length\n },\n\n sum() {\n return total\n },\n\n size() {\n return length\n },\n\n capacity() {\n return capacity\n },\n\n clear() {\n head = 0\n length = 0\n total = 0\n }\n }\n}\n","import { log, logLevels } from \"./logging\";\n\nexport type ChillUpdateFn<TValue> = (value: TValue) => void;\n\nexport type ChillUpdaterSetParams<TValue> = {\n updateKey: string;\n updateFunction: ChillUpdateFn<TValue>;\n value: TValue;\n minMS?: number;\n};\n\nexport type ChillUpdater = {\n check: () => boolean;\n setUpdate: <TValue>(params: ChillUpdaterSetParams<TValue>) => void;\n};\n\ntype UpdateRecord = {\n updateFunction: ChillUpdateFn<unknown>;\n value: unknown;\n minMS: number;\n lastSentAt: number;\n};\n\nfunction createChillUpdater(): ChillUpdater {\n const records = new Map<string, UpdateRecord>();\n const pendingKeys = new Set<string>();\n const getNow = (): number => {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n };\n\n const check = (): boolean => {\n if (records.size === 0 || pendingKeys.size === 0) return false;\n\n const now = getNow();\n let didSend = false;\n\n for (const updateKey of pendingKeys) {\n const record = records.get(updateKey);\n if (record === undefined) {\n pendingKeys.delete(updateKey);\n continue;\n }\n if (record.lastSentAt !== 0 && now - record.lastSentAt < record.minMS) continue;\n\n record.lastSentAt = now;\n pendingKeys.delete(updateKey);\n try {\n record.updateFunction(record.value);\n }\n catch (e) {\n log(logLevels.error, \"chillUpdater update failed\", [\"chillUpdate\"], updateKey, e);\n }\n\n didSend = true;\n }\n\n return didSend;\n };\n\n const setUpdate = <TValue>({\n updateKey,\n updateFunction,\n value,\n minMS = 0,\n }: ChillUpdaterSetParams<TValue>): void => {\n const safeMinMS = minMS > 0 ? minMS : 0;\n const existing = records.get(updateKey);\n\n if (existing !== undefined) {\n existing.updateFunction = updateFunction as ChillUpdateFn<unknown>;\n existing.value = value;\n if (safeMinMS !== 0) existing.minMS = safeMinMS;\n pendingKeys.add(updateKey);\n return;\n }\n\n pendingKeys.add(updateKey);\n records.set(updateKey, {\n updateFunction: updateFunction as ChillUpdateFn<unknown>,\n value,\n minMS: safeMinMS,\n lastSentAt: 0\n });\n };\n\n return {\n check,\n setUpdate\n };\n}\n\nexport { createChillUpdater };\n","// ChronoTrigger Library (CT) using Factory Functions with FPS Tracking\nimport { log, logLevels } from './logging';\nimport { createRingBuffer } from './ringBuffer';\n\ninterface ScheduledTask {\n id: number;\n callback: (deltaMs: number) => void;\n callbackName: string;\n intervalMs: number;\n accumulatorMs: number;\n lastCatchupWarningMs: number;\n}\n\nexport interface ChronoTrigger {\n Start: () => void;\n Stop: () => void;\n runAt: (options: { name?: string; callback: (deltaMs: number) => void; fpsTarget?: number }) => number;\n dispose: (id: number) => boolean;\n CurrentFPS: () => number;\n AverageFPS: () => number;\n}\n\nexport function createChronoTrigger(): ChronoTrigger {\n const scheduledTasks: ScheduledTask[] = [];\n const taskIndexById = new Map<number, number>();\n let nextTaskId = 1;\n const maxTicks = 5;\n const catchupWarningThrottleMs = 5000;\n let running = false;\n let rafId = 0;\n let fps = 0; // Tracks the current running FPS\n const fpsHist = createRingBuffer(100);\n let lastFrameTime = 0;\n\n const Start = (): void => {\n if (running) return;\n running = true;\n const frame = (time: number): void => {\n if (!running) return;\n\n let delta = 0;\n if (lastFrameTime > 0) {\n delta = time - lastFrameTime;\n if (delta > 0) {\n // using Math.max to avoid divide by 0\n const newFps = Math.round(1000 / Math.max(delta, 1));\n fps = newFps !== 1000 ? newFps : fps;\n fpsHist.push(fps);\n }\n }\n lastFrameTime = time;\n\n for (let i = 0; i < scheduledTasks.length; i++) {\n const task = scheduledTasks[i];\n if (task.intervalMs <= 0) {\n task.callback(delta);\n continue;\n }\n\n task.accumulatorMs += delta;\n if (task.accumulatorMs > task.intervalMs * maxTicks) {\n task.accumulatorMs = task.intervalMs * maxTicks;\n if (time - task.lastCatchupWarningMs >= catchupWarningThrottleMs) {\n task.lastCatchupWarningMs = time;\n log(\n logLevels.warning,\n `${task.callbackName} fell behind and attempted catch-up ticks, but catch-up was capped at ${maxTicks}.`,\n [\"chronoTrigger\"],\n );\n }\n }\n\n const ticks = (task.accumulatorMs / task.intervalMs) | 0;\n if (ticks <= 0) continue;\n\n task.accumulatorMs -= ticks * task.intervalMs;\n for (let t = 0; t < ticks; t++) {\n task.callback(task.intervalMs);\n }\n }\n\n rafId = requestAnimationFrame(frame);\n };\n rafId = requestAnimationFrame(frame);\n };\n\n const Stop = (): void => {\n running = false;\n lastFrameTime = 0;\n if (rafId) {\n cancelAnimationFrame(rafId);\n rafId = 0;\n }\n };\n\n const dispose = (id: number): boolean => {\n const index = taskIndexById.get(id);\n if (index === undefined) return false;\n\n const lastIndex = scheduledTasks.length - 1;\n if (index !== lastIndex) {\n const lastTask = scheduledTasks[lastIndex];\n scheduledTasks[index] = lastTask;\n taskIndexById.set(lastTask.id, index);\n }\n\n scheduledTasks.pop();\n taskIndexById.delete(id);\n return true;\n };\n\n const runAt = ({\n name,\n fpsTarget,\n callback,\n }: {\n name?: string;\n callback: (deltaMs: number) => void;\n fpsTarget?: number;\n }): number => {\n if (typeof callback !== \"function\") {\n throw new Error(\"runAt requires a callback function.\");\n }\n\n let intervalMs = 0;\n if (fpsTarget !== undefined) {\n if (!Number.isFinite(fpsTarget) || fpsTarget <= 0) {\n throw new Error(\"fpsTarget must be a positive number when provided.\");\n }\n\n intervalMs = 1000 / fpsTarget;\n }\n\n const id = nextTaskId++;\n const callbackName = name || callback.name || \"anonymous\";\n const accumulatorMs = intervalMs > 0 ? intervalMs : 0;\n scheduledTasks.push({\n id,\n callback,\n callbackName,\n intervalMs,\n accumulatorMs,\n lastCatchupWarningMs: -Infinity,\n });\n taskIndexById.set(id, scheduledTasks.length - 1);\n return id;\n };\n\n const CurrentFPS = (): number => fps;\n const AverageFPS = (): number => Math.round(fpsHist.average());\n\n return { Start, Stop, runAt, dispose, CurrentFPS, AverageFPS };\n}\n\nexport const Crono = createChronoTrigger();\n","import { log, logLevels } from \"./logging\";\n\n/**\n * Generates a random number within the specified range, supporting precision.\n *\n * - If `decimals` is provided, the result is rounded to that many decimal places.\n * - If `decimals` is not provided, the precision is derived from the decimal places of the `low` and `high` inputs.\n *\n * @param {number} low - The minimum value (inclusive) of the range.\n * @param {number} high - The maximum value (exclusive) of the range.\n * @param {number | null} [decimals=null] - The number of decimal places to round to. If not provided, precision is derived automatically.\n * @returns {number} - A random number between `low` and `high`, rounded to the specified or derived precision.\n *\n * @example\n * // Example 1: Whole number output\n * rng(1, 10); // Could return 7\n *\n * @example\n * // Example 2: Fixed decimal places\n * rng(1.5, 3.5, 2); // Could return 2.78\n *\n * @example\n * // Example 3: Automatically derived precision\n * rng(0.001, 0.1); // Could return 0.023 (3 decimal places derived)\n */\n\nexport function rng(low: number, high: number, decimals: number | null = null): number {\n const min = low < high ? low : high;\n const max = low < high ? high : low;\n const lowDecimals = (Math.abs(low).toString().split(\".\")[1] || \"\").length;\n const highDecimals = (Math.abs(high).toString().split(\".\")[1] || \"\").length;\n const derivedDecimals = Math.max(lowDecimals, highDecimals); // Derive max precision\n const precision = decimals !== null ? decimals : derivedDecimals;\n const factor = 10 ** precision;\n\n const randomValue = Math.random() * (max - min) + min;\n\n return Math.round(randomValue * factor) / factor;\n}\n\nexport function clamp(value: number, low: number, high: number) {\n return Math.max(low, Math.min(high, value));\n}\n\n/**\n * Selects a random item from an array of a specific type.\n *\n * Utilizes the `rng` function to generate a random index within the bounds of the array.\n *\n * @template T - The type of elements in the array.\n * @param {T[]} collection - The array to select a random item from.\n * @returns {T} - A randomly selected item from the array.\n *\n * @throws {Error} Throws an error if the collection is empty.\n *\n * @example\n * // Example 1: Random item from a number array\n * const numbers = [1, 2, 3, 4, 5];\n * const randomNum = randomItem(numbers); // TypeScript infers: number\n *\n * @example\n * // Example 2: Random item from a string array\n * const fruits = [\"apple\", \"banana\", \"cherry\"];\n * const randomFruit = randomItem(fruits); // TypeScript infers: string\n */\nexport function randomItem<T>(collection: T[]): T {\n if (collection.length === 0) {\n log(logLevels.error, \"Cannot select a random item from an empty array.\", ['randomItem'], collection);\n }\n return collection[Math.floor(rng(0, collection.length-1))];\n}\n\nexport function getRndColor(){\n\n const red = Math.round(Math.random() * (234)+10);\n\tconst green= Math.round(Math.random() * (234)+20);\n\tconst blue= Math.round(Math.random() * (234)+20);\n\n\tconst color =\"#\"+red.toString(16)+green.toString(16)+blue.toString(16);\n\n\treturn color;\n}\n\nexport function getColorPair() {\n\n const red = Math.round(Math.random() * (234) + 10);\n const antiRed = Math.abs(red - 234) + 20;\n const green = Math.round(Math.random() * (234) + 20);\n const antiGreen = Math.abs(green - 234) + 20;\n const blue = Math.round(Math.random() * (234) + 20);\n const antiblue = Math.abs(blue - 234) + 20;\n\n const color = \"#\" + red.toString(16) + green.toString(16) + blue.toString(16);\n const antiColor = \"#\" + antiRed.toString(16) + antiGreen.toString(16) + antiblue.toString(16);\n\n return { c1: color, c2: antiColor };\n }\n\n export function colorFrmRange(c1: string, c2: string, percent: number) {\n percent = percent / 100;\n\n const c1R = parseInt(c1.slice(1, 3), 16);\n const c1G = parseInt(c1.slice(3, 5), 16);\n const c1B = parseInt(c1.slice(5, 7), 16);\n\n const c2R = parseInt(c2.slice(1, 3), 16);\n const c2G = parseInt(c2.slice(3, 5), 16);\n const c2B = parseInt(c2.slice(5, 7), 16);\n\n const rDif = c1R - c2R;\n const gDif = c1G - c2G;\n const bDif = c1B - c2B;\n\n let red = Math.round(c1R - percent * rDif).toString(16);\n let green = Math.round(c1G - percent * gDif).toString(16);\n let blue = Math.round(c1B - percent * bDif).toString(16);\n\n if (red.length < 2) { red = \"0\" + red; }\n if (green.length < 2) { green = \"0\" + green; }\n if (blue.length < 2) { blue = \"0\" + blue; }\n\n const color = \"#\" + red + green + blue;\n\n return color;\n }\n\n /**\n * Determines whether a hex color is light or dark and returns a contrasting color (black or white).\n * @param hexColor - The hex color string (e.g., \"#FFFFFF\" or \"FFFFFF\").\n * @returns A string representing the contrasting color (\"#000000\" for black or \"#FFFFFF\" for white).\n */\nexport function getContrastingColor(hexColor: string): string {\n // Remove the '#' if present\n const normalizedHex = hexColor.replace(/^#/, \"\");\n\n // Parse the RGB values\n const r = parseInt(normalizedHex.slice(0, 2), 16);\n const g = parseInt(normalizedHex.slice(2, 4), 16);\n const b = parseInt(normalizedHex.slice(4, 6), 16);\n\n // Calculate the relative luminance (standard formula)\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n\n // Return black for light backgrounds, white for dark backgrounds\n return luminance > 0.5 ? \"#000000\" : \"#FFFFFF\";\n }\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@kikorin/util",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^20",
20
+ "tsup": "^8",
21
+ "typescript": "^5",
22
+ "vitest": "^3.2.0"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "test": "vitest run",
27
+ "typecheck": "tsc --noEmit"
28
+ }
29
+ }