@pistonite/pure 0.26.2 → 0.26.4
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/package.json +6 -6
- package/src/log/index.ts +134 -36
- package/src/memory/async_erc.ts +179 -0
- package/src/memory/idgen.test.ts +26 -0
- package/src/memory/idgen.ts +40 -0
- package/src/memory/index.ts +1 -0
- package/src/pref/dark.ts +1 -1
- package/src/pref/index.ts +1 -1
- package/src/pref/locale.ts +1 -1
- package/src/sync/batch.ts +2 -2
- /package/src/pref/{injectStyle.ts → inject_style.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pistonite/pure",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pure TypeScript libraries for my projects",
|
|
6
6
|
"homepage": "https://github.com/Pistonite/pure",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"url": "git+https://github.com/Pistonite/pure.git",
|
|
27
27
|
"directory": "packages/pure"
|
|
28
28
|
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"denque": "2.1.0"
|
|
31
|
-
},
|
|
32
29
|
"devDependencies": {
|
|
33
|
-
"vitest": "^3.
|
|
34
|
-
"mono-dev": "0.
|
|
30
|
+
"vitest": "^3.2.4",
|
|
31
|
+
"mono-dev": "0.2.2"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"denque": "^2.1.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
package/src/log/index.ts
CHANGED
|
@@ -1,56 +1,154 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Client side log util
|
|
3
3
|
*
|
|
4
|
+
* This is rather simple logging stuff with the primary focus
|
|
5
|
+
* being easy to debug.
|
|
6
|
+
*
|
|
7
|
+
* Use {@link logger} to create a logger with a name and a color,
|
|
8
|
+
* then use one of the {@link LoggerFactory} methods to setup the logging level.
|
|
9
|
+
*
|
|
10
|
+
* There are 3 levels of logging:
|
|
11
|
+
* 1. (Default) Warnings and Errors only
|
|
12
|
+
* 2. Also log info messages
|
|
13
|
+
* 3. Also log debug messages.
|
|
14
|
+
*
|
|
15
|
+
* Each logger can turn on info and debug logging separately, or it can
|
|
16
|
+
* be turned on at the global level for debugging.
|
|
17
|
+
*
|
|
18
|
+
* You can also turn off each logger individually or at global level for debugging.
|
|
19
|
+
*
|
|
20
|
+
* Due to the nature of JS, all logging calls, even when turned off, will incur
|
|
21
|
+
* some small runtime overhead. While we could remove debug calls
|
|
22
|
+
* for release build, that's currently not done (and it would require
|
|
23
|
+
* bundler to inline the call to remove the call completely, which might
|
|
24
|
+
* not be the case)
|
|
25
|
+
*
|
|
4
26
|
* @module
|
|
5
27
|
*/
|
|
6
28
|
|
|
7
|
-
import Denque from "denque";
|
|
8
29
|
import { errstr } from "../result/index.ts";
|
|
9
30
|
|
|
10
|
-
const
|
|
31
|
+
export const LogLevel = {
|
|
32
|
+
Off: 0,
|
|
33
|
+
High: 1,
|
|
34
|
+
Info: 2,
|
|
35
|
+
Debug: 3,
|
|
36
|
+
} as const;
|
|
37
|
+
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
|
|
11
38
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
39
|
+
let globalLevel: LogLevel = LogLevel.High;
|
|
40
|
+
/**
|
|
41
|
+
* Suppress ALL logging.
|
|
42
|
+
*
|
|
43
|
+
* This overrides logger-level settings
|
|
44
|
+
*/
|
|
45
|
+
export const globalLogOff = () => (globalLevel = 0);
|
|
46
|
+
/**
|
|
47
|
+
* Enable +info logging for ALL loggers
|
|
48
|
+
*
|
|
49
|
+
* This overrides logger-level settings
|
|
50
|
+
*/
|
|
51
|
+
export const globalLogInfo = () => (globalLevel = 2);
|
|
52
|
+
/**
|
|
53
|
+
* Enable +debug logging for ALL loggers
|
|
54
|
+
*
|
|
55
|
+
* This overrides logger-level settings
|
|
56
|
+
*/
|
|
57
|
+
export const globalLogDebug = () => (globalLevel = 3);
|
|
20
58
|
|
|
21
|
-
/**
|
|
22
|
-
export
|
|
23
|
-
return
|
|
24
|
-
|
|
59
|
+
/** Create a logger creator. Use the factory methods to finish making the logger */
|
|
60
|
+
export const logger = (name: string, color?: string): LoggerFactory => {
|
|
61
|
+
return {
|
|
62
|
+
default: () => new LoggerImpl(name, color, LogLevel.High),
|
|
63
|
+
debug: () => new LoggerImpl(name, color, LogLevel.Debug),
|
|
64
|
+
info: () => new LoggerImpl(name, color, LogLevel.Info),
|
|
65
|
+
off: () => new LoggerImpl(name, color, LogLevel.Off),
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type LoggerFactory = {
|
|
70
|
+
/** Standard important logger (warning and errors) */
|
|
71
|
+
default(): Logger;
|
|
72
|
+
/** Enable +info +debug logging for this logger */
|
|
73
|
+
debug(): Logger;
|
|
74
|
+
/** Enable +info logging for this logger */
|
|
75
|
+
info(): Logger;
|
|
76
|
+
/** Stop all logging, including warn and error */
|
|
77
|
+
off(): Logger;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type Logger = {
|
|
81
|
+
/** Log a debug message */
|
|
82
|
+
debug(obj: unknown): void;
|
|
83
|
+
/** Log an info message */
|
|
84
|
+
info(obj: unknown): void;
|
|
85
|
+
/** Log a warning message */
|
|
86
|
+
warn(obj: unknown): void;
|
|
87
|
+
/** Log an error message */
|
|
88
|
+
error(obj: unknown): void;
|
|
89
|
+
};
|
|
25
90
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
91
|
+
class LoggerImpl implements Logger {
|
|
92
|
+
name: string;
|
|
93
|
+
color: string | undefined;
|
|
94
|
+
level: LogLevel;
|
|
30
95
|
|
|
31
|
-
constructor(
|
|
32
|
-
this.
|
|
96
|
+
constructor(name: string, color: string | undefined, level: LogLevel) {
|
|
97
|
+
this.name = name;
|
|
98
|
+
this.color =
|
|
99
|
+
"padding:0 3x;color:white" + (color ? `;background:${color}` : "");
|
|
100
|
+
this.level = level;
|
|
33
101
|
}
|
|
34
102
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
103
|
+
debug(obj: unknown) {
|
|
104
|
+
if (globalLevel !== LogLevel.Debug && this.level !== LogLevel.Debug) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
console.log(
|
|
108
|
+
`%cDEBUG%c${this.name}%c ${obj}`,
|
|
109
|
+
"background:gray;color:white;padding:0 3px",
|
|
110
|
+
this.color,
|
|
111
|
+
"color:inherit;background:inherit",
|
|
112
|
+
);
|
|
40
113
|
}
|
|
41
114
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
115
|
+
info(obj: unknown) {
|
|
116
|
+
if (globalLevel < LogLevel.Info && this.level < LogLevel.Info) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(
|
|
120
|
+
`%cINFO%c${this.name}%c ${obj}`,
|
|
121
|
+
"background:green;color:white;padding:0 3px",
|
|
122
|
+
this.color,
|
|
123
|
+
"color:inherit;background:inherit",
|
|
124
|
+
);
|
|
47
125
|
}
|
|
48
126
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
127
|
+
warn(obj: unknown) {
|
|
128
|
+
if (globalLevel < LogLevel.High || this.level < LogLevel.High) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.warn(
|
|
132
|
+
`%cWARN%c${this.name}%c ${obj}`,
|
|
133
|
+
"background:orange;color:white;padding:0 3px",
|
|
134
|
+
this.color,
|
|
135
|
+
"color:inherit;background:inherit",
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
error(obj: unknown) {
|
|
140
|
+
if (globalLevel < LogLevel.High || this.level < LogLevel.High) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const msg = errstr(obj);
|
|
144
|
+
console.error(
|
|
145
|
+
`%cERROR%c${this.name}%c ${msg}`,
|
|
146
|
+
"background:orange;color:white;padding:0 3px",
|
|
147
|
+
this.color,
|
|
148
|
+
"color:inherit;background:inherit",
|
|
149
|
+
);
|
|
150
|
+
if (msg !== obj) {
|
|
151
|
+
console.error(obj);
|
|
152
|
+
}
|
|
55
153
|
}
|
|
56
154
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A holder for an externally ref-counted object, where free and addref
|
|
3
|
+
* operations are asynchronous.
|
|
4
|
+
*
|
|
5
|
+
* See {@link makeErcType} for how to use
|
|
6
|
+
*/
|
|
7
|
+
export type AsyncErc<TName, TRepr = number> = {
|
|
8
|
+
readonly type: TName;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Underlying object representation.
|
|
12
|
+
*
|
|
13
|
+
* The repr should not be undefinable. undefined means nullptr
|
|
14
|
+
*/
|
|
15
|
+
readonly value: TRepr | undefined;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Free the underlying object.
|
|
19
|
+
*
|
|
20
|
+
* All weak references will be immediately invalidated, and this Erc becomes
|
|
21
|
+
* empty. Awaiting on the promise will ensure that the object is freed externally
|
|
22
|
+
*/
|
|
23
|
+
free: () => Promise<void>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Assign a new value to this Erc.
|
|
27
|
+
*
|
|
28
|
+
* The old value will be freed, and all weak references will be invalidated.
|
|
29
|
+
*/
|
|
30
|
+
assign: (value: TRepr | undefined) => Promise<void>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Take the inner value without freeing it.
|
|
34
|
+
*
|
|
35
|
+
* All weak references will be invalidated, and this Erc will become
|
|
36
|
+
* empty
|
|
37
|
+
*/
|
|
38
|
+
take: () => TRepr | undefined;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a weak reference to the inner value.
|
|
42
|
+
*
|
|
43
|
+
* When this Erc is freed, all weak references will be invalidated.
|
|
44
|
+
*/
|
|
45
|
+
getWeak: () => AsyncErcRef<TName, TRepr>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a strong reference to the inner value, essentially
|
|
49
|
+
* incrementing the ref count.
|
|
50
|
+
*/
|
|
51
|
+
getStrong: () => Promise<AsyncErc<TName, TRepr>>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Weak reference to an externally ref-counted object.
|
|
56
|
+
*
|
|
57
|
+
* See {@link makeErcType} for how to use
|
|
58
|
+
*/
|
|
59
|
+
export type AsyncErcRef<TName, TRepr = number> = {
|
|
60
|
+
readonly type: TName;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The underlying object representation.
|
|
64
|
+
*
|
|
65
|
+
* This may become undefined across async calls if the weak reference
|
|
66
|
+
* is invalidated
|
|
67
|
+
*/
|
|
68
|
+
readonly value: TRepr | undefined;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a strong reference to the inner value, essentially
|
|
72
|
+
* incrementing the ref count.
|
|
73
|
+
*/
|
|
74
|
+
getStrong: () => Promise<AsyncErc<TName, TRepr>>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type AsyncErcRefType<T> =
|
|
78
|
+
T extends AsyncErc<infer TName, infer TRepr>
|
|
79
|
+
? AsyncErcRef<TName, TRepr>
|
|
80
|
+
: never;
|
|
81
|
+
|
|
82
|
+
export type AsyncErcTypeConstructor<TName, TRepr> = {
|
|
83
|
+
/**
|
|
84
|
+
* A marker value for the underlying object type.
|
|
85
|
+
*
|
|
86
|
+
* This is commonly a string literal or a symbol.
|
|
87
|
+
*/
|
|
88
|
+
marker: TName;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The function to free the underlying object.
|
|
92
|
+
*/
|
|
93
|
+
free: (value: TRepr) => Promise<void> | void;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Given a value, increase the ref count and return the new reference.
|
|
97
|
+
* The returned representation should be a different value if double indirection
|
|
98
|
+
* is used (each value is a pointer to the smart pointer), or the same value
|
|
99
|
+
* if single indirection is used (the value is pointing to the object itself).
|
|
100
|
+
*/
|
|
101
|
+
addRef: (value: TRepr) => Promise<TRepr> | TRepr;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** See {@link makeErcType} */
|
|
105
|
+
export const makeAsyncErcType = <TName, TRepr>({
|
|
106
|
+
marker,
|
|
107
|
+
free,
|
|
108
|
+
addRef,
|
|
109
|
+
}: AsyncErcTypeConstructor<TName, TRepr>): ((
|
|
110
|
+
value: TRepr | undefined,
|
|
111
|
+
) => AsyncErc<TName, TRepr>) => {
|
|
112
|
+
const createStrongRef = (
|
|
113
|
+
value: TRepr | undefined,
|
|
114
|
+
): AsyncErc<TName, TRepr> => {
|
|
115
|
+
let weakRef:
|
|
116
|
+
| (AsyncErcRef<TName, TRepr> & { invalidate: () => void })
|
|
117
|
+
| undefined = undefined;
|
|
118
|
+
const invalidateWeakRef = () => {
|
|
119
|
+
if (!weakRef) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const oldWeakRef = weakRef;
|
|
123
|
+
weakRef = undefined;
|
|
124
|
+
oldWeakRef.invalidate();
|
|
125
|
+
};
|
|
126
|
+
const createWeakRef = (initialValue: TRepr | undefined) => {
|
|
127
|
+
const weak = {
|
|
128
|
+
type: marker,
|
|
129
|
+
value: initialValue,
|
|
130
|
+
invalidate: () => {
|
|
131
|
+
weak.value = undefined;
|
|
132
|
+
},
|
|
133
|
+
getStrong: async () => {
|
|
134
|
+
if (weak.value === undefined) {
|
|
135
|
+
return createStrongRef(undefined);
|
|
136
|
+
}
|
|
137
|
+
return createStrongRef(await addRef(weak.value));
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
return weak;
|
|
141
|
+
};
|
|
142
|
+
const erc = {
|
|
143
|
+
type: marker,
|
|
144
|
+
value,
|
|
145
|
+
free: async () => {
|
|
146
|
+
if (erc.value !== undefined) {
|
|
147
|
+
invalidateWeakRef();
|
|
148
|
+
const freePromise = free(erc.value);
|
|
149
|
+
erc.value = undefined;
|
|
150
|
+
await freePromise;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
assign: async (newValue: TRepr | undefined) => {
|
|
154
|
+
await erc.free();
|
|
155
|
+
erc.value = newValue;
|
|
156
|
+
},
|
|
157
|
+
take: () => {
|
|
158
|
+
invalidateWeakRef();
|
|
159
|
+
const oldValue = erc.value;
|
|
160
|
+
erc.value = undefined;
|
|
161
|
+
return oldValue;
|
|
162
|
+
},
|
|
163
|
+
getWeak: () => {
|
|
164
|
+
if (!weakRef) {
|
|
165
|
+
weakRef = createWeakRef(erc.value);
|
|
166
|
+
}
|
|
167
|
+
return weakRef;
|
|
168
|
+
},
|
|
169
|
+
getStrong: async () => {
|
|
170
|
+
if (erc.value === undefined) {
|
|
171
|
+
return createStrongRef(undefined);
|
|
172
|
+
}
|
|
173
|
+
return createStrongRef(await addRef(erc.value));
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
return erc;
|
|
177
|
+
};
|
|
178
|
+
return createStrongRef;
|
|
179
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { idgen, safeidgen } from "./idgen.ts";
|
|
4
|
+
|
|
5
|
+
describe("idgen", () => {
|
|
6
|
+
it("idgen generates ids", () => {
|
|
7
|
+
const idgen1 = idgen();
|
|
8
|
+
const idgen2 = idgen();
|
|
9
|
+
expect(idgen1()).toBe(2);
|
|
10
|
+
expect(idgen2()).toBe(2);
|
|
11
|
+
expect(idgen1()).toBe(3);
|
|
12
|
+
expect(idgen2()).toBe(3);
|
|
13
|
+
expect(idgen1()).toBe(4);
|
|
14
|
+
expect(idgen2()).toBe(4);
|
|
15
|
+
});
|
|
16
|
+
it("safeidgen wraps", () => {
|
|
17
|
+
const x = safeidgen(5);
|
|
18
|
+
expect(x()).toBe(2);
|
|
19
|
+
expect(x()).toBe(3);
|
|
20
|
+
expect(x()).toBe(4);
|
|
21
|
+
expect(x()).toBe(1);
|
|
22
|
+
expect(x()).toBe(2);
|
|
23
|
+
expect(x()).toBe(3);
|
|
24
|
+
expect(x()).toBe(4);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return an id generator that will generate ids in order
|
|
3
|
+
* from 1 to n, wrapping around to 1 when it's n
|
|
4
|
+
*/
|
|
5
|
+
export const safeidgen = (n: number): (() => number) => {
|
|
6
|
+
let x = 1;
|
|
7
|
+
return () => {
|
|
8
|
+
x += 1;
|
|
9
|
+
if (x === n) {
|
|
10
|
+
x = 1;
|
|
11
|
+
}
|
|
12
|
+
return x;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Return an id generator that returns bigint,
|
|
18
|
+
* starting from 1n, and will always return a new bigint
|
|
19
|
+
*/
|
|
20
|
+
export const bidgen = (): (() => bigint) => {
|
|
21
|
+
let x = 1n;
|
|
22
|
+
return () => {
|
|
23
|
+
x += 1n;
|
|
24
|
+
return x;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns an id generator that returns number staring from 1
|
|
30
|
+
* and always increasing. The number could become inaccurate
|
|
31
|
+
* (not integer) when exceeding Number.MAX_SAFE_INTEGER (which
|
|
32
|
+
* is 2^53 - 1
|
|
33
|
+
*/
|
|
34
|
+
export const idgen = (): (() => number) => {
|
|
35
|
+
let x = 1;
|
|
36
|
+
return () => {
|
|
37
|
+
x += 1;
|
|
38
|
+
return x;
|
|
39
|
+
};
|
|
40
|
+
};
|
package/src/memory/index.ts
CHANGED
package/src/pref/dark.ts
CHANGED
package/src/pref/index.ts
CHANGED
package/src/pref/locale.ts
CHANGED
|
@@ -189,7 +189,7 @@ export const setLocale = (newLocale: string): boolean => {
|
|
|
189
189
|
return true;
|
|
190
190
|
}
|
|
191
191
|
settingLocale = supported;
|
|
192
|
-
onBeforeChangeHook(supported).then((result) => {
|
|
192
|
+
void onBeforeChangeHook(supported).then((result) => {
|
|
193
193
|
if (result.err) {
|
|
194
194
|
return;
|
|
195
195
|
}
|
package/src/sync/batch.ts
CHANGED
|
@@ -214,7 +214,7 @@ class BatchImpl<TFn extends AnyFn> {
|
|
|
214
214
|
let done = this.disregardExecutionTime;
|
|
215
215
|
setTimeout(() => {
|
|
216
216
|
if (done) {
|
|
217
|
-
this.scheduleNext();
|
|
217
|
+
void this.scheduleNext();
|
|
218
218
|
} else {
|
|
219
219
|
done = true;
|
|
220
220
|
}
|
|
@@ -225,7 +225,7 @@ class BatchImpl<TFn extends AnyFn> {
|
|
|
225
225
|
if (!this.disregardExecutionTime) {
|
|
226
226
|
if (done) {
|
|
227
227
|
// interval already passed, we need to call it
|
|
228
|
-
this.scheduleNext();
|
|
228
|
+
void this.scheduleNext();
|
|
229
229
|
} else {
|
|
230
230
|
done = true;
|
|
231
231
|
}
|
|
File without changes
|