@gesslar/toolkit 4.1.0 → 4.3.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/README.md CHANGED
@@ -101,6 +101,37 @@ The browser version includes: Collection, Data, Disposer, HTML, Notify, Sass,
101
101
  Tantrum, Type (TypeSpec), Util, and Valid. Node-only modules (Cache,
102
102
  DirectoryObject, FileObject, FileSystem, Glog, Term) are not available in the browser version.
103
103
 
104
+ ### Vendor Bundle
105
+
106
+ Pre-built bundles are included for environments without a build pipeline, such
107
+ as webviews (VS Code, Tauri, Electron) or plain HTML pages. No bundler required.
108
+
109
+ #### ES module
110
+
111
+ ```html
112
+ <script type="module">
113
+ import {Data, Collection} from "./node_modules/@gesslar/toolkit/vendor/toolkit.esm.js"
114
+ </script>
115
+ ```
116
+
117
+ Or import via the package export:
118
+
119
+ ```javascript
120
+ import {Data, Collection} from "@gesslar/toolkit/vendor"
121
+ ```
122
+
123
+ #### UMD (global script)
124
+
125
+ ```html
126
+ <script src="./node_modules/@gesslar/toolkit/vendor/toolkit.umd.js"></script>
127
+ <script>
128
+ const {Data, Collection} = Toolkit
129
+ </script>
130
+ ```
131
+
132
+ The vendor bundles contain the same browser-compatible utilities listed above,
133
+ fully self-contained with zero external dependencies.
134
+
104
135
  ## Post Partum
105
136
 
106
137
  If you made it this far, please understand that I have absolutely zero scruples
@@ -117,3 +148,20 @@ off klaxons about my fetish for breaking changes, so you should be all right
117
148
  if you're paying attention. 🤷🏻
118
149
 
119
150
  Sincerely, Senator Yabba of the Dabba (Doo)
151
+
152
+ ## License
153
+
154
+ toolkit is released into the public domain under the
155
+ [Unlicense](UNLICENSE.txt).
156
+
157
+ This package includes or depends on third-party components under their own
158
+ licenses:
159
+
160
+ | Dependency | License |
161
+ | --- | --- |
162
+ | [@gesslar/colours](https://github.com/gesslar/colours) | Unlicense |
163
+ | [ajv](https://github.com/ajv-validator/ajv) | MIT |
164
+ | [DOMPurify](https://github.com/cure53/DOMPurify) | Apache-2.0 / MPL-2.0 |
165
+ | [json5](https://github.com/json5/json5) | MIT |
166
+ | [supports-color](https://github.com/chalk/supports-color) | MIT |
167
+ | [yaml](https://github.com/eemeli/yaml) | ISC |
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "gesslar",
6
6
  "url": "https://gesslar.dev"
7
7
  },
8
- "version": "4.1.0",
8
+ "version": "4.3.0",
9
9
  "license": "Unlicense",
10
10
  "homepage": "https://github.com/gesslar/toolkit#readme",
11
11
  "repository": {
@@ -41,11 +41,16 @@
41
41
  "./node": {
42
42
  "types": "./types/node/index.d.ts",
43
43
  "default": "./src/node/index.js"
44
+ },
45
+ "./vendor": {
46
+ "types": "./types/browser/index.d.ts",
47
+ "default": "./vendor/toolkit.esm.js"
44
48
  }
45
49
  },
46
50
  "files": [
47
51
  "src/",
48
52
  "types/",
53
+ "vendor/",
49
54
  "scripts/",
50
55
  "UNLICENSE.txt"
51
56
  ],
@@ -55,7 +60,8 @@
55
60
  "scripts": {
56
61
  "preinstall": "node ./scripts/check-node-version.mjs",
57
62
  "types": "node -e \"require('fs').rmSync('types',{recursive:true,force:true});\" && tsc -p tsconfig.types.json",
58
- "prepublishOnly": "npm run types",
63
+ "vendor:build": "node scripts/vendor-build.js",
64
+ "prepack": "npm run types && npm run vendor:build",
59
65
  "lint": "eslint src/",
60
66
  "lint:fix": "eslint src/ --fix",
61
67
  "submit": "npm publish --access public --//registry.npmjs.org/:_authToken=\"${NPM_ACCESS_TOKEN}\"",
@@ -77,9 +83,11 @@
77
83
  "yaml": "^2.8.2"
78
84
  },
79
85
  "devDependencies": {
80
- "@gesslar/uglier": "^2.0.0",
81
- "eslint": "^10.0.2",
82
- "happy-dom": "^20.8.3",
86
+ "@gesslar/uglier": "^2.2.0",
87
+ "@rollup/plugin-node-resolve": "^16.0.3",
88
+ "rollup": "^4.59.0",
89
+ "eslint": "^10.0.3",
90
+ "happy-dom": "^20.8.4",
83
91
  "typescript": "^5.9.3"
84
92
  }
85
93
  }
@@ -0,0 +1,32 @@
1
+ import {readFileSync, mkdirSync} from "node:fs"
2
+ import {rollup} from "rollup"
3
+ import {nodeResolve} from "@rollup/plugin-node-resolve"
4
+
5
+ const vendorDir = "vendor"
6
+ const {version} = JSON.parse(readFileSync("package.json", "utf8"))
7
+
8
+ mkdirSync(vendorDir, {recursive: true})
9
+
10
+ const bundle = await rollup({
11
+ input: "src/browser/index.js",
12
+ plugins: [nodeResolve()],
13
+ })
14
+
15
+ try {
16
+ await bundle.write({
17
+ file: `${vendorDir}/toolkit.esm.js`,
18
+ format: "es",
19
+ banner: `// @gesslar/toolkit v${version} - ES module bundle`,
20
+ })
21
+
22
+ await bundle.write({
23
+ file: `${vendorDir}/toolkit.umd.js`,
24
+ format: "umd",
25
+ name: "Toolkit",
26
+ banner: `// @gesslar/toolkit v${version} - UMD bundle`,
27
+ })
28
+ } finally {
29
+ await bundle.close()
30
+ }
31
+
32
+ console.log(`Built vendor bundles for @gesslar/toolkit@${version}`)
@@ -54,6 +54,26 @@ export class Notify {
54
54
  await Util.asyncEmit(this.#emitter, type, payload)
55
55
  }
56
56
 
57
+ /**
58
+ * Fires an event asynchronously without blocking the caller.
59
+ * Listeners run in the background. If any listener throws and an error
60
+ * callback is provided, it receives the error. Otherwise errors are
61
+ * silently discarded.
62
+ *
63
+ * @param {string} type - Event name to dispatch.
64
+ * @param {unknown} [payload] - Data to send with the event.
65
+ * @param {((error: Error) => void)|null} [errorCb] - Optional callback for errors.
66
+ * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the operation.
67
+ * @returns {undefined}
68
+ */
69
+ fire(type, payload, errorCb, signal) {
70
+ Valid.type(type, "String", {allowEmpty: false})
71
+ Valid.type(errorCb, "Undefined|Null|Function")
72
+ Valid.type(signal, "Undefined|Null|AbortSignal")
73
+
74
+ Util.fire(this.#emitter, type, payload, errorCb, signal)
75
+ }
76
+
57
77
  /**
58
78
  * Emits an event and returns the payload for simple request/response flows.
59
79
  * Listeners can mutate the payload object to provide responses.
@@ -68,7 +68,7 @@ export default class Util extends BrowserUtil {
68
68
  * @returns {Promise<undefined>} Resolves when all listeners have completed
69
69
  */
70
70
  static async #performAsyncEmit(emitter, event, ...args) {
71
- const listeners = emitter.listeners(event)
71
+ const listeners = emitter.rawListeners(event)
72
72
 
73
73
  if(listeners.length === 0)
74
74
  return // No listeners, nothing to do
@@ -115,6 +115,48 @@ export default class Util extends BrowserUtil {
115
115
  }
116
116
  }
117
117
 
118
+ /**
119
+ * Fires an event asynchronously without blocking the caller.
120
+ * Listeners run in the background via asyncEmit. If any listener rejects
121
+ * and an error callback is provided, it receives the error. If no callback
122
+ * is provided, errors are silently discarded.
123
+ *
124
+ * @param {EventEmitter} emitter - The EventEmitter instance to emit on
125
+ * @param {string} event - The event name to emit
126
+ * @param {unknown} [payload] - Data to send with the event
127
+ * @param {((error: Error) => void)|null} [errorCb] - Optional callback for errors
128
+ * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the operation
129
+ * @returns {undefined}
130
+ */
131
+ static fire(emitter, event, payload, errorCb, signal) {
132
+ Valid.type(errorCb, "Undefined|Null|Function")
133
+ Valid.type(signal, "Undefined|Null|AbortSignal")
134
+
135
+ if(signal?.aborted)
136
+ return
137
+
138
+ const promise = this.asyncEmit(emitter, event, payload)
139
+
140
+ if(signal) {
141
+ const onAbort = () => {} // asyncEmit already in flight, nothing to cancel
142
+ signal.addEventListener("abort", onAbort, {once: true})
143
+ promise.then(
144
+ () => signal.removeEventListener("abort", onAbort),
145
+ error => {
146
+ signal.removeEventListener("abort", onAbort)
147
+
148
+ if(!signal.aborted && errorCb) {
149
+ errorCb(error)
150
+ }
151
+ }
152
+ )
153
+ } else if(errorCb) {
154
+ promise.catch(errorCb)
155
+ } else {
156
+ promise.catch(() => {})
157
+ }
158
+ }
159
+
118
160
  /**
119
161
  * Emits an event asynchronously and waits for all listeners to complete.
120
162
  * Like asyncEmit, but uses duck typing for more flexible emitter validation.
@@ -129,7 +171,7 @@ export default class Util extends BrowserUtil {
129
171
  static async asyncEmitQuack(emitter, event, ...args) {
130
172
  try {
131
173
  if(!emitter ||
132
- typeof emitter.listeners !== "function" ||
174
+ typeof emitter.rawListeners !== "function" ||
133
175
  typeof emitter.on !== "function" ||
134
176
  typeof emitter.emit !== "function") {
135
177
  throw Sass.new("First argument must be an EventEmitter-like object")
@@ -28,6 +28,19 @@ export class Notify {
28
28
  * @returns {Promise<undefined>} Resolves when all listeners have completed.
29
29
  */
30
30
  asyncEmit(type: string, payload?: unknown): Promise<undefined>;
31
+ /**
32
+ * Fires an event asynchronously without blocking the caller.
33
+ * Listeners run in the background. If any listener throws and an error
34
+ * callback is provided, it receives the error. Otherwise errors are
35
+ * silently discarded.
36
+ *
37
+ * @param {string} type - Event name to dispatch.
38
+ * @param {unknown} [payload] - Data to send with the event.
39
+ * @param {((error: Error) => void)|null} [errorCb] - Optional callback for errors.
40
+ * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the operation.
41
+ * @returns {undefined}
42
+ */
43
+ fire(type: string, payload?: unknown, errorCb?: ((error: Error) => void) | null, signal?: AbortSignal): undefined;
31
44
  /**
32
45
  * Emits an event and returns the payload for simple request/response flows.
33
46
  * Listeners can mutate the payload object to provide responses.
@@ -1 +1 @@
1
- {"version":3,"file":"Notify.d.ts","sourceRoot":"","sources":["../../../src/node/lib/Notify.js"],"names":[],"mappings":"AAWA;;;;GAIG;AAEH;;;GAGG;AACH;IACE,iDAAiD;IACjD,MADW,MAAM,CACF;IAKf;;;;;;OAMG;IACH,WAJW,MAAM,YACN,OAAO,GACL,SAAS,CAMrB;IAED;;;;;;;;OAQG;IACH,gBAJW,MAAM,YACN,OAAO,GACL,OAAO,CAAC,SAAS,CAAC,CAM9B;IAED;;;;;;;OAOG;IACH,cAJW,MAAM,YACN,OAAO,GACL,OAAO,CAQnB;IAED;;;;;;;;OAQG;IACH,SANW,MAAM,WACN,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,YAC/B,YAAY,YACZ,kBAAkB,GAChB,MAAM,SAAS,CAa3B;IAED;;;;;;;OAOG;IACH,UALW,MAAM,WACN,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,YAC/B,YAAY,GACV,SAAS,CAMrB;;CACF;;;;;;;WA9Fa,OAAO;;;;aACP,WAAW;;6BARE,aAAa"}
1
+ {"version":3,"file":"Notify.d.ts","sourceRoot":"","sources":["../../../src/node/lib/Notify.js"],"names":[],"mappings":"AAWA;;;;GAIG;AAEH;;;GAGG;AACH;IACE,iDAAiD;IACjD,MADW,MAAM,CACF;IAKf;;;;;;OAMG;IACH,WAJW,MAAM,YACN,OAAO,GACL,SAAS,CAMrB;IAED;;;;;;;;OAQG;IACH,gBAJW,MAAM,YACN,OAAO,GACL,OAAO,CAAC,SAAS,CAAC,CAM9B;IAED;;;;;;;;;;;OAWG;IACH,WANW,MAAM,YACN,OAAO,YACP,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAC,IAAI,WAC7B,WAAW,GACT,SAAS,CAQrB;IAED;;;;;;;OAOG;IACH,cAJW,MAAM,YACN,OAAO,GACL,OAAO,CAQnB;IAED;;;;;;;;OAQG;IACH,SANW,MAAM,WACN,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,YAC/B,YAAY,YACZ,kBAAkB,GAChB,MAAM,SAAS,CAa3B;IAED;;;;;;;OAOG;IACH,UALW,MAAM,WACN,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,YAC/B,YAAY,GACV,SAAS,CAMrB;;CACF;;;;;;;WAlHa,OAAO;;;;aACP,WAAW;;6BARE,aAAa"}
@@ -59,6 +59,20 @@ export default class Util extends BrowserUtil {
59
59
  * @returns {Promise<undefined>} Resolves when all listeners have completed
60
60
  */
61
61
  static asyncEmit(emitter: EventEmitter, event: string, ...args: unknown[]): Promise<undefined>;
62
+ /**
63
+ * Fires an event asynchronously without blocking the caller.
64
+ * Listeners run in the background via asyncEmit. If any listener rejects
65
+ * and an error callback is provided, it receives the error. If no callback
66
+ * is provided, errors are silently discarded.
67
+ *
68
+ * @param {EventEmitter} emitter - The EventEmitter instance to emit on
69
+ * @param {string} event - The event name to emit
70
+ * @param {unknown} [payload] - Data to send with the event
71
+ * @param {((error: Error) => void)|null} [errorCb] - Optional callback for errors
72
+ * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the operation
73
+ * @returns {undefined}
74
+ */
75
+ static fire(emitter: EventEmitter, event: string, payload?: unknown, errorCb?: ((error: Error) => void) | null, signal?: AbortSignal): undefined;
62
76
  /**
63
77
  * Emits an event asynchronously and waits for all listeners to complete.
64
78
  * Like asyncEmit, but uses duck typing for more flexible emitter validation.
@@ -1 +1 @@
1
- {"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../../../src/node/lib/Util.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IACE;;;;;OAKG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,mCAHW,MAAM,GACJ,KAAK,CAAC,MAAM,CAAC,CAazB;IAED;;;;;;;;OAQG;IACH,+CALW,MAAM,SACN,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAmB9B;IAED;;;;;;;;;;;;OAYG;IACH,0BALW,YAAY,SACZ,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAgB9B;IAED;;;;;;;;;;OAUG;IACH,+BALW,MAAM,SACN,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAoB9B;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,kBALW,MAAM,QACN,OAAO,GACL,OAAO,CAmBnB;CACF;wBA1LuB,2BAA2B;6BADxB,aAAa"}
1
+ {"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../../../src/node/lib/Util.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IACE;;;;;OAKG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,mCAHW,MAAM,GACJ,KAAK,CAAC,MAAM,CAAC,CAazB;IAED;;;;;;;;OAQG;IACH,+CALW,MAAM,SACN,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAmB9B;IAED;;;;;;;;;;;;OAYG;IACH,0BALW,YAAY,SACZ,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAgB9B;IAED;;;;;;;;;;;;OAYG;IACH,qBAPW,YAAY,SACZ,MAAM,YACN,OAAO,YACP,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAC,IAAI,WAC7B,WAAW,GACT,SAAS,CA6BrB;IAED;;;;;;;;;;OAUG;IACH,+BALW,MAAM,SACN,MAAM,WACH,OAAO,EAAA,GACR,OAAO,CAAC,SAAS,CAAC,CAoB9B;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,kBALW,MAAM,QACN,OAAO,GACL,OAAO,CAmBnB;CACF;wBApOuB,2BAA2B;6BADxB,aAAa"}