@pistonite/pure 0.27.1 → 0.29.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.
Files changed (60) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +23 -4
  3. package/dist/log/index.js +57 -0
  4. package/dist/log/index.js.map +1 -0
  5. package/dist/memory/index.js +92 -0
  6. package/dist/memory/index.js.map +1 -0
  7. package/dist/result/index.js +29 -0
  8. package/dist/result/index.js.map +1 -0
  9. package/dist/sync/index.js +252 -0
  10. package/dist/sync/index.js.map +1 -0
  11. package/package.json +22 -13
  12. package/src/env.d.ts +1 -0
  13. package/src/log/index.ts +36 -11
  14. package/src/log/logger.ts +93 -115
  15. package/src/memory/cell.ts +21 -11
  16. package/src/memory/emp.ts +34 -23
  17. package/src/memory/idgen.test.ts +1 -1
  18. package/src/memory/index.ts +1 -4
  19. package/src/memory/persist.ts +9 -12
  20. package/src/result/index.ts +12 -4
  21. package/src/sync/batch.test.ts +1 -1
  22. package/src/sync/batch.ts +12 -17
  23. package/src/sync/capture.ts +2 -0
  24. package/src/sync/debounce.test.ts +1 -1
  25. package/src/sync/debounce.ts +12 -15
  26. package/src/sync/index.ts +2 -3
  27. package/src/sync/latest.test.ts +1 -1
  28. package/src/sync/latest.ts +19 -16
  29. package/src/sync/once.test.ts +1 -1
  30. package/src/sync/once.ts +13 -8
  31. package/src/sync/serial.test.ts +1 -1
  32. package/src/sync/serial.ts +14 -12
  33. package/src/sync/util.ts +2 -2
  34. package/src/fs/FsError.ts +0 -55
  35. package/src/fs/FsFile.ts +0 -67
  36. package/src/fs/FsFileImpl.ts +0 -219
  37. package/src/fs/FsFileMgr.ts +0 -29
  38. package/src/fs/FsFileStandalone.ts +0 -21
  39. package/src/fs/FsFileStandaloneImplFileAPI.ts +0 -54
  40. package/src/fs/FsFileStandaloneImplHandleAPI.ts +0 -147
  41. package/src/fs/FsFileSystem.ts +0 -71
  42. package/src/fs/FsFileSystemInternal.ts +0 -30
  43. package/src/fs/FsImplEntryAPI.ts +0 -149
  44. package/src/fs/FsImplFileAPI.ts +0 -116
  45. package/src/fs/FsImplHandleAPI.ts +0 -199
  46. package/src/fs/FsOpen.ts +0 -271
  47. package/src/fs/FsOpenFile.ts +0 -256
  48. package/src/fs/FsPath.ts +0 -137
  49. package/src/fs/FsSave.ts +0 -216
  50. package/src/fs/FsSupportStatus.ts +0 -87
  51. package/src/fs/index.ts +0 -123
  52. package/src/log/internal.ts +0 -14
  53. package/src/memory/async_erc.ts +0 -186
  54. package/src/memory/erc.test.ts +0 -258
  55. package/src/memory/erc.ts +0 -320
  56. package/src/pref/dark.ts +0 -151
  57. package/src/pref/device.ts +0 -118
  58. package/src/pref/index.ts +0 -13
  59. package/src/pref/inject_style.ts +0 -22
  60. package/src/pref/locale.ts +0 -296
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pistonite/pure",
3
- "version": "0.27.1",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "description": "Pure TypeScript libraries for my projects",
6
6
  "homepage": "https://github.com/Pistonite/pure",
@@ -10,25 +10,34 @@
10
10
  "license": "MIT",
11
11
  "author": "Pistonight <pistonknight@outlook.com>",
12
12
  "files": [
13
- "src/**/*",
14
- "!src/**/*.test.ts"
13
+ "dist/**/*",
14
+ "src/**/*"
15
15
  ],
16
16
  "exports": {
17
- "./fs": "./src/fs/index.ts",
18
- "./log": "./src/log/index.ts",
19
- "./memory": "./src/memory/index.ts",
20
- "./result": "./src/result/index.ts",
21
- "./sync": "./src/sync/index.ts",
22
- "./pref": "./src/pref/index.ts"
17
+ "./log": {
18
+ "import": "./dist/log/index.js",
19
+ "types": "./dist/_dts_/src/log/index.d.ts"
20
+ },
21
+ "./memory": {
22
+ "import": "./dist/memory/index.js",
23
+ "types": "./dist/_dts_/src/memory/index.d.ts"
24
+ },
25
+ "./result": {
26
+ "import": "./dist/result/index.js",
27
+ "types": "./dist/_dts_/src/result/index.d.ts"
28
+ },
29
+ "./sync": {
30
+ "import": "./dist/sync/index.js",
31
+ "types": "./dist/_dts_/src/sync/index.d.ts"
32
+ }
23
33
  },
24
34
  "repository": {
25
35
  "type": "git",
26
- "url": "git+https://github.com/Pistonite/pure.git",
27
- "directory": "packages/pure"
36
+ "url": "git+https://github.com/Pistonite/pure.git"
28
37
  },
29
38
  "devDependencies": {
30
- "vitest": "^3.2.4",
31
- "mono-dev": "0.2.5"
39
+ "eslint": "^9",
40
+ "mono-dev": "link:./mono-dev"
32
41
  },
33
42
  "dependencies": {
34
43
  "denque": "^2.1.0"
package/src/env.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  /// <reference lib="dom" />
2
+ /// <reference types="mono-dev/vitest-types" />
package/src/log/index.ts CHANGED
@@ -1,11 +1,36 @@
1
- export {
2
- globalLogOff,
3
- globalLogInfo,
4
- globalLogDebug,
5
- logger,
6
- type LoggerFactory,
7
- type Logger,
8
- resettableLogger,
9
- type ResettableLogger,
10
- } from "./logger.ts";
11
- export { internalLogOff, internalLogDebug, internalLogInfo } from "./internal.ts";
1
+ /**
2
+ * Client side log util
3
+ *
4
+ * This is rather simple logging stuff with the primary focus
5
+ * being easy-to-debug, instead of optimized for bundle size or performance.
6
+ *
7
+ * Because this library doesn't have global states, there is no "global" logger
8
+ * or any global logger settings. Instead, each library or component of the
9
+ * application can create their own instance of the logger, which stores
10
+ * settings like the name, color and level of that logger.
11
+ *
12
+ * ```typescript
13
+ * import { logger } from "@pistonite/pure";
14
+ *
15
+ * export const myLogger = logger("my-library", {
16
+ * color: "#ff8800", // any CSS color (note that styling only works in browser)
17
+ * });
18
+ * ```
19
+ *
20
+ * It's recommended that a library exports the logger object
21
+ * so downstream app can modify the logging level if needed for debugging.
22
+ * ```typescript
23
+ * import { myLogger } from "my-library";
24
+ *
25
+ * myLogger.setLevel("debug");
26
+ * ```
27
+ *
28
+ * Due to the nature of JS, all logging calls, even when turned off, will incur
29
+ * some small runtime overhead. While we could remove debug calls
30
+ * for release build, that's currently not done (and it would require
31
+ * bundler to inline the call to remove the call completely, which might
32
+ * not be the case)
33
+ *
34
+ * @module
35
+ */
36
+ export { type LogLevelStr, type LoggerConstructor, type Logger, logger } from "./logger.ts";
package/src/log/logger.ts CHANGED
@@ -1,106 +1,39 @@
1
- /**
2
- * Client side log util
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
- *
26
- * @module
27
- */
28
-
29
1
  import { errstr } from "../result/index.ts";
30
2
 
31
- export const LogLevel = { Off: 0, High: 1, Info: 2, Debug: 3 } as const;
32
- export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
33
-
34
- let globalLevel: LogLevel = LogLevel.High;
35
- /**
36
- * Suppress ALL logging.
37
- *
38
- * This overrides logger-level settings
39
- */
40
- export const globalLogOff = () => {
41
- globalLevel = LogLevel.Off;
42
- };
43
3
  /**
44
- * Enable +info logging for ALL loggers
4
+ * String-enum for logging levels
45
5
  *
46
- * This overrides logger-level settings
6
+ * off - no logging at all
7
+ * default - warning and errors only
8
+ * info - warning, errors, info
9
+ * debug - warning, errors, info, debug
47
10
  */
48
- export const globalLogInfo = () => {
49
- globalLevel = LogLevel.Info;
50
- };
51
- /**
52
- * Enable +debug logging for ALL loggers
53
- *
54
- * This overrides logger-level settings
55
- */
56
- export const globalLogDebug = () => {
57
- globalLevel = LogLevel.Debug;
58
- };
59
-
60
- /** Create a logger creator. Use the factory methods to finish making the logger */
61
- export const logger = (name: string, color?: string): LoggerFactory => {
62
- return {
63
- default: () => new LoggerImpl(name, color, LogLevel.High),
64
- debug: () => new LoggerImpl(name, color, LogLevel.Debug),
65
- info: () => new LoggerImpl(name, color, LogLevel.Info),
66
- off: () => new LoggerImpl(name, color, LogLevel.Off),
67
- };
68
- };
69
-
70
- /** Create a {@link ResettableLogger} that can be easily reconfigured */
71
- export const resettableLogger = (name: string, color?: string): ResettableLogger => {
72
- const logger = new LoggerImpl(name, color, LogLevel.High);
73
- return {
74
- logger,
75
- debug: () => (logger.level = LogLevel.Debug),
76
- info: () => (logger.level = LogLevel.Info),
77
- off: () => (logger.level = LogLevel.Off),
78
- };
79
- };
80
11
 
81
- /**
82
- * A logger whose level can be changed later. Useful for libraries to expose,
83
- * so users can easily debug calls in the library
84
- */
85
- export type ResettableLogger = {
86
- logger: Logger;
87
- debug(): void;
88
- info(): void;
89
- off(): void;
90
- };
12
+ const LogLevel = { off: 0, default: 1, info: 2, debug: 3 } as const;
13
+ export type LogLevelStr = "off" | "default" | "info" | "debug";
14
+ if (import.meta.vitest) {
15
+ const { test, expectTypeOf } = import.meta.vitest;
16
+ test("type LogLevelStr", () => {
17
+ expectTypeOf<LogLevelStr>().toEqualTypeOf<keyof typeof LogLevel>();
18
+ });
19
+ }
20
+ type LogLevel = (typeof LogLevel)[LogLevelStr];
91
21
 
92
- export type LoggerFactory = {
93
- /** Standard important logger (warning and errors) */
94
- default(): Logger;
95
- /** Enable +info +debug logging for this logger */
96
- debug(): Logger;
97
- /** Enable +info logging for this logger */
98
- info(): Logger;
99
- /** Stop all logging, including warn and error */
100
- off(): Logger;
101
- };
22
+ /** Args for constructing a logger */
23
+ export interface LoggerConstructor {
24
+ /** CSS Color for the logger, default is 'gray' */
25
+ color?: string;
26
+ /**
27
+ * Logging level, default is "default".
28
+ * The level can still be changed later with setLevel
29
+ */
30
+ level?: LogLevelStr;
31
+ }
102
32
 
103
- export type Logger = {
33
+ /** The logger type */
34
+ export interface Logger {
35
+ /** Set the level of the logger */
36
+ setLevel(level: LogLevelStr): void;
104
37
  /** Log a debug message */
105
38
  debug(obj: unknown): void;
106
39
  /** Log an info message */
@@ -109,21 +42,69 @@ export type Logger = {
109
42
  warn(obj: unknown): void;
110
43
  /** Log an error message */
111
44
  error(obj: unknown): void;
112
- };
45
+ }
113
46
 
114
- export class LoggerImpl implements Logger {
115
- name: string;
116
- color: string | undefined;
117
- level: LogLevel;
47
+ /** Create a logger creator. Use the factory methods to finish making the logger */
48
+ export const logger = (name: string, args: LoggerConstructor): Logger => {
49
+ const { color, level } = args;
50
+ const levelObj = LogLevel[level || "off"] || LogLevel.off;
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ if ((globalThis as any).process) {
53
+ return new BareLoggerImpl(name, levelObj);
54
+ }
55
+ const color2 = color || "gray";
56
+ return new CssLoggerImpl(name, color2, levelObj);
57
+ };
118
58
 
119
- constructor(name: string, color: string | undefined, level: LogLevel) {
120
- this.name = name;
121
- this.color = "padding:0 3x;color:white" + (color ? `;background:${color}` : "");
122
- this.level = level;
59
+ class BareLoggerImpl implements Logger {
60
+ constructor(
61
+ private name: string,
62
+ private level: LogLevel,
63
+ ) {}
64
+ setLevel(level: LogLevelStr): void {
65
+ this.level = LogLevel[level] || LogLevel["off"];
66
+ }
67
+ debug(obj: unknown): void {
68
+ if (this.level !== LogLevel.debug) {
69
+ return;
70
+ }
71
+ console.debug(`DEBUG [${this.name}] ${obj}`);
72
+ }
73
+ info(obj: unknown): void {
74
+ if (this.level < LogLevel.info) {
75
+ return;
76
+ }
77
+ console.info(`INFO [${this.name}] ${obj}`);
78
+ }
79
+ warn(obj: unknown): void {
80
+ if (this.level < LogLevel.default) {
81
+ return;
82
+ }
83
+ console.warn(`WARN [${this.name}] ${obj}`);
84
+ }
85
+ error(obj: unknown): void {
86
+ if (this.level < LogLevel.default) {
87
+ return;
88
+ }
89
+ const msg = errstr(obj);
90
+ console.error(`ERROR [${this.name}] ${msg}`);
91
+ if (msg !== obj) {
92
+ console.error(obj);
93
+ }
123
94
  }
95
+ }
124
96
 
125
- debug(obj: unknown) {
126
- if (globalLevel !== LogLevel.Debug && this.level !== LogLevel.Debug) {
97
+ class CssLoggerImpl implements Logger {
98
+ constructor(
99
+ private name: string,
100
+ private color: string,
101
+ private level: LogLevel,
102
+ ) {}
103
+ setLevel(level: LogLevelStr): void {
104
+ this.level = LogLevel[level] || LogLevel["off"];
105
+ }
106
+ debug(obj: unknown): void {
107
+ if (this.level !== LogLevel.debug) {
127
108
  return;
128
109
  }
129
110
  console.debug(
@@ -133,9 +114,8 @@ export class LoggerImpl implements Logger {
133
114
  "color:inherit;background:inherit",
134
115
  );
135
116
  }
136
-
137
- info(obj: unknown) {
138
- if (globalLevel < LogLevel.Info && this.level < LogLevel.Info) {
117
+ info(obj: unknown): void {
118
+ if (this.level < LogLevel.info) {
139
119
  return;
140
120
  }
141
121
  console.info(
@@ -145,9 +125,8 @@ export class LoggerImpl implements Logger {
145
125
  "color:inherit;background:inherit",
146
126
  );
147
127
  }
148
-
149
- warn(obj: unknown) {
150
- if (globalLevel < LogLevel.High || this.level < LogLevel.High) {
128
+ warn(obj: unknown): void {
129
+ if (this.level < LogLevel.default) {
151
130
  return;
152
131
  }
153
132
  console.warn(
@@ -157,9 +136,8 @@ export class LoggerImpl implements Logger {
157
136
  "color:inherit;background:inherit",
158
137
  );
159
138
  }
160
-
161
- error(obj: unknown) {
162
- if (globalLevel < LogLevel.High || this.level < LogLevel.High) {
139
+ error(obj: unknown): void {
140
+ if (this.level < LogLevel.default) {
163
141
  return;
164
142
  }
165
143
  const msg = errstr(obj);
@@ -1,21 +1,31 @@
1
- /**
2
- * Create a light weight storage wrapper around a value
3
- * that can be subscribed to for changes
4
- */
5
- export function cell<T>({ initial }: CellConstructor<T>): Cell<T> {
6
- return new CellImpl(initial);
7
- }
1
+ /** Create a {@link Cell} */
2
+ export const cell = <T>(args: CellConstructor<T>): Cell<T> => {
3
+ return new CellImpl(args.initial);
4
+ };
8
5
 
9
- export type CellConstructor<T> = {
6
+ /** Args for constructing a cell */
7
+ export interface CellConstructor<T> {
10
8
  /** Initial value */
11
9
  initial: T;
12
- };
10
+ }
13
11
 
14
- export type Cell<T> = {
12
+ /**
13
+ * A light weight storage wrapper around a value
14
+ * that can be subscribed to for changes
15
+ *
16
+ * Created via `cell()`
17
+ *
18
+ * ```typescript
19
+ * import { cell } from "@pistonite/pure/memory";
20
+ *
21
+ * const myCell = cell(true);
22
+ * ```
23
+ */
24
+ export interface Cell<T> {
15
25
  get(): T;
16
26
  set(value: T): void;
17
27
  subscribe(callback: (value: T) => void, notifyImmediately?: boolean): () => void;
18
- };
28
+ }
19
29
 
20
30
  class CellImpl<T> implements Cell<T> {
21
31
  private subscribers: ((value: T) => void)[] = [];
package/src/memory/emp.ts CHANGED
@@ -39,31 +39,41 @@
39
39
  * In 32-bit context like WASM32, the inner value can be a `number`.
40
40
  * In 64-bit context, `number` might be fine for some systems, but `bigint`
41
41
  * is recommended.
42
+ *
43
+ * ## Usage
44
+ *
45
+ * ```typescript
46
+ * // First create a marker to distinguish between different Emp types for TypeScript.
47
+ * // This is not used at runtime
48
+ * const MyNativeType = Symbol("MyNativeType");
49
+ * export type MyNativeType = typeof MyNativeType;
50
+ *
51
+ * // Then use makeEmpType to create a factory function
52
+ * const makeMyNativeTypeEmp = makeEmpType({
53
+ * marker: MyNativeType,
54
+ * free: (ptr) => freeMyNativeType(ptr),
55
+ * });
56
+ *
57
+ * // Now acquire ownership of an object from external native code
58
+ * // and assign it to an Emp
59
+ * const myObjRawPtr = allocMyNativeType();
60
+ * const myObj = makeMyNativeTypeEmp(myObjRawPtr);
61
+ *
62
+ * // when myObj is GC'ed, it will be freed by calling freeMyNativeType
63
+ * ```
42
64
  */
43
- export type Emp<T, TRepr> = {
65
+ export interface Emp<T, TRepr> {
44
66
  /** The type marker for T. This only marks the type for TypeScript and does not exist at runtime */
45
67
  readonly __phantom: T;
46
68
  /** The underlying pointer value */
47
69
  readonly value: TRepr;
48
- };
70
+ }
49
71
 
50
- export type EmpConstructor<T, TRepr> = {
72
+ /** Args for constructing a Emp type */
73
+ export interface EmpConstructor<T, TRepr> {
51
74
  /**
52
- * The marker for the Emp type, used to distinguish between multiple types
53
- *
54
- * Typically, this is a unique symbol:
55
- * ```typescript
56
- * const MyNativeType = Symbol("MyNativeType");
57
- * export type MyNativeType = typeof MyNativeType;
58
- *
59
- * const makeMyNativeTypeEmp = makeEmpType({
60
- * marker: MyNativeType,
61
- * free: (ptr) => void freeMyNativeType(ptr)
62
- * })
63
- * ```
64
- *
65
- * Note that this is not used in runtime, but just used for type inference,
66
- * so you can also skip passing it and specify the type parameter instead
75
+ * The marker for the Emp type, used to distinguish between multiple Emp types
76
+ * in TypeScript
67
77
  */
68
78
  marker?: T;
69
79
 
@@ -71,14 +81,15 @@ export type EmpConstructor<T, TRepr> = {
71
81
  * Function to free the underlying object. Called when this Emp is garbage-collected
72
82
  */
73
83
  free: (ptr: TRepr) => void | Promise<void>;
74
- };
84
+ }
75
85
 
76
86
  /**
77
- * Create a factory function for an {@link Emp} type.
87
+ * Create a factory function for an Emp type. See {@link Emp}
78
88
  */
79
- export const makeEmpType = <T, TRepr>({
80
- free,
81
- }: EmpConstructor<T, TRepr>): ((ptr: TRepr) => Emp<T, TRepr>) => {
89
+ export const makeEmpType = <T, TRepr>(
90
+ args: EmpConstructor<T, TRepr>,
91
+ ): ((ptr: TRepr) => Emp<T, TRepr>) => {
92
+ const { free } = args;
82
93
  const registry = new FinalizationRegistry(free);
83
94
  return (ptr: TRepr) => {
84
95
  const obj = Object.freeze({ value: ptr });
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it } from "mono-dev/vitest";
2
2
 
3
3
  import { idgen, safeidgen } from "./idgen.ts";
4
4
 
@@ -1,12 +1,9 @@
1
1
  /**
2
- * # pure/memory
3
- * JS memory utilities
2
+ * Memory utilities
4
3
  *
5
4
  * @module
6
5
  */
7
6
  export { cell, type CellConstructor, type Cell } from "./cell.ts";
8
7
  export { persist, type PersistConstructor, type Persist } from "./persist.ts";
9
- export * from "./async_erc.ts";
10
8
  export * from "./emp.ts";
11
- export * from "./erc.ts";
12
9
  export * from "./idgen.ts";
@@ -3,13 +3,8 @@ import { cell, type Cell, type CellConstructor } from "./cell.ts";
3
3
  /**
4
4
  * Create a cell that persists its value to a web storage
5
5
  */
6
- export function persist<T>({
7
- storage,
8
- key,
9
- serialize = JSON.stringify,
10
- deserialize,
11
- initial,
12
- }: PersistConstructor<T>): Persist<T> {
6
+ export function persist<T>(args: PersistConstructor<T>): Persist<T> {
7
+ const { storage, key, serialize = JSON.stringify, deserialize, initial } = args;
13
8
  const deser =
14
9
  deserialize ??
15
10
  ((value: string) => {
@@ -22,7 +17,8 @@ export function persist<T>({
22
17
  return new PersistImpl(storage, key, serialize, deser, initial);
23
18
  }
24
19
 
25
- export type PersistConstructor<T> = CellConstructor<T> & {
20
+ /** Args for creating a persisted cell */
21
+ export interface PersistConstructor<T> extends CellConstructor<T> {
26
22
  /** The web storage to use */
27
23
  storage: Storage;
28
24
 
@@ -41,9 +37,10 @@ export type PersistConstructor<T> = CellConstructor<T> & {
41
37
  * By default, it will use `JSON.parse` wrapped with try-catch
42
38
  */
43
39
  deserialize?(value: string): T | null;
44
- };
40
+ }
45
41
 
46
- export type Persist<T> = Cell<T> & {
42
+ /** A cell that also persists its value */
43
+ export interface Persist<T> extends Cell<T> {
47
44
  /**
48
45
  * Load the value initially, and notify all the current subscribers
49
46
  *
@@ -52,9 +49,9 @@ export type Persist<T> = Cell<T> & {
52
49
  init(initial?: T): T;
53
50
  /** Clear the value from the storage */
54
51
  clear(): void;
55
- /** Clera the value and disable the persistence */
52
+ /** Clear the value and disable the persistence */
56
53
  disable(): void;
57
- };
54
+ }
58
55
 
59
56
  class PersistImpl<T> implements Persist<T> {
60
57
  private cell: Cell<T>;
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * Rust-like `Result<T, E>` type and error handling utils
3
+ *
2
4
  * **I once had a fancy error object with TypeScript magic that tries
3
5
  * to reduce allocation while maintaining Result-safety. It turns out
4
6
  * that was slower than allocating plain objects for every return, because
@@ -163,9 +165,15 @@ export type Result<T, E> = Ok<T> | Err<E>;
163
165
  // This is to get type narrowing to work most of the time
164
166
 
165
167
  /** A success value */
166
- export type Ok<T> = { val: T; err?: never };
168
+ export interface Ok<T> {
169
+ val: T;
170
+ err?: never;
171
+ }
167
172
  /** An error value */
168
- export type Err<E> = { err: E; val?: never };
173
+ export interface Err<E> {
174
+ err: E;
175
+ val?: never;
176
+ }
169
177
 
170
178
  /**
171
179
  * A value that is either `void` or an error
@@ -177,7 +185,7 @@ export type Void<E> = { val?: never; err?: never } | { err: E };
177
185
  export type VoidOk = Record<string, never>;
178
186
 
179
187
  /** Wrap a function with try-catch and return a Result. */
180
- export function tryCatch<T, E = unknown>(fn: () => T): Result<T, E> {
188
+ export function tryCatch<T, E = Error>(fn: () => T): Result<T, E> {
181
189
  try {
182
190
  return { val: fn() };
183
191
  } catch (e) {
@@ -186,7 +194,7 @@ export function tryCatch<T, E = unknown>(fn: () => T): Result<T, E> {
186
194
  }
187
195
 
188
196
  /** Wrap an async function with try-catch and return a Promise<Result>. */
189
- export async function tryAsync<T, E = unknown>(fn: () => Promise<T>): Promise<Result<T, E>> {
197
+ export async function tryAsync<T, E = Error>(fn: () => Promise<T>): Promise<Result<T, E>> {
190
198
  try {
191
199
  return { val: await fn() };
192
200
  } catch (e) {
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from "mono-dev/vitest";
2
2
 
3
3
  import { batch } from "./batch.ts";
4
4
 
package/src/sync/batch.ts CHANGED
@@ -1,7 +1,16 @@
1
1
  import { type AnyFn, makePromise, type PromiseHandle, type AwaitRet } from "./util.ts";
2
2
 
3
+ /** Factory for batched function. See {@link BatchConstructor} for usage. */
4
+ export function batch<TFn extends AnyFn>(args: BatchConstructor<TFn>) {
5
+ const { fn, batch, unbatch, interval, disregardExecutionTime } = args;
6
+ const impl = new BatchImpl(fn, batch, unbatch, interval, !!disregardExecutionTime);
7
+ return (...args: Parameters<TFn>) => impl.invoke(...args);
8
+ }
9
+
3
10
  /**
4
- * An async event wrapper that allows multiple calls in an interval
11
+ * Options to construct a `batch` function
12
+ *
13
+ * A batch function is an async event wrapper that allows multiple calls in an interval
5
14
  * to be batched together, and only call the underlying function once.
6
15
  *
7
16
  * Optionally, the output can be unbatched to match the inputs.
@@ -87,21 +96,7 @@ import { type AnyFn, makePromise, type PromiseHandle, type AwaitRet } from "./ut
87
96
  * ```
88
97
  *
89
98
  */
90
- export function batch<TFn extends AnyFn>({
91
- fn,
92
- batch,
93
- unbatch,
94
- interval,
95
- disregardExecutionTime,
96
- }: BatchConstructor<TFn>) {
97
- const impl = new BatchImpl(fn, batch, unbatch, interval, !!disregardExecutionTime);
98
- return (...args: Parameters<TFn>) => impl.invoke(...args);
99
- }
100
-
101
- /**
102
- * Options to construct a `batch` function
103
- */
104
- export type BatchConstructor<TFn extends AnyFn> = {
99
+ export interface BatchConstructor<TFn extends AnyFn> {
105
100
  /** Function to be wrapped */
106
101
  fn: TFn;
107
102
  /** Function to batch the inputs across multiple calls */
@@ -122,7 +117,7 @@ export type BatchConstructor<TFn extends AnyFn> = {
122
117
 
123
118
  /** See `debounce` for more information */
124
119
  disregardExecutionTime?: boolean;
125
- };
120
+ }
126
121
 
127
122
  class BatchImpl<TFn extends AnyFn> {
128
123
  private idle: boolean;
@@ -1,3 +1,5 @@
1
+ // this is an exception to the global state rule - since it doesn't really matter
2
+ // if this is duplicated
1
3
  const captured = new Set<unknown>();
2
4
 
3
5
  /**
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from "mono-dev/vitest";
2
2
 
3
3
  import { debounce } from "./debounce.ts";
4
4