@arkstack/foundry 0.19.6 → 0.20.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
@@ -16,24 +16,32 @@ pnpm add @arkstack/foundry
16
16
 
17
17
  **`src/Hook.ts`**
18
18
 
19
- A global, named hook registry for extending Arkstack internals without modifying core code. Hooks are keyed by name and support positional slots (`before`, `after`, or any custom string).
19
+ A global, named hook registry for extending Arkstack internals without modifying core code. Hooks are keyed by name and support positional slots (`before`, `after`, or any custom string). There is one registry per process: `@arkstack/common` re-exports this same `Hook`.
20
20
 
21
- #### `Hook.set(name, hook)`
21
+ #### `Hook.on(name, positions)`
22
22
 
23
- Registers a hook. Multiple calls for the same name are merged.
23
+ Registers callbacks for a hook's positions. Registering the same name again merges: only the positions supplied are added or replaced.
24
24
 
25
25
  ```ts
26
26
  import { Hook } from '@arkstack/foundry'
27
27
 
28
- Hook.set('request:handle', {
28
+ Hook.on('request:handle', {
29
29
  before: (ctx) => console.log('before handler'),
30
30
  after: (ctx) => console.log('after handler'),
31
31
  })
32
32
  ```
33
33
 
34
+ #### `Hook.emit(name, pos, ...args)`
35
+
36
+ Calls a position with the given arguments and returns its result: a Promise for an async hook, so it can be awaited. When nothing is registered there it does nothing and returns `undefined`, so callers need no `has()` check.
37
+
38
+ ```ts
39
+ await Hook.emit('request:handle', 'before', ctx)
40
+ ```
41
+
34
42
  #### `Hook.get(name, pos?)`
35
43
 
36
- Retrieves the full hook object or a specific positional handler.
44
+ Retrieves the full hook object or one position's callback, without calling it.
37
45
 
38
46
  ```ts
39
47
  const hook = Hook.get('request:handle') // IHook | undefined
@@ -49,25 +57,29 @@ Hook.has('request:handle') // true | false
49
57
  Hook.has('request:handle', 'after') // true | false
50
58
  ```
51
59
 
52
- #### `Hook.unset(name?, pos?)`
60
+ #### `Hook.off(name?, pos?)`
53
61
 
54
- Removes a hook or a single positional handler. If the hook becomes empty after removal, it is deleted entirely. Called with no arguments, it delegates to `Hook.clear()`.
62
+ Removes a hook or a single position. A hook left with no positions is deleted. Called with no arguments it clears every hook; a name that isn't registered is a no-op.
55
63
 
56
64
  ```ts
57
- Hook.unset('request:handle', 'before') // removes only the 'before' handler
58
- Hook.unset('request:handle') // removes the entire hook
59
- Hook.unset() // clears all hooks
65
+ Hook.off('request:handle', 'before') // removes only the 'before' callback
66
+ Hook.off('request:handle') // removes the entire hook
67
+ Hook.off() // clears all hooks
60
68
  ```
61
69
 
62
- #### `Hook.getAll()`
70
+ #### `Hook.all()`
63
71
 
64
72
  Returns all registered hooks as a plain record.
65
73
 
66
74
  ```ts
67
- const hooks = Hook.getAll()
75
+ const hooks = Hook.all()
68
76
  // { 'request:handle': { before: fn, after: fn } }
69
77
  ```
70
78
 
71
79
  #### `Hook.clear()`
72
80
 
73
81
  Clears all registered hooks.
82
+
83
+ #### Deprecated aliases
84
+
85
+ `set()` → `on()`, `unset()` → `off()`, `getAll()` → `all()`. `get(name, pos, ...args)` with arguments still calls the hook, but use `emit()`; `get()` should only retrieve.
package/dist/index.d.ts CHANGED
@@ -7,23 +7,44 @@ type HookFor<N extends string> = N extends keyof HookRegistry ? HookRegistry[N]
7
7
  type HookPos<N extends string, P extends string> = N extends keyof HookRegistry ? P extends keyof HookRegistry[N] ? HookRegistry[N][P] : (...args: any[]) => void : (...args: any[]) => void;
8
8
  type HookPositions<N extends string> = N extends keyof HookRegistry ? keyof HookRegistry[N] : Position;
9
9
  type HookArgs<N extends string, P extends string> = N extends keyof HookRegistry ? P extends keyof HookRegistry[N] ? HookRegistry[N][P] extends ((...args: infer A) => any) ? A : any[] : any[] : any[];
10
+ type HookResult<N extends string, P extends string> = HookPos<N, P> extends ((...args: any[]) => infer R) ? R : unknown;
10
11
  //#endregion
11
12
  //#region src/Hook.d.ts
12
13
  declare class Hook {
13
14
  private static hooks;
14
15
  /**
15
- * Hooks define code that should run within defined boundaries to add extra functionalities.
16
+ * Register callbacks for a hook's positions. Registering the same name again
17
+ * merges: only the positions supplied are added or replaced.
16
18
  *
17
19
  * @param name
18
- * @param value
20
+ * @param hook
19
21
  */
20
- static set<N extends HookName>(name: N | (string & {}), hook: HookFor<N>): void;
22
+ static on<N extends HookName>(name: N | (string & {}), hook: Partial<HookFor<N>>): void;
21
23
  /**
22
- * Check if a hook is defined by name
24
+ * @deprecated Use {@link Hook.on}; `set` merges rather than replaces.
23
25
  *
24
26
  * @param name
27
+ * @param hook
28
+ */
29
+ static set<N extends HookName>(name: N | (string & {}), hook: Partial<HookFor<N>>): void;
30
+ /**
31
+ * Check if a hook is defined by name, or a position of it.
32
+ *
33
+ * @param name
34
+ * @param pos
25
35
  */
26
36
  static has<N extends HookName, P extends HookPositions<N>>(name: N | (string & {}), pos?: P): boolean;
37
+ /**
38
+ * Run a hook position with the given arguments and return its result (a
39
+ * Promise for an async hook, so it can be awaited). A no-op returning
40
+ * `undefined` when nothing is registered there, so call sites need no
41
+ * {@link Hook.has} check.
42
+ *
43
+ * @param name
44
+ * @param pos
45
+ * @param args
46
+ */
47
+ static emit<N extends HookName, P extends HookPositions<N>>(name: N, pos: P, ...args: HookArgs<N, P & string>): HookResult<N, P & string> | undefined;
27
48
  /**
28
49
  * Retrieve a defined hook by name
29
50
  *
@@ -31,35 +52,46 @@ declare class Hook {
31
52
  */
32
53
  static get<N extends HookName>(name: N): HookFor<N> | undefined;
33
54
  /**
34
- * Retrieve a defined hook by name and position
55
+ * Retrieve a defined hook's position by name and position
35
56
  *
36
57
  * @param name
37
58
  * @param pos
38
59
  */
39
- static get<N extends HookName, P extends HookPositions<N>>(name: N, pos: P): HookPos<N, P> | undefined;
60
+ static get<N extends HookName, P extends HookPositions<N>>(name: N, pos: P): HookPos<N, P & string> | undefined;
40
61
  /**
41
- * Retrieve a defined hook by name and position the set args for callback
62
+ * @deprecated Use {@link Hook.emit} to call a hook; `get` with arguments calls it.
42
63
  *
43
64
  * @param name
44
65
  * @param pos
66
+ * @param args
45
67
  */
46
- static get<N extends HookName, P extends HookPositions<N>>(name: N, pos: P, ...args: HookArgs<N, P>): void;
68
+ static get<N extends HookName, P extends HookPositions<N>>(name: N, pos: P, ...args: HookArgs<N, P & string>): void;
47
69
  /**
48
70
  * Retrieve all defined hooks
71
+ */
72
+ static all: () => Record<string, IHook> & HookRegistry;
73
+ /**
74
+ * @deprecated Use {@link Hook.all}.
75
+ */
76
+ static getAll: () => Record<string, IHook> & HookRegistry;
77
+ /**
78
+ * Remove a hook, or one position of it. With no name, removes every hook.
49
79
  *
50
80
  * @param name
81
+ * @param pos
51
82
  */
52
- static getAll: () => Record<string, IHook> & HookRegistry;
83
+ static off<N extends HookName, P extends HookPositions<N>>(name?: N | (string & {}), pos?: P): undefined;
53
84
  /**
54
- * Remove the defined hook
85
+ * @deprecated Use {@link Hook.off}.
55
86
  *
56
87
  * @param name
88
+ * @param pos
57
89
  */
58
- static unset<N extends HookName, P extends HookPositions<N>>(name?: N | (string & {}), pos?: P): undefined;
90
+ static unset<N extends HookName, P extends HookPositions<N>>(name?: N | (string & {}), pos?: P): void;
59
91
  /**
60
92
  * Clear all the defined hooks
61
93
  */
62
94
  static clear: () => void;
63
95
  }
64
96
  //#endregion
65
- export { Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, IHook };
97
+ export { Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, HookResult, IHook };
package/dist/index.js CHANGED
@@ -2,12 +2,13 @@
2
2
  var Hook = class {
3
3
  static hooks = /* @__PURE__ */ new Map();
4
4
  /**
5
- * Hooks define code that should run within defined boundaries to add extra functionalities.
5
+ * Register callbacks for a hook's positions. Registering the same name again
6
+ * merges: only the positions supplied are added or replaced.
6
7
  *
7
8
  * @param name
8
- * @param value
9
+ * @param hook
9
10
  */
10
- static set(name, hook) {
11
+ static on(name, hook) {
11
12
  const oldhook = this.hooks.get(name) ?? {};
12
13
  this.hooks.set(name, {
13
14
  ...oldhook,
@@ -15,48 +16,82 @@ var Hook = class {
15
16
  });
16
17
  }
17
18
  /**
18
- * Check if a hook is defined by name
19
+ * @deprecated Use {@link Hook.on}; `set` merges rather than replaces.
20
+ *
21
+ * @param name
22
+ * @param hook
23
+ */
24
+ static set(name, hook) {
25
+ this.on(name, hook);
26
+ }
27
+ /**
28
+ * Check if a hook is defined by name, or a position of it.
19
29
  *
20
30
  * @param name
31
+ * @param pos
21
32
  */
22
33
  static has(name, pos) {
23
- if (pos && this.hooks.has(name)) return Boolean(this.get(name, pos));
34
+ if (pos && this.hooks.has(name)) return Boolean(this.hooks.get(name)?.[pos]);
24
35
  return this.hooks.has(name);
25
36
  }
37
+ /**
38
+ * Run a hook position with the given arguments and return its result (a
39
+ * Promise for an async hook, so it can be awaited). A no-op returning
40
+ * `undefined` when nothing is registered there, so call sites need no
41
+ * {@link Hook.has} check.
42
+ *
43
+ * @param name
44
+ * @param pos
45
+ * @param args
46
+ */
47
+ static emit(name, pos, ...args) {
48
+ const fn = this.hooks.get(name)?.[pos];
49
+ return fn ? fn(...args) : void 0;
50
+ }
26
51
  static get(name, pos, ...args) {
27
52
  const hook = this.hooks.get(name);
28
53
  if (!hook) return void 0;
29
54
  if (pos === void 0) return hook;
30
- const fn = hook[pos];
31
- if (!fn) return void 0;
32
- if (args.length > 0) return fn(...args);
33
- return fn;
55
+ if (args.length > 0) return void this.emit(name, pos, ...args);
56
+ return hook[pos];
34
57
  }
35
58
  /**
36
59
  * Retrieve all defined hooks
37
- *
38
- * @param name
39
60
  */
40
- static getAll = () => {
61
+ static all = () => {
41
62
  const hooks = {};
42
63
  for (const [name, value] of this.hooks) hooks[name] = value;
43
64
  return hooks;
44
65
  };
45
66
  /**
46
- * Remove the defined hook
67
+ * @deprecated Use {@link Hook.all}.
68
+ */
69
+ static getAll = () => this.all();
70
+ /**
71
+ * Remove a hook, or one position of it. With no name, removes every hook.
47
72
  *
48
73
  * @param name
74
+ * @param pos
49
75
  */
50
- static unset(name, pos) {
76
+ static off(name, pos) {
51
77
  if (name && this.hooks.has(name)) {
52
78
  if (pos !== void 0) {
53
- const hook = this.get(name);
79
+ const hook = this.hooks.get(name);
54
80
  if (hook?.[pos]) delete hook[pos];
55
81
  Object.keys(hook ?? {}).length < 1 && this.hooks.delete(name);
56
82
  return;
57
83
  }
58
84
  this.hooks.delete(name);
59
- } else this.clear();
85
+ } else if (!name) this.clear();
86
+ }
87
+ /**
88
+ * @deprecated Use {@link Hook.off}.
89
+ *
90
+ * @param name
91
+ * @param pos
92
+ */
93
+ static unset(name, pos) {
94
+ this.off(name, pos);
60
95
  }
61
96
  /**
62
97
  * Clear all the defined hooks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/foundry",
3
- "version": "0.19.6",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Extensibility primitives and framework internals for the Arkstack ecosystem.",
6
6
  "homepage": "https://arkstack.toneflix.net",