spooder 6.1.7 → 6.1.93

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
@@ -655,6 +655,8 @@ type WorkerPoolOptions = {
655
655
  worker: string | string[];
656
656
  size?: number;
657
657
  auto_restart?: boolean | AutoRestartConfig;
658
+ onWorkerStart?: (pool: WorkerPool, worker_id: string) => void;
659
+ onWorkerStop?: (pool: WorkerPool, worker_id: string, exit_code: number) => void;
658
660
  };
659
661
 
660
662
  type AutoRestartConfig = {
@@ -1732,7 +1734,7 @@ global_subs: {
1732
1734
  build_time: async () => {
1733
1735
  // Example: fetch build timestamp from git
1734
1736
  const process = Bun.spawn(['git', 'log', '-1', '--format=%ct']);
1735
- const output = await Bun.readableStreamToText(process.stdout);
1737
+ const output = await new Response(process.stdout).text();
1736
1738
  return new Date(parseInt(output.trim()) * 1000).toISOString();
1737
1739
  },
1738
1740
 
@@ -2106,6 +2108,32 @@ pool.on('MSG_REQUEST', msg => {
2106
2108
  });
2107
2109
  ```
2108
2110
 
2111
+ ### Lifecycle Callbacks
2112
+
2113
+ Worker pools support lifecycle callbacks to monitor when workers start and stop. Callbacks receive the pool instance, allowing you to communicate with workers immediately.
2114
+
2115
+ ```ts
2116
+ const pool = await worker_pool({
2117
+ worker: './worker.ts',
2118
+ auto_restart: true,
2119
+ onWorkerStart: async (pool, worker_id) => {
2120
+ console.log(`Worker ${worker_id} started`);
2121
+ await pool.send(worker_id, 'init', { config: 'value' }, true);
2122
+ },
2123
+ onWorkerStop: (pool, worker_id, exit_code) => {
2124
+ console.log(`Worker ${worker_id} stopped with exit code ${exit_code}`);
2125
+ if (exit_code !== 0 && exit_code !== 42) {
2126
+ console.log(`Worker ${worker_id} crashed`);
2127
+ }
2128
+ }
2129
+ });
2130
+ ```
2131
+
2132
+ #### Callback Signatures
2133
+
2134
+ - `onWorkerStart: (pool: WorkerPool, worker_id: string) => void` - Fires when a worker registers with the pool
2135
+ - `onWorkerStop: (pool: WorkerPool, worker_id: string, exit_code: number) => void` - Fires when a worker stops
2136
+
2109
2137
  ### Auto-Restart
2110
2138
 
2111
2139
  The `worker_pool` function supports automatic worker restart when workers crash or close unexpectedly. This feature includes an exponential backoff protocol to prevent restart loops.
package/bun.lock CHANGED
@@ -10,11 +10,11 @@
10
10
  },
11
11
  },
12
12
  "packages": {
13
- "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
13
+ "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
14
14
 
15
- "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
15
+ "@types/node": ["@types/node@25.0.6", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q=="],
16
16
 
17
- "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
17
+ "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
18
18
 
19
19
  "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
20
20
  }
package/package.json CHANGED
@@ -2,8 +2,11 @@
2
2
  "name": "spooder",
3
3
  "author": "Kruithne <kruithne@gmail.com>",
4
4
  "type": "module",
5
- "version": "6.1.7",
5
+ "version": "6.1.93",
6
6
  "module": "./src/api.ts",
7
+ "repository": {
8
+ "url": "https://github.com/Kruithne/spooder"
9
+ },
7
10
  "bin": {
8
11
  "spooder": "./src/cli.ts"
9
12
  },
package/src/api.ts CHANGED
@@ -33,6 +33,9 @@ type WorkerMessage = {
33
33
 
34
34
  const RESPONSE_TIMEOUT_MS = 5000;
35
35
 
36
+ type WorkerStartCallback = (pool: WorkerPool, worker_id: string) => void;
37
+ type WorkerStopCallback = (pool: WorkerPool, worker_id: string, exit_code: number) => void;
38
+
36
39
  export interface WorkerPool {
37
40
  id: string;
38
41
  send(peer: string, id: string, data?: WorkerMessageData, expect_response?: false): void;
@@ -59,6 +62,8 @@ type WorkerPoolOptions = {
59
62
  size?: number;
60
63
  auto_restart?: boolean | AutoRestartConfig;
61
64
  response_timeout?: number;
65
+ onWorkerStart?: WorkerStartCallback;
66
+ onWorkerStop?: WorkerStopCallback;
62
67
  };
63
68
 
64
69
  type WorkerState = {
@@ -94,6 +99,9 @@ export async function worker_pool(options: WorkerPoolOptions): Promise<WorkerPoo
94
99
  const callbacks = new Map<string, (data: WorkerMessage) => Promise<void> | void>();
95
100
  const pending_responses = new Map<string, { resolve: (message: WorkerMessage) => void, reject: (error: Error) => void, timeout: Timer | undefined }>();
96
101
 
102
+ const on_worker_start = options.onWorkerStart;
103
+ const on_worker_stop = options.onWorkerStop;
104
+
97
105
  async function restart_worker(worker: Worker) {
98
106
  if (!auto_restart_enabled)
99
107
  return;
@@ -150,6 +158,9 @@ export async function worker_pool(options: WorkerPoolOptions): Promise<WorkerPoo
150
158
  if (worker_id)
151
159
  pipe_workers.deleteByKey(worker_id);
152
160
 
161
+ if (worker_id)
162
+ on_worker_stop?.(pool, worker_id, exit_code);
163
+
153
164
  if (auto_restart_enabled && exit_code !== WORKER_EXIT_NO_RESTART)
154
165
  restart_worker(worker);
155
166
  else if (exit_code === WORKER_EXIT_NO_RESTART)
@@ -180,6 +191,8 @@ export async function worker_pool(options: WorkerPoolOptions): Promise<WorkerPoo
180
191
 
181
192
  worker_promises.get(worker)?.();
182
193
  worker_promises.delete(worker);
194
+
195
+ on_worker_start?.(pool, worker_id);
183
196
  } else if (message.peer === '__broadcast__') {
184
197
  const worker_id = pipe_workers.getByValue(worker);
185
198
  if (worker_id === undefined)
@@ -808,7 +821,7 @@ export class ErrorWithMetadata extends Error {
808
821
  else if (typeof value === 'function')
809
822
  resolved_value = await value();
810
823
  else if (value instanceof ReadableStream)
811
- resolved_value = await Bun.readableStreamToText(value);
824
+ resolved_value = await new Response(value).text();
812
825
 
813
826
  if (typeof resolved_value === 'string' && resolved_value.includes('\n'))
814
827
  resolved_value = resolved_value.split(/\r?\n/);
@@ -957,12 +970,10 @@ export async function parse_template(template: string, replacements: Replacement
957
970
  }
958
971
  }
959
972
 
960
- if (!drop_missing)
961
- return match;
962
-
973
+ // missing/undefined items treated as empty array (no output)
963
974
  return '';
964
975
  });
965
-
976
+
966
977
  // Parse t-if tags
967
978
  const if_regex = /<t-if\s+test="([^"]+)"\s*>(.*?)<\/t-if>/gs;
968
979
  result = await replace_async(result, if_regex, async (match, condition_key, if_content) => {
@@ -1106,9 +1117,9 @@ export async function git_get_hashes(length = 7): Promise<Record<string, string>
1106
1117
  await process.exited;
1107
1118
 
1108
1119
  if (process.exitCode as number > 0)
1109
- throw new Error('git_get_hashes() failed, `' + cmd.join(' ') + '` exited with non-zero exit code.');
1120
+ return {};
1110
1121
 
1111
- const stdout = await Bun.readableStreamToText(process.stdout as ReadableStream);
1122
+ const stdout = await new Response(process.stdout).text();
1112
1123
  const hash_map: Record<string, string> = {};
1113
1124
 
1114
1125
  const regex = /([^\s]+)\s([^\s]+)\s([^\s]+)\t(.+)/g;
@@ -1128,7 +1139,7 @@ export function git_get_hashes_sync(length = 7): Record<string, string> {
1128
1139
  });
1129
1140
 
1130
1141
  if (process.exitCode > 0)
1131
- throw new Error('git_get_hashes_sync() failed, `' + cmd.join(' ') + '` exited with non-zero exit code.');
1142
+ return {};
1132
1143
 
1133
1144
  const stdout = process.stdout.toString();
1134
1145
  const hash_map: Record<string, string> = {};