@otorp/plugin-utils 1.0.0-staging.1 → 1.0.1-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 +78 -88
- package/index.js +116 -150
- package/package.json +6 -12
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
<!-- omit in toc -->
|
|
9
9
|
## 🎯 Objective
|
|
10
10
|
|
|
11
|
-
Small, init-time utilities extracted from the Otorp
|
|
11
|
+
Small, init-time utilities extracted from the Otorp monolith so plugin authors can register modules without duplicating boilerplate or reimplementing the shared symbol registry.
|
|
12
12
|
|
|
13
|
-
This package does not extend prototypes or run Otorp's alias pass — it only wires your plugin into
|
|
13
|
+
This package does not extend prototypes or run Otorp's alias pass — it only wires your plugin into otorp plugin registry that **Otorp** consumes during initialization.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -20,20 +20,42 @@ This package does not extend prototypes or run Otorp's alias pass — it only wi
|
|
|
20
20
|
- [🚀 Quickstart](#-quickstart)
|
|
21
21
|
- [📚 API](#-api)
|
|
22
22
|
- [`setMod(config)`](#setmodconfig)
|
|
23
|
-
- [`newErrorHandler(modName?)`](#newerrorhandlermodname)
|
|
24
23
|
- [`ref(key, value?)`](#refkey-value)
|
|
24
|
+
- [`newErrorHandler(modName?)`](#newerrorhandlermodname)
|
|
25
25
|
- [`root`](#root)
|
|
26
26
|
- [🔌 Plugin registration](#-plugin-registration)
|
|
27
27
|
- [With `@otorp/plugin-utils` (recommended)](#with-otorpplugin-utils-recommended)
|
|
28
28
|
- [Manual registration (equivalent)](#manual-registration-equivalent)
|
|
29
29
|
- [❓ FAQ](#-faq)
|
|
30
|
+
- [Why a separate package?](#why-a-separate-package)
|
|
31
|
+
- [Do I need this package to write a plugin?](#do-i-need-this-package-to-write-a-plugin)
|
|
30
32
|
- [🤝 Contributing](#-contributing)
|
|
31
33
|
|
|
32
34
|
<!-- /TOC -->
|
|
33
35
|
|
|
34
36
|
## 📦 Installation
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
Load **before** Otorp core, alongside your plugin script:
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script type="module">
|
|
42
|
+
import { setMod, newErrorHandler } from './path/to/@otorp/plugin-utils/src/main.js';
|
|
43
|
+
|
|
44
|
+
const key = setMod({
|
|
45
|
+
source: 'myPlugin',
|
|
46
|
+
endpoints: { HTMLElement: ['myPlugin.customMethod'] },
|
|
47
|
+
modules(methodName) {
|
|
48
|
+
return function (...args) {
|
|
49
|
+
// `this` is the DOM instance
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Optional: use error helpers attached to the returned symbol key
|
|
55
|
+
key.warn('registered');
|
|
56
|
+
</script>
|
|
57
|
+
<script src="path/to/otorp.js"></script>
|
|
58
|
+
```
|
|
37
59
|
|
|
38
60
|
**npm**
|
|
39
61
|
|
|
@@ -42,51 +64,42 @@ npm install @otorp/plugin-utils
|
|
|
42
64
|
```
|
|
43
65
|
|
|
44
66
|
```javascript
|
|
45
|
-
import setMod from '@otorp/plugin-utils';
|
|
46
|
-
|
|
67
|
+
import { setMod } from '@otorp/plugin-utils';
|
|
47
68
|
```
|
|
48
69
|
|
|
70
|
+
> **Requirement:** [Otorp core](https://gitlab.com/tdj.dev/otorp) must be loaded after your plugin. See the core README for configuration (`otorpConfig`), alias behaviour, and the init pass.
|
|
71
|
+
|
|
49
72
|
---
|
|
50
73
|
|
|
51
74
|
## 🚀 Quickstart
|
|
52
75
|
|
|
76
|
+
Minimal plugin using `setMod`:
|
|
77
|
+
|
|
53
78
|
```javascript
|
|
54
|
-
import setMod from '@otorp/plugin-utils';
|
|
79
|
+
import { setMod } from '@otorp/plugin-utils';
|
|
55
80
|
|
|
56
81
|
setMod({
|
|
57
82
|
source: 'myPlugin',
|
|
58
83
|
endpoints: {
|
|
59
84
|
"HTMLElement": [
|
|
60
85
|
'myPlugin.customMethod',
|
|
61
|
-
'myPlugin.
|
|
86
|
+
'myPlugin.meta.customMethod'
|
|
62
87
|
]
|
|
63
88
|
},
|
|
64
|
-
factory(
|
|
65
|
-
// methodName: last segment of the query string
|
|
66
|
-
// meta: everything between source and methodName
|
|
89
|
+
factory(customMethod, meta) {
|
|
67
90
|
return function (...args) {
|
|
68
|
-
//
|
|
91
|
+
// Installed on HTMLElement.prototype during Otorp init
|
|
69
92
|
};
|
|
70
93
|
}
|
|
71
94
|
});
|
|
72
95
|
```
|
|
73
96
|
|
|
74
|
-
**Query string anatomy:**
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
"myPlugin.meta1.meta2.methodName"
|
|
78
|
-
│ │ │ └── methodName (passed as first arg to factory)
|
|
79
|
-
│ └─────┘────────── meta (spread as remaining args to factory)
|
|
80
|
-
└────────────────────────── source (identifies your plugin)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
97
|
What `setMod` does in one call:
|
|
84
98
|
|
|
85
|
-
1. Validates the `
|
|
86
|
-
2.
|
|
87
|
-
3.
|
|
88
|
-
4.
|
|
89
|
-
5. Assigns the factory to `root` under the returned key so Otorp core can invoke it once per resolved endpoint during init.
|
|
99
|
+
1. Validates the `modules` factory.
|
|
100
|
+
2. Registers `{ endpoints, aliases, key }` in the modules **Map** under `source`.
|
|
101
|
+
3. Creates a unique symbol-like key object with `.throw`, `.warn`, and `.error` helpers.
|
|
102
|
+
4. Assigns the factory to `window` under the returned key so Otorp core can invoke it once per resolved endpoint.
|
|
90
103
|
|
|
91
104
|
---
|
|
92
105
|
|
|
@@ -94,56 +107,39 @@ What `setMod` does in one call:
|
|
|
94
107
|
|
|
95
108
|
All exports are init-time helpers. There is no runtime API after Otorp finishes its pass.
|
|
96
109
|
|
|
97
|
-
### `setMod(config)`
|
|
110
|
+
### `setMod(config)`
|
|
98
111
|
|
|
99
112
|
Register a plugin module for Otorp's initialization pass.
|
|
100
113
|
|
|
101
|
-
| Property | Type |
|
|
102
|
-
|
|
|
103
|
-
| `source` | `string` |
|
|
104
|
-
| `endpoints` | `Record<string, string[]>` |
|
|
105
|
-
| `
|
|
106
|
-
| `aliases` | `Record<string, string> \| null` |
|
|
114
|
+
| Property | Type | Description |
|
|
115
|
+
| :--- | :--- | :--- |
|
|
116
|
+
| `source` | `string` | Unique module id — used as the Map key. |
|
|
117
|
+
| `endpoints` | `Record<string, string[]>` | Target prototype name → endpoint query strings. |
|
|
118
|
+
| `modules` | `Function` | Factory called per endpoint; must return the function to install on the prototype. |
|
|
119
|
+
| `aliases` | `Record<string, string> \| null` | Optional alias map. Default `null`. |
|
|
107
120
|
|
|
108
|
-
**Returns:**
|
|
121
|
+
**Returns:** a Symbol-like object with attached `.throw`, `.warn`, and `.error` handlers.
|
|
109
122
|
|
|
110
|
-
|
|
123
|
+
### `ref(key, value?)`
|
|
124
|
+
|
|
125
|
+
Read or write an entry in Otorp namespace.
|
|
126
|
+
|
|
127
|
+
- Pass a **truthy** `value` to assign and return it.
|
|
128
|
+
- Omit `value` (or pass a falsy one) to read only.
|
|
111
129
|
|
|
112
130
|
### `newErrorHandler(modName?)`
|
|
113
131
|
|
|
114
|
-
Creates a namespaced error handler. The namespace
|
|
132
|
+
Creates a namespaced error handler. The namespace is written to `error.name`.
|
|
115
133
|
|
|
116
134
|
| Callable | Behaviour |
|
|
117
135
|
| :--- | :--- |
|
|
118
|
-
| `handler(msg,
|
|
119
|
-
| `handler.
|
|
120
|
-
| `handler.
|
|
121
|
-
|
|
122
|
-
```javascript
|
|
123
|
-
import { newErrorHandler } from '@otorp/plugin-utils';
|
|
124
|
-
|
|
125
|
-
const err = newErrorHandler('myPlugin');
|
|
126
|
-
err('Missing endpoint'); // throws exeption "[myPlugin] Missing endpoint"
|
|
127
|
-
err.log('Deprecated alias used'); // error log "[myPlugin] Deprecated alias used"
|
|
128
|
-
err.warn('Init failed', { cause }); // warning log "[myPlugin - <cause.name>] Init failed"
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
### `ref(key, value?)`
|
|
134
|
-
|
|
135
|
-
Read or write an entry in the Otorp namespace (`Symbol.for('otorp:' + key)` on `root`). Designed for registries, configuration objects, and shared tooling.
|
|
136
|
-
|
|
137
|
-
* Pass a **truthy** `value` to assign and return it.
|
|
138
|
-
* Omit `value` (or pass a falsy one) to read the current value.
|
|
139
|
-
|
|
140
|
-
> Falsy values cannot be stored through this helper.
|
|
141
|
-
|
|
142
|
-
---
|
|
136
|
+
| `handler(msg, options?)` | Throws an `Error` (optional `cause` / `type`). |
|
|
137
|
+
| `handler.warn(...)` | `console.warn` with the same formatting. |
|
|
138
|
+
| `handler.log(...)` | `console.error` with the same formatting. |
|
|
143
139
|
|
|
144
140
|
### `root`
|
|
145
141
|
|
|
146
|
-
|
|
142
|
+
Shorthand for `window` — Minifier-friendly alias used internally and exposed for advanced plugin setups.
|
|
147
143
|
|
|
148
144
|
---
|
|
149
145
|
|
|
@@ -152,46 +148,42 @@ Minifier-friendly alias for `window`. Allows bundlers to mangle the identifier a
|
|
|
152
148
|
### With `@otorp/plugin-utils` (recommended)
|
|
153
149
|
|
|
154
150
|
```javascript
|
|
155
|
-
import setMod from '@otorp/plugin-utils';
|
|
151
|
+
import { setMod } from '@otorp/plugin-utils';
|
|
156
152
|
|
|
157
|
-
|
|
153
|
+
setMod({
|
|
158
154
|
source: 'myPlugin',
|
|
159
|
-
endpoints: {
|
|
160
|
-
|
|
155
|
+
endpoints: { HTMLElement: ['myPlugin.customMethod'] },
|
|
156
|
+
modules(methodName) {
|
|
161
157
|
return function (...args) { /* … */ };
|
|
162
158
|
}
|
|
163
159
|
});
|
|
164
|
-
|
|
165
|
-
key.warn('registered'); // "[myPlugin] registered"
|
|
166
|
-
|
|
167
160
|
```
|
|
168
161
|
|
|
169
162
|
### Manual registration (equivalent)
|
|
170
163
|
|
|
171
|
-
If you prefer not to
|
|
164
|
+
If you prefer not to use this package, the same contract applies — core expects a **Map**, not a plain object:
|
|
172
165
|
|
|
173
166
|
```javascript
|
|
174
|
-
const
|
|
175
|
-
const pluginKey = Object(Symbol(source));
|
|
176
|
-
const modulesMap = (window[Symbol.for('otorp:modules')] ??= new Map());
|
|
167
|
+
const pluginKey = Object(Symbol('myPlugin'));
|
|
177
168
|
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
Object.defineProperties(pluginKey, {
|
|
170
|
+
throw: { value: myErrHandler, writable: false, configurable: false },
|
|
171
|
+
warn: { value: myErrHandler.warn, writable: false, configurable: false },
|
|
172
|
+
error: { value: myErrHandler.log, writable: false, configurable: false }
|
|
173
|
+
});
|
|
180
174
|
|
|
181
|
-
|
|
182
|
-
endpoints: {
|
|
175
|
+
(window[Symbol.for('otorp:modules')] ??= new Map()).set('myPlugin', {
|
|
176
|
+
endpoints: { HTMLElement: ['myPlugin.customMethod'] },
|
|
183
177
|
aliases: null,
|
|
184
|
-
key: pluginKey
|
|
185
|
-
count
|
|
178
|
+
key: pluginKey
|
|
186
179
|
});
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
window[pluginKey] = function factory(methodName, ...meta) {
|
|
181
|
+
window[pluginKey] = function factory (methodName) {
|
|
190
182
|
return function (...args) { /* … */ };
|
|
191
183
|
};
|
|
192
184
|
```
|
|
193
185
|
|
|
194
|
-
`setMod` exists so you do not maintain that boilerplate
|
|
186
|
+
`setMod` exists so you do not maintain that boilerplate in every plugin repo.
|
|
195
187
|
|
|
196
188
|
---
|
|
197
189
|
|
|
@@ -199,18 +191,16 @@ window[pluginKey] = function factory(methodName, ...meta) {
|
|
|
199
191
|
|
|
200
192
|
### Why a separate package?
|
|
201
193
|
|
|
202
|
-
Otorp originally bundled core, helpers, and plugin utilities
|
|
194
|
+
Otorp originally bundled core, helpers, and plugin utilities in one repo. Splitting `@otorp/plugin-utils` keeps the core lean and gives plugin authors a stable, shared registration layer without copy-pasting registry code.
|
|
203
195
|
|
|
204
196
|
### Do I need this package to write a plugin?
|
|
205
197
|
|
|
206
|
-
No. Any script that writes the correct shape into `otorp:modules` and `root[key]` before Otorp
|
|
198
|
+
No. Any script that writes the correct shape into `otorp:modules` and `root[key]` before Otorp loads will work. This package is the supported shortcut.
|
|
207
199
|
|
|
208
200
|
---
|
|
209
201
|
|
|
210
202
|
## 🤝 Contributing
|
|
211
203
|
|
|
212
|
-
Contributions, bug reports, and feature requests are welcome.
|
|
204
|
+
Contributions, bug reports, and feature requests are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
213
205
|
|
|
214
|
-
|
|
215
|
-
* **Issue Tracker:** [gitlab.com/otorp/plugin-utils/-/issues](https://gitlab.com/otorp/plugin-utils/-/issues)
|
|
216
|
-
* **Core Otorp Docs:** [gitlab.com/tdj.dev/otorp](https://gitlab.com/tdj.dev/otorp)
|
|
206
|
+
**Core Otorp docs:** [gitlab.com/tdj.dev/otorp](https://gitlab.com/tdj.dev/otorp)
|
package/index.js
CHANGED
|
@@ -1,173 +1,139 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/** Register an Otorp plugin module for the initialization pass.
|
|
5
|
-
*
|
|
6
|
-
* Stores endpoint and aliases metadata in the shared Otorp modules Map,
|
|
7
|
-
* attaches module-scoped error helpers to the returned key, and installs
|
|
8
|
-
* the factory on `root` so Otorp core can resolve it during init.
|
|
9
|
-
*
|
|
10
|
-
* > **Load order:** must be called before Otorp core initializes.
|
|
11
|
-
* > Otorp deletes itself after its init pass — late registrations are ignored.
|
|
12
|
-
*
|
|
13
|
-
* @param {object} config - Plugin registration payload.
|
|
14
|
-
* @param {string} config.source - Unique module identifier. Used as the Map
|
|
15
|
-
* key and as the first segment of endpoint query strings.
|
|
16
|
-
* @param {Record<string, string[]>} config.endpoints - Maps a target prototype
|
|
17
|
-
* name to an array of endpoint query strings.
|
|
18
|
-
* Format: `"source.meta1.meta2.methodName"` — everything between `source`
|
|
19
|
-
* and `methodName` is forwarded to `factory` as metadata.
|
|
20
|
-
* (e.g. `{ "HTMLElement": ['myPlugin.meta.customMethod'] }`)
|
|
21
|
-
* @param {Function} config.factory - Called by Otorp once per resolved
|
|
22
|
-
* endpoint. Receives `(methodName, ...meta)` and must return the function
|
|
23
|
-
* to install on the prototype, where `this` will be the target instance.
|
|
24
|
-
* @param {Record<string, string>|null} [config.aliases] - Optional alias
|
|
25
|
-
* map consumed by Otorp's aliaser during the init pass.
|
|
26
|
-
* @returns {Object} A unique symbol-like key object (`Object(Symbol(source))`)
|
|
27
|
-
* with `.throw`, `.warn`, and `.error` error handlers attached.
|
|
28
|
-
* Otorp resolves the factory via `root[key]` — not through `Symbol.for`.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* import setMod from '@otorp/plugin-utils'
|
|
32
|
-
* const key = setMod({
|
|
33
|
-
* source: 'myPlugin',
|
|
34
|
-
* endpoints: { "HTMLElement": ['myPlugin.meta.customMethod'] },
|
|
35
|
-
* factory(methodName, ...meta) {
|
|
36
|
-
* return function (...args) { /* `this` is the DOM instance *\/ };
|
|
37
|
-
* }
|
|
38
|
-
* });
|
|
39
|
-
*
|
|
40
|
-
* key.warn('registered'); // "[myPlugin] registered"
|
|
41
|
-
*/
|
|
42
|
-
export default function ({ source, endpoints, factory, aliases = null }) {
|
|
43
|
-
typeof factory !== 'function' && err("factory must be a function")
|
|
44
|
-
|
|
45
|
-
// Non-global Symbol wrapped in Object() so it can carry properties (.throw,
|
|
46
|
-
// .warn, .error) while remaining usable as a Map/window key.
|
|
47
|
-
// Symbol.for is intentionally avoided — each registration must be isolated.
|
|
48
|
-
const key = Object(Symbol(source)), modErr = newErrorHandler(source);
|
|
49
|
-
|
|
50
|
-
// Attach scoped error helpers directly on the key so plugin authors can use
|
|
51
|
-
// them without importing newErrorHandler separately.
|
|
52
|
-
Object.defineProperties(key, propsDesc(modErr, modErr.log, modErr.warn));
|
|
53
|
-
|
|
54
|
-
// Track registration count so Otorp core can warn on duplicate source names
|
|
55
|
-
// (same source registered twice, or two plugins sharing the same identifier).
|
|
56
|
-
const count = (get(source)?.count || 0) + 1
|
|
57
|
-
|
|
58
|
-
set(source, { endpoints, aliases, key, count });
|
|
59
|
-
|
|
60
|
-
// Install factory under the unique key on root. Otorp reads root[key] during
|
|
61
|
-
// its init pass — it never uses Symbol.for to look up plugin factories.
|
|
62
|
-
return (root[key] = factory), key;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Creates a namespaced error handler.
|
|
66
|
-
*
|
|
67
|
-
* Returns a callable that throws an `Error`. The namespace appears in
|
|
68
|
-
* `error.name` so stack traces remain readable.
|
|
69
|
-
* `.warn` and `.log` variants emit to the console instead of throwing.
|
|
70
|
-
*
|
|
71
|
-
* @param {string} [modName] - Namespace prefix written to `error.name`.
|
|
72
|
-
* @returns {((msg: string, options?: { type?: string, cause?: any }) => never) & { warn: Function, log: Function }}
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* const err = newErrorHandler('myPlugin');
|
|
76
|
-
* err('Missing endpoint'); // throws — name: "[myPlugin]"
|
|
77
|
-
* err.log('Deprecated alias used'); // error log — name: "[myPlugin]"
|
|
78
|
-
* err.warn('Init failed', { cause }); // warns — name: "[myPlugin - <cause.name>]"
|
|
79
|
-
*/
|
|
80
|
-
export function newErrorHandler(modName) {
|
|
81
|
-
let [prefix, separator, suffix] = modName ? [`[${modName}`, "-", "]"] : ["", "", ""];
|
|
82
|
-
|
|
83
|
-
/** Builds an Error and delegates to the bound `output` sink. */
|
|
84
|
-
function genErrHandler(msg, { type: errType, cause } = {}) {
|
|
85
|
-
const error = new Error(msg, { cause }), actualType = errType || cause?.name;
|
|
86
|
-
error.name = actualType
|
|
87
|
-
? (`${prefix} ${separator} ${actualType + suffix}`).trim()
|
|
88
|
-
: prefix + suffix;
|
|
89
|
-
// V8-only: removes genErrHandler from the stack trace.
|
|
90
|
-
// Falls back to a no-op on other engines — full trace is kept.
|
|
91
|
-
(Error.captureStackTrace || (x => x))(error, genErrHandler);
|
|
92
|
-
this.output(error)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const myErrHandler = genErrHandler.bind({ output(err) { throw err } })
|
|
96
|
-
myErrHandler.log = genErrHandler.bind({ output(err) { console.error(err) } })
|
|
97
|
-
myErrHandler.warn = genErrHandler.bind({ output(err) { console.warn(err) } })
|
|
98
|
-
return myErrHandler
|
|
99
|
-
}
|
|
100
|
-
|
|
1
|
+
// public
|
|
101
2
|
export const
|
|
102
3
|
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
* Allows bundlers to mangle the identifier across the codebase,
|
|
106
|
-
* reducing bundle size without impacting runtime behavior.
|
|
4
|
+
/** Uglyfiable shorthand for the window object.
|
|
107
5
|
*
|
|
6
|
+
* Make the bundle more concise and lightweight
|
|
7
|
+
*
|
|
108
8
|
* @type {Window}
|
|
109
9
|
*/
|
|
110
10
|
root = window,
|
|
111
11
|
|
|
112
|
-
/** Read or write an entry in
|
|
12
|
+
/** Read or write an entry in otorp namespace.
|
|
113
13
|
*
|
|
114
|
-
* Keys are stored
|
|
115
|
-
*
|
|
116
|
-
*
|
|
14
|
+
* Keys are stored as `Symbol.for('otorp:' + key)`. Pass a truthy `value` to
|
|
15
|
+
* assign and return it; omit `value` or pass a falsy one to read only. Falsy
|
|
16
|
+
* values cannot be stored through this helper.
|
|
117
17
|
*
|
|
118
|
-
* @param {string} key -
|
|
119
|
-
* @param {*} [value] -
|
|
18
|
+
* @param {string} key - Registry name (without the `otorp:` prefix).
|
|
19
|
+
* @param {*} [value] - Value to store. Must be truthy when writing.
|
|
120
20
|
* @returns {*} The stored or newly assigned value.
|
|
121
21
|
*
|
|
122
22
|
* @example
|
|
123
|
-
* ref('modules', new Map()); //
|
|
124
|
-
* ref('modules');
|
|
23
|
+
* const modules = ref('modules', new Map()); // create registry
|
|
24
|
+
* ref('modules'); // → same Map instance
|
|
125
25
|
*/
|
|
126
|
-
ref = (key, value) => value
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
26
|
+
ref = (key, value) => value ? (root[sym(refPrefix + key)] = value) : root[sym(refPrefix + key)],
|
|
27
|
+
|
|
28
|
+
/** Creates a namespaced error logger.
|
|
29
|
+
*
|
|
30
|
+
* Returns a callable that throws an Error with an optional `cause`. The
|
|
31
|
+
* returned function also has `.warn` and `.log` variants that emit warnings or
|
|
32
|
+
* errors to the console instead of throwing.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} [modName] - Optional namespace written to `error.name` (not `message`).
|
|
35
|
+
* @returns {((msg:string, options?:{type?:string, cause?:any})=>never) & { warn:Function, log:Function }}
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const err = newErrorHandler('myPlugin');
|
|
39
|
+
* err('Missing endpoint'); // throws: "[myPlugin] Missing endpoint"
|
|
40
|
+
* err.warn('Deprecated alias used'); // console.warn: "[myPlugin] Deprecated alias used'"
|
|
41
|
+
* err.log('Init failed', { cause }); // console.error => "[myPlugin - <cause>] Missing endpoint"
|
|
42
|
+
*/
|
|
43
|
+
newErrorHandler = (modName) => {
|
|
44
|
+
let [prefix, separator, suffix] = modName ? [`[${modName}`, "-", "]"] : ["", "", ""];
|
|
45
|
+
|
|
46
|
+
/** Builds an Error and delegates to the bound `output` sink. */
|
|
47
|
+
function genErrHandler(msg, { type: errType, cause } = {}) {
|
|
48
|
+
const error = new Error(msg, { cause }), actualType = errType || cause?.name;
|
|
49
|
+
error.name = actualType ? (`${prefix} ${separator} ${actualType + suffix}`).trim() : prefix + suffix;
|
|
50
|
+
// V8 trims the factory from the stack; other engines keep the full trace.
|
|
51
|
+
(Error.captureStackTrace || (x => x))(error, genErrHandler);
|
|
52
|
+
this.output(error)
|
|
53
|
+
}
|
|
54
|
+
const myErrHandler = genErrHandler.bind({ output(err) { throw err } })
|
|
55
|
+
myErrHandler.log = genErrHandler.bind({ output(err) { console.error(err) } })
|
|
56
|
+
myErrHandler.warn = genErrHandler.bind({ output(err) { console.warn(err) } })
|
|
57
|
+
return myErrHandler
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/** Register an Otorp plugin module for the initialization pass.
|
|
61
|
+
*
|
|
62
|
+
* Stores endpoint and aliases metadata in otorp module Map, exposes
|
|
63
|
+
* module-scoped error helpers on the plugin's symbol key, and installs the
|
|
64
|
+
* factory on root` so Otorp can resolve it from the registered `key`.
|
|
65
|
+
*
|
|
66
|
+
* @param {object} config - Plugin registration payload.
|
|
67
|
+
* @param {string} config.source - Unique module identifier used as the Map key.
|
|
68
|
+
* @param {Record<string, string[]>} config.endpoints - Target's name to
|
|
69
|
+
* endpoint query strings (e.g. `{ "HTMLElement": ['myPlugin.customMethod'] }`).
|
|
70
|
+
* @param {Function} config.factory - Factory called during Otorp init; receives
|
|
71
|
+
* the resolved method name and optional metadata segments, and must return
|
|
72
|
+
* the function to install on the prototype.
|
|
73
|
+
* @param {Record<string, string>|null} [config.aliases=null] - Optional alias map.
|
|
74
|
+
* @returns {Object} A unique symbol object (via `Object(Symbol(source))`) with
|
|
75
|
+
* `.throw`, `.warn`, and `.error` handlers attached. Otorp reads the factory
|
|
76
|
+
* from `root[key]`, not from a global `Symbol.for` lookup.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* setMod({
|
|
80
|
+
* source: 'myPlugin',
|
|
81
|
+
* endpoints: { "HTMLElement": ['myPlugin.customMethod'] },
|
|
82
|
+
* factory(methodName) {
|
|
83
|
+
* return function (...args) { /* `this` is the DOM instance *\/ };
|
|
84
|
+
* }
|
|
85
|
+
* });
|
|
86
|
+
*/
|
|
87
|
+
setMod = ({ source, endpoints, factory, aliases = null }) => {
|
|
88
|
+
typeof factory !== 'function' && err("factory must be a function")
|
|
89
|
+
|
|
90
|
+
// Unique per call — Symbol(source), not Symbol.for. Otorp resolves the
|
|
91
|
+
// factory through the Map entry's `key`, keeping plugins isolated.
|
|
92
|
+
const key = Object(Symbol(source)), modErr = newErrorHandler(source);
|
|
93
|
+
|
|
94
|
+
// Attach throw / warn / log helpers to the symbol object itself.
|
|
95
|
+
Object.defineProperties(key, {
|
|
96
|
+
'throw': newDescritor(modErr),
|
|
97
|
+
'error': newDescritor(modErr.log),
|
|
98
|
+
'warn': newDescritor(modErr.warn),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const count = (get(source)?.count || 0) + 1
|
|
102
|
+
|
|
103
|
+
set(source, {
|
|
104
|
+
endpoints,
|
|
105
|
+
aliases,
|
|
106
|
+
key,
|
|
107
|
+
count
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return (root[key] = factory), key;
|
|
111
|
+
};
|
|
143
112
|
|
|
113
|
+
// private
|
|
144
114
|
const
|
|
145
115
|
/** Error handler for this package's own validation and setup failures. */
|
|
146
116
|
err = newErrorHandler('@otorp/plugin-utils'),
|
|
147
117
|
|
|
148
|
-
/**
|
|
118
|
+
/** Shorthand for `Symbol.for` to reduce resolution/lookup at each `ref` invocation. */
|
|
149
119
|
sym = Symbol.for,
|
|
150
120
|
|
|
151
|
-
/**
|
|
121
|
+
/** Otorp namespace. Used as prefix in the ref function */
|
|
152
122
|
refPrefix = 'otorp:',
|
|
153
123
|
|
|
154
|
-
/** Bound Map#
|
|
155
|
-
|
|
156
|
-
* The Map is retrieved (or created) once at module evaluation time.
|
|
157
|
-
* Methods are bound directly to avoid prototype lookups on every registration.
|
|
158
|
-
*/
|
|
159
|
-
[get, set] = (map => [map.get.bind(map), map.set.bind(map)])(
|
|
160
|
-
ref('modules') || ref('modules', new Map)
|
|
161
|
-
),
|
|
124
|
+
/** Bound `Map#set` to reduce resolution/lookup during plugins registration. */
|
|
125
|
+
[get, set] = (map => [map.get.bind(map), map.set.bind(map)])(ref('modules') || ref('modules', new Map));
|
|
162
126
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
127
|
+
/**
|
|
128
|
+
* @type {{ value: any, writable: boolean, configurable: boolean }}
|
|
129
|
+
*/
|
|
130
|
+
const descriptorDummy = Object.create(null)
|
|
131
|
+
descriptorDummy.writable = descriptorDummy.configurable = false
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
*
|
|
135
|
+
* @param {any} val
|
|
136
|
+
*/
|
|
137
|
+
export function newDescritor(val) {
|
|
138
|
+
return descriptorDummy.value = val, descriptorDummy
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otorp/plugin-utils",
|
|
3
|
-
"version": "1.0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
".": "./index.js"
|
|
7
|
-
},
|
|
3
|
+
"version": "1.0.1-0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "main.js",
|
|
8
6
|
"type": "module",
|
|
9
7
|
"files": [
|
|
10
8
|
"index.js"
|
|
11
9
|
],
|
|
12
10
|
"keywords": [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"plugin",
|
|
16
|
-
"utilities",
|
|
17
|
-
"error-handler",
|
|
18
|
-
"registry"
|
|
11
|
+
"JavaScript",
|
|
12
|
+
"Otorp"
|
|
19
13
|
],
|
|
20
14
|
"homepage": "https://gitlab.com/otorp/plugin-utils/-/wikis/home",
|
|
21
15
|
"bugs": {
|
|
22
16
|
"url": "https://gitlab.com/otorp/plugin-utils/-/issues"
|
|
23
17
|
},
|
|
24
|
-
"author": "
|
|
18
|
+
"author": "TDJ.dev",
|
|
25
19
|
"license": "MIT",
|
|
26
20
|
"repository": {
|
|
27
21
|
"type": "git",
|