@plurnk/plurnk-hooks 1.24.0 → 1.25.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/.env.defaults +5 -1
- package/README.md +20 -13
- package/SPEC.md +42 -23
- package/dist/Module.d.ts +2 -6
- package/dist/Module.d.ts.map +1 -1
- package/dist/Module.js +69 -26
- package/dist/Module.js.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -29
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/.env.defaults
CHANGED
|
@@ -7,5 +7,9 @@
|
|
|
7
7
|
# PLURNK_HOOKS_ARGS=["/absolute/path/to/plurnk-hook.mjs"]
|
|
8
8
|
# Selected events, comma-separated.
|
|
9
9
|
# PLURNK_HOOKS_EVENTS=loop/terminated,loop/proposal,notice/event
|
|
10
|
-
# Delivery deadline,
|
|
10
|
+
# Delivery deadline from admission, including queue time; expiry kills the command, not the loop.
|
|
11
11
|
PLURNK_HOOKS_TIMEOUT_MS=30000
|
|
12
|
+
# Maximum simultaneous commands; 1 preserves delivery completion order.
|
|
13
|
+
PLURNK_HOOKS_CONCURRENCY=1
|
|
14
|
+
# Waiting events beyond running commands; excess events are reported, not silently dropped.
|
|
15
|
+
PLURNK_HOOKS_QUEUE_LIMIT=64
|
package/README.md
CHANGED
|
@@ -16,18 +16,10 @@ PLURNK_HOOKS_ARGS=["/absolute/path/to/plurnk-hook.mjs"]
|
|
|
16
16
|
PLURNK_HOOKS_EVENTS=loop/terminated,loop/proposal,notice/event
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
`PLURNK_HOOKS_ARGS` is a JSON string array.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
log/entry
|
|
24
|
-
loop/proposal
|
|
25
|
-
loop/terminated
|
|
26
|
-
notice/event
|
|
27
|
-
stream/concluded
|
|
28
|
-
stream/event
|
|
29
|
-
workspace/created
|
|
30
|
-
```
|
|
19
|
+
`PLURNK_HOOKS_ARGS` is a JSON string array. Select exact event names from the
|
|
20
|
+
[core event reference](../plurnk-core/SPEC.md#notifications-core-events),
|
|
21
|
+
such as `loop/terminated`, `loop/interaction`, or `workspace/preparation`.
|
|
22
|
+
There is no hook-specific event vocabulary or wildcard syntax.
|
|
31
23
|
|
|
32
24
|
Each process receives one line such as:
|
|
33
25
|
|
|
@@ -42,6 +34,12 @@ failure is reported to daemon diagnostics without changing loop control flow.
|
|
|
42
34
|
Event payloads retain the core contract and may contain project or model
|
|
43
35
|
content; treat the command and any downstream sink as trusted.
|
|
44
36
|
|
|
37
|
+
By default, commands run one at a time with up to 64 waiting events and a
|
|
38
|
+
30-second deadline from admission. Queue overflow and deadline expiry are
|
|
39
|
+
reported; neither blocks the agent. Adjust `PLURNK_HOOKS_CONCURRENCY`,
|
|
40
|
+
`PLURNK_HOOKS_QUEUE_LIMIT`, and `PLURNK_HOOKS_TIMEOUT_MS` through the same
|
|
41
|
+
environment cascade. Delivery is best-effort, without retries or replay.
|
|
42
|
+
|
|
45
43
|
## Test one hook
|
|
46
44
|
|
|
47
45
|
Save this as the script path named in `PLURNK_HOOKS_ARGS`:
|
|
@@ -61,6 +59,15 @@ printf '%s\n' '{"workspaceId":42,"method":"loop/terminated","params":{}}' \
|
|
|
61
59
|
| /usr/bin/node /absolute/path/to/plurnk-hook.mjs
|
|
62
60
|
```
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
## Plugin or external tool?
|
|
63
|
+
|
|
64
|
+
An ordinary executable needs only the configuration above. An installed daemon
|
|
65
|
+
module can instead use `ApplicationPort.subscribeToEvents` directly. Both
|
|
66
|
+
observe the same core events. Producer modules implement `stop()` to refuse
|
|
67
|
+
new work and settle existing work; observers unsubscribe in `close()`, after
|
|
68
|
+
producer settlement. See the
|
|
69
|
+
[module contract](../plurnk-core/SPEC.md#module-lifecycle-module-lifecycle-and-setup-seam).
|
|
70
|
+
|
|
71
|
+
The complete delivery contract lives in
|
|
65
72
|
[`SPEC.md`](./SPEC.md). Portable defaults live in
|
|
66
73
|
[`.env.defaults`](./.env.defaults).
|
package/SPEC.md
CHANGED
|
@@ -2,46 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
## §hooks-command-delivery Exact command delivery
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
argument array and `shell: false
|
|
5
|
+
An admitted selected event is delivered to the configured executable with its
|
|
6
|
+
argument array and `shell: false`, subject to {§hooks-bounded-delivery}. The child receives one complete
|
|
7
7
|
`{ workspaceId, method, params }` JSON envelope followed by a newline on stdin.
|
|
8
8
|
The core-supplied workspace scope and payload pass through without inferred
|
|
9
9
|
coordinates or payload translation. The child inherits the daemon's working
|
|
10
|
-
directory and
|
|
11
|
-
daemon's streams.
|
|
10
|
+
directory and resolved daemon environment captured at module construction;
|
|
11
|
+
its standard output and error remain visible in the daemon's streams. Event
|
|
12
|
+
data is serialized at publication, never read later from a mutable payload.
|
|
12
13
|
|
|
13
14
|
## §hooks-selection Event selection
|
|
14
15
|
|
|
15
|
-
`PLURNK_HOOKS_EVENTS`
|
|
16
|
-
|
|
17
|
-
language.
|
|
16
|
+
`PLURNK_HOOKS_EVENTS` selects exact comma-separated event names from
|
|
17
|
+
{§notifications}. Hooks do not maintain a second closed inventory, translate
|
|
18
|
+
names, infer lifecycle transitions, or implement a wildcard/filter language.
|
|
19
|
+
A selected name with no corresponding publication causes no delivery.
|
|
18
20
|
|
|
19
|
-
|
|
|
21
|
+
| Consumer | Integration |
|
|
20
22
|
|---|---|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
|
23
|
+
| Installed module | `ApplicationPort.subscribeToEvents`, under {§module-lifecycle} |
|
|
24
|
+
| Ordinary executable or script | This package's JSON-stdin adapter; no plugin manifest required |
|
|
25
|
+
| Client | The client-interface protocol's projection; not a raw event-bus subscription |
|
|
26
|
+
|
|
27
|
+
## §hooks-bounded-delivery Bounded delivery
|
|
28
|
+
|
|
29
|
+
| Boundary | Behavior |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Admission | Start immediately when a command slot is free; otherwise queue FIFO up to the configured waiting-event limit. Reject excess events with a diagnostic. |
|
|
32
|
+
| Concurrency | At most `PLURNK_HOOKS_CONCURRENCY` commands at once. A value of `1` preserves completion order; larger values permit overlapping completion. |
|
|
33
|
+
| Deadline | `PLURNK_HOOKS_TIMEOUT_MS` starts at admission, including queue time. Expired queued events are reported without execution; a running executable is killed on expiry. |
|
|
34
|
+
| Failure | At most one command attempt per admitted event; no retry, replay, or durable queue. |
|
|
35
|
+
| Shutdown | Remain subscribed through producer settlement under {§module-shutdown-order}; unsubscribe in `close()`, then drain already admitted deliveries. Repeated close joins the same drain. |
|
|
36
|
+
|
|
37
|
+
The daemon's absolute shutdown deadline also bounds hook drainage. Forced
|
|
38
|
+
shutdown can lose notifications. A command owns any subprocesses it creates;
|
|
39
|
+
this adapter is not a process-tree supervisor.
|
|
28
40
|
|
|
29
41
|
## §hooks-failure-isolation Failure isolation
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
Typed configuration errors refuse module construction; the host contains them
|
|
44
|
+
according to {§configuration-repair-path}. Event dispatch never awaits
|
|
45
|
+
command completion. Serialization, spawn, stdin, nonzero-exit, signal, and timeout failures are
|
|
33
46
|
reported to daemon diagnostics and cannot alter loop state or event dispatch.
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
Command output and exit status cannot veto or rewrite an operation. Child
|
|
48
|
+
ownership lasts until process closure, including after a broken stdin. A failed
|
|
49
|
+
diagnostic reporter preserves both errors in the fallback diagnostic.
|
|
36
50
|
|
|
37
51
|
## §hooks-config Operator configuration
|
|
38
52
|
|
|
39
53
|
| Variable | Contract |
|
|
40
54
|
|---|---|
|
|
41
55
|
| `PLURNK_HOOKS_COMMAND` | One executable; absent disables hooks |
|
|
42
|
-
| `PLURNK_HOOKS_ARGS` | Optional JSON string array; invalid JSON or non-string members
|
|
56
|
+
| `PLURNK_HOOKS_ARGS` | Optional JSON string array; invalid JSON or non-string members refuse hook configuration |
|
|
43
57
|
| `PLURNK_HOOKS_EVENTS` | Required with a command; explicit comma-separated names from {§hooks-selection} |
|
|
44
|
-
| `PLURNK_HOOKS_TIMEOUT_MS` |
|
|
58
|
+
| `PLURNK_HOOKS_TIMEOUT_MS` | Positive integer delivery deadline including queue time |
|
|
59
|
+
| `PLURNK_HOOKS_CONCURRENCY` | Positive integer simultaneous-command limit |
|
|
60
|
+
| `PLURNK_HOOKS_QUEUE_LIMIT` | Nonnegative integer waiting-event limit; `0` disables queueing |
|
|
45
61
|
|
|
46
|
-
Arguments or events without a command,
|
|
47
|
-
|
|
62
|
+
Arguments or events without a command, malformed or duplicate event names,
|
|
63
|
+
malformed arguments, and invalid bounds fail at this module boundary. The
|
|
64
|
+
command is an executable name or path, including paths containing spaces;
|
|
65
|
+
it is never split or interpreted as shell text. Choices come from the assembled
|
|
66
|
+
cascading daemon environment; workspace and model inputs cannot configure hooks.
|
package/dist/Module.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
subscribeToEvents(handler: (workspaceId: number | null, method: string, params: unknown) => void): () => void;
|
|
3
|
-
}
|
|
1
|
+
import type { ApplicationPort } from "@plurnk/plurnk-contracts";
|
|
4
2
|
export interface ModuleOptions {
|
|
5
|
-
readonly env?: NodeJS.ProcessEnv;
|
|
6
3
|
readonly report?: (message: string, cause: unknown) => void;
|
|
7
4
|
}
|
|
8
5
|
export default class Module {
|
|
9
6
|
#private;
|
|
10
7
|
static init(options?: ModuleOptions): Module;
|
|
11
8
|
private constructor();
|
|
12
|
-
start(seam:
|
|
9
|
+
start(seam: Pick<ApplicationPort, "subscribeToEvents">): void;
|
|
13
10
|
close(): Promise<void>;
|
|
14
11
|
}
|
|
15
|
-
export {};
|
|
16
12
|
//# sourceMappingURL=Module.d.ts.map
|
package/dist/Module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Module.d.ts","sourceRoot":"","sources":["../src/Module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Module.d.ts","sourceRoot":"","sources":["../src/Module.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAShE,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IAUvB,MAAM,CAAC,IAAI,CAAC,OAAO,GAAE,aAAkB,GAAG,MAAM,CAM/C;IAED,OAAO,eAIN;IAED,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,mBAAmB,CAAC,GAAG,IAAI,CAe5D;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAKrB;CA2DJ"}
|
package/dist/Module.js
CHANGED
|
@@ -2,67 +2,110 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { hookConfig } from "./config.js";
|
|
3
3
|
export default class Module {
|
|
4
4
|
#config;
|
|
5
|
+
#environment;
|
|
5
6
|
#report;
|
|
6
7
|
#active = new Set();
|
|
8
|
+
#queued = [];
|
|
7
9
|
#unsubscribe = null;
|
|
8
10
|
#started = false;
|
|
11
|
+
#closing = null;
|
|
9
12
|
static init(options = {}) {
|
|
10
|
-
return new Module(hookConfig(
|
|
13
|
+
return new Module(hookConfig(), { ...process.env }, options.report ?? ((message, cause) => { console.error(`${message}:`, cause); }));
|
|
11
14
|
}
|
|
12
|
-
constructor(config, report) {
|
|
15
|
+
constructor(config, environment, report) {
|
|
13
16
|
this.#config = config;
|
|
17
|
+
this.#environment = environment;
|
|
14
18
|
this.#report = report;
|
|
15
19
|
}
|
|
16
20
|
start(seam) {
|
|
17
|
-
if (this.#started)
|
|
18
|
-
throw new Error("hooks module
|
|
21
|
+
if (this.#started || this.#closing !== null)
|
|
22
|
+
throw new Error("hooks module cannot be started again");
|
|
19
23
|
this.#started = true;
|
|
20
24
|
if (this.#config === null)
|
|
21
25
|
return;
|
|
22
26
|
this.#unsubscribe = seam.subscribeToEvents((workspaceId, method, params) => {
|
|
23
27
|
if (!this.#config?.events.has(method))
|
|
24
28
|
return;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.#active.add(observed);
|
|
29
|
+
if (this.#active.size >= this.#config.concurrency && this.#queued.length >= this.#config.queueLimit) {
|
|
30
|
+
this.#failed(method, new Error("Hook delivery queue is full; event not delivered."));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
this.#queued.push({ method, input: `${JSON.stringify({ workspaceId, method, params })}\n`, deadline: Date.now() + this.#config.timeoutMs });
|
|
35
|
+
this.#pump();
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
this.#failed(method, cause);
|
|
39
|
+
}
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
|
-
|
|
42
|
+
close() {
|
|
40
43
|
this.#unsubscribe?.();
|
|
41
44
|
this.#unsubscribe = null;
|
|
42
|
-
|
|
45
|
+
this.#closing ??= this.#drain();
|
|
46
|
+
return this.#closing;
|
|
47
|
+
}
|
|
48
|
+
async #drain() {
|
|
49
|
+
while (this.#active.size > 0)
|
|
50
|
+
await Promise.all(this.#active);
|
|
43
51
|
}
|
|
44
|
-
#
|
|
52
|
+
#failed(method, cause) {
|
|
53
|
+
try {
|
|
54
|
+
this.#report(`hook command failed for ${method}`, cause);
|
|
55
|
+
}
|
|
56
|
+
catch (reportCause) {
|
|
57
|
+
console.error(`hook failure reporter failed for ${method}:`, new AggregateError([cause, reportCause]));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
#pump() {
|
|
45
61
|
const config = this.#config;
|
|
46
62
|
if (config === null)
|
|
47
|
-
return
|
|
48
|
-
|
|
63
|
+
return;
|
|
64
|
+
while (this.#queued.length > 0 && this.#active.size < config.concurrency) {
|
|
65
|
+
const delivery = this.#queued.shift();
|
|
66
|
+
const remaining = delivery.deadline - Date.now();
|
|
67
|
+
if (remaining <= 0) {
|
|
68
|
+
this.#failed(delivery.method, new Error("Hook delivery deadline expired in queue; event not delivered."));
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
let observed;
|
|
72
|
+
observed = this.#deliver(delivery.input, remaining)
|
|
73
|
+
.catch((cause) => { this.#failed(delivery.method, cause); })
|
|
74
|
+
.finally(() => { this.#active.delete(observed); this.#pump(); });
|
|
75
|
+
this.#active.add(observed);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async #deliver(input, timeoutMs) {
|
|
79
|
+
const config = this.#config;
|
|
80
|
+
if (config === null)
|
|
81
|
+
return;
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
const failures = [];
|
|
49
84
|
const child = spawn(config.command, config.args, {
|
|
50
85
|
shell: false,
|
|
86
|
+
env: this.#environment,
|
|
51
87
|
stdio: ["pipe", "inherit", "inherit"],
|
|
52
|
-
signal: AbortSignal.timeout(
|
|
88
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
53
89
|
killSignal: "SIGKILL",
|
|
54
90
|
});
|
|
55
|
-
child.once("error",
|
|
91
|
+
child.once("error", (cause) => { failures.push(cause); });
|
|
56
92
|
child.once("close", (code, signal) => {
|
|
57
|
-
if (
|
|
93
|
+
if (failures.length > 1)
|
|
94
|
+
reject(new AggregateError(failures, "Hook delivery failed."));
|
|
95
|
+
else if (failures.length === 1)
|
|
96
|
+
reject(failures[0]);
|
|
97
|
+
else if (code === 0)
|
|
58
98
|
resolve();
|
|
59
99
|
else
|
|
60
100
|
reject(new Error(signal === null
|
|
61
101
|
? `hook command exited with status ${String(code)}`
|
|
62
102
|
: `hook command exited on ${signal}`));
|
|
63
103
|
});
|
|
64
|
-
child.stdin.once("error",
|
|
65
|
-
|
|
104
|
+
child.stdin.once("error", (cause) => {
|
|
105
|
+
failures.push(cause);
|
|
106
|
+
child.kill("SIGKILL");
|
|
107
|
+
});
|
|
108
|
+
child.stdin.end(input, "utf8");
|
|
66
109
|
});
|
|
67
110
|
}
|
|
68
111
|
}
|
package/dist/Module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Module.js","sourceRoot":"","sources":["../src/Module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Module.js","sourceRoot":"","sources":["../src/Module.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export declare const HOOK_EVENTS: readonly ["log/entry", "loop/proposal", "loop/terminated", "notice/event", "stream/concluded", "stream/event", "workspace/created"];
|
|
2
|
-
export type HookEvent = typeof HOOK_EVENTS[number];
|
|
3
1
|
export interface HookConfig {
|
|
4
2
|
readonly command: string;
|
|
5
3
|
readonly args: string[];
|
|
6
|
-
readonly events: ReadonlySet<
|
|
4
|
+
readonly events: ReadonlySet<string>;
|
|
7
5
|
readonly timeoutMs: number;
|
|
6
|
+
readonly concurrency: number;
|
|
7
|
+
readonly queueLimit: number;
|
|
8
8
|
}
|
|
9
|
-
export declare const hookConfig: (
|
|
9
|
+
export declare const hookConfig: () => HookConfig | null;
|
|
10
10
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AA+BD,eAAO,MAAM,UAAU,QAAO,UAAU,GAAG,IAuB1C,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
"log/entry",
|
|
3
|
-
"loop/proposal",
|
|
4
|
-
"loop/terminated",
|
|
5
|
-
"notice/event",
|
|
6
|
-
"stream/concluded",
|
|
7
|
-
"stream/event",
|
|
8
|
-
"workspace/created",
|
|
9
|
-
];
|
|
1
|
+
import { ConfigurationError, Knob } from "@plurnk/plurnk-meta";
|
|
10
2
|
const hookArgs = (raw) => {
|
|
11
3
|
if (raw === undefined || raw.length === 0)
|
|
12
4
|
return [];
|
|
@@ -15,54 +7,48 @@ const hookArgs = (raw) => {
|
|
|
15
7
|
parsed = JSON.parse(raw);
|
|
16
8
|
}
|
|
17
9
|
catch (cause) {
|
|
18
|
-
throw new
|
|
10
|
+
throw new ConfigurationError("PLURNK_HOOKS_ARGS", "PLURNK_HOOKS_ARGS must be a JSON array of strings.", { cause });
|
|
19
11
|
}
|
|
20
12
|
if (!Array.isArray(parsed) || !parsed.every((value) => typeof value === "string")) {
|
|
21
|
-
throw new
|
|
13
|
+
throw new ConfigurationError("PLURNK_HOOKS_ARGS", "PLURNK_HOOKS_ARGS must be a JSON array of strings.");
|
|
22
14
|
}
|
|
23
15
|
return parsed;
|
|
24
16
|
};
|
|
25
17
|
const hookEvents = (raw) => {
|
|
26
18
|
if (raw === undefined || raw.trim().length === 0) {
|
|
27
|
-
throw new
|
|
19
|
+
throw new ConfigurationError("PLURNK_HOOKS_EVENTS", "PLURNK_HOOKS_EVENTS must select at least one event.");
|
|
28
20
|
}
|
|
29
|
-
const known = new Set(HOOK_EVENTS);
|
|
30
21
|
const selected = new Set();
|
|
31
22
|
for (const event of raw.split(",").map((value) => value.trim())) {
|
|
32
|
-
if (
|
|
33
|
-
throw new
|
|
23
|
+
if (!/^[^\s,/*]+(?:\/[^\s,/*]+)+$/u.test(event))
|
|
24
|
+
throw new ConfigurationError("PLURNK_HOOKS_EVENTS", `PLURNK_HOOKS_EVENTS requires exact event names; got '${event}'.`);
|
|
34
25
|
if (selected.has(event)) {
|
|
35
|
-
throw new
|
|
26
|
+
throw new ConfigurationError("PLURNK_HOOKS_EVENTS", `PLURNK_HOOKS_EVENTS selects '${event}' more than once.`);
|
|
36
27
|
}
|
|
37
28
|
selected.add(event);
|
|
38
29
|
}
|
|
39
30
|
return selected;
|
|
40
31
|
};
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return timeoutMs;
|
|
47
|
-
};
|
|
48
|
-
export const hookConfig = (environment = process.env) => {
|
|
49
|
-
const timeoutMs = hookTimeoutMs(environment.PLURNK_HOOKS_TIMEOUT_MS);
|
|
32
|
+
export const hookConfig = () => {
|
|
33
|
+
const environment = process.env;
|
|
34
|
+
const timeoutMs = Knob.integer("PLURNK_HOOKS_TIMEOUT_MS", 1);
|
|
35
|
+
const concurrency = Knob.integer("PLURNK_HOOKS_CONCURRENCY", 1);
|
|
36
|
+
const queueLimit = Knob.integer("PLURNK_HOOKS_QUEUE_LIMIT", 0);
|
|
50
37
|
const command = environment.PLURNK_HOOKS_COMMAND?.trim() ?? "";
|
|
51
38
|
if (command.length === 0) {
|
|
52
39
|
if ((environment.PLURNK_HOOKS_ARGS?.length ?? 0) > 0
|
|
53
40
|
|| (environment.PLURNK_HOOKS_EVENTS?.length ?? 0) > 0) {
|
|
54
|
-
throw new
|
|
41
|
+
throw new ConfigurationError("PLURNK_HOOKS_COMMAND", "PLURNK_HOOKS configuration has companions but no PLURNK_HOOKS_COMMAND.");
|
|
55
42
|
}
|
|
56
43
|
return null;
|
|
57
44
|
}
|
|
58
|
-
if (/\s/.test(command)) {
|
|
59
|
-
throw new Error("PLURNK_HOOKS_COMMAND must contain one executable; put arguments in PLURNK_HOOKS_ARGS.");
|
|
60
|
-
}
|
|
61
45
|
return {
|
|
62
46
|
command,
|
|
63
47
|
args: hookArgs(environment.PLURNK_HOOKS_ARGS),
|
|
64
48
|
events: hookEvents(environment.PLURNK_HOOKS_EVENTS),
|
|
65
49
|
timeoutMs,
|
|
50
|
+
concurrency,
|
|
51
|
+
queueLimit,
|
|
66
52
|
};
|
|
67
53
|
};
|
|
68
54
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { default as Module } from "./Module.ts";
|
|
2
2
|
export { default } from "./Module.ts";
|
|
3
3
|
export type { ModuleOptions } from "./Module.ts";
|
|
4
|
-
export { hookConfig
|
|
5
|
-
export type { HookConfig
|
|
4
|
+
export { hookConfig } from "./config.ts";
|
|
5
|
+
export type { HookConfig } from "./config.ts";
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plurnk/plurnk-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "Deliver selected Plurnk lifecycle events to exact local commands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"plurnk",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"author": "@wikitopian",
|
|
25
25
|
"type": "module",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@plurnk/plurnk-contracts": "1.25.0",
|
|
28
|
+
"@plurnk/plurnk-meta": "1.25.0"
|
|
29
|
+
},
|
|
26
30
|
"publishConfig": {
|
|
27
31
|
"access": "public"
|
|
28
32
|
},
|