frosty 0.0.149 → 0.0.151

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
@@ -117,6 +117,16 @@ module.exports = {
117
117
  basepath: '/', // (Optional) URL base path for this entry
118
118
  }
119
119
  },
120
+ workers: { // (Optional) Worker entry points
121
+ myClientWorker: {
122
+ entry: 'src/workers/myClientWorker.js', // Path to worker entry file
123
+ client: true, // true = browser worker (bundled into public/workers/)
124
+ },
125
+ myServerWorker: {
126
+ entry: 'src/workers/myServerWorker.js', // Path to worker entry file
127
+ client: false, // false = server worker (bundled into workers/)
128
+ }
129
+ },
120
130
  moduleSuffixes: { // (Optional) Custom module resolution suffixes
121
131
  client: ['.browser', '.web', ''],
122
132
  server: ['.node', '.server', '.web', '']
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frosty",
3
- "version": "0.0.149",
3
+ "version": "0.0.151",
4
4
  "main": "dist/index",
5
5
  "module": "dist/index",
6
6
  "types": "dist/index",
@@ -44,6 +44,7 @@ print_usage() {
44
44
  echo " -B, --no-build Skip build step"
45
45
  echo " -p, --port <port> Specify port for the server (default: 8080)"
46
46
  echo " -n, --num-workers <count> Specify number of worker processes"
47
+ echo " -i, --instance-var <name> Specify the env variable name for worker instance index (default: NODE_APP_INSTANCE)"
47
48
  echo " -c, --configuration <file> Specify configuration file to use (default: server.config.js)"
48
49
  echo " -s, --src <dir> Specify source root directory"
49
50
  echo " -o, --output <dir> Specify output directory for build artifacts"
@@ -66,6 +67,7 @@ while [ $# -gt 0 ]; do
66
67
  -B|--no-build) NO_BUILD=true; shift ;;
67
68
  -p|--port) PORT="$2"; shift 2 ;;
68
69
  -n|--num-workers) NUM_WORKERS="$2"; shift 2 ;;
70
+ -i|--instance-var) INSTANCE_VAR="$2"; shift 2 ;;
69
71
  -c|--configuration) CONFIG_FILE="$2"; shift 2 ;;
70
72
  -s|--src) SRCROOT="$2"; shift 2 ;;
71
73
  -o|--output) OUTPUT_DIR="$2"; shift 2 ;;
@@ -114,6 +116,7 @@ if [ "$NO_BUILD" != "true" ]; then
114
116
  [ -n "$INPUT_FILE" ] && BUILD_OPTS="$BUILD_OPTS --env INPUT_FILE="$INPUT_FILE""
115
117
  [ -n "$SRCROOT" ] && BUILD_OPTS="$BUILD_OPTS --env SRCROOT="$SRCROOT""
116
118
  [ -n "$NUM_WORKERS" ] && BUILD_OPTS="$BUILD_OPTS --env NUM_WORKERS="$NUM_WORKERS""
119
+ [ -n "$INSTANCE_VAR" ] && BUILD_OPTS="$BUILD_OPTS --env INSTANCE_VAR="$INSTANCE_VAR""
117
120
  [ -n "$PORT" ] && BUILD_OPTS="$BUILD_OPTS --env PORT="$PORT""
118
121
  if [ "$WATCH_MODE" = "true" ]; then
119
122
  node "$FROSTY_CLI_ROOT/node_modules/webpack-cli/bin/cli.js" $BUILD_OPTS --watch &
@@ -28,3 +28,4 @@ import { availableParallelism } from 'os';
28
28
 
29
29
  export const PORT = process.env.PORT ? parseInt(process.env.PORT) : 8080;
30
30
  export const NUM_WORKERS = process.env.NUM_WORKERS ? parseInt(process.env.NUM_WORKERS) : availableParallelism();
31
+ export const INSTANCE_VAR = process.env.INSTANCE_VAR || 'NODE_APP_INSTANCE';
@@ -31,18 +31,44 @@ import { Server } from '@o2ter/server-js';
31
31
  import { FrostyRoute } from './route';
32
32
  import * as __SERVER__ from '__SERVER__';
33
33
  import * as __APPLICATIONS__ from '__APPLICATIONS__';
34
- import { PORT, NUM_WORKERS } from './env';
34
+ import { PORT, NUM_WORKERS, INSTANCE_VAR } from './env';
35
35
 
36
36
  if (cluster.isPrimary && NUM_WORKERS > 1) {
37
- console.log(`Primary ${process.pid} is running`);
37
+ console.info(`Primary ${process.pid} is running`);
38
38
 
39
- // Fork workers.
40
- for (let i = 0; i < NUM_WORKERS; i++) {
41
- cluster.fork();
42
- }
39
+ // Fork workers one by one, waiting for each to come online before starting the next.
40
+ let forked = 0;
41
+ const workerInstances = new Map(); // worker.id -> instance index
42
+ const startedWorkers = new Set(); // worker IDs that have successfully started
43
+
44
+ const spawnWorker = (instance) => {
45
+ const worker = cluster.fork({ [INSTANCE_VAR]: instance });
46
+ workerInstances.set(worker.id, instance);
47
+ return worker;
48
+ };
49
+
50
+ const forkNext = () => {
51
+ if (forked >= NUM_WORKERS) return;
52
+ const instance = forked++;
53
+ const worker = spawnWorker(instance);
54
+ worker.once('listening', () => {
55
+ startedWorkers.add(worker.id);
56
+ forkNext();
57
+ });
58
+ };
59
+ forkNext();
43
60
 
44
61
  cluster.on('exit', (worker, code, signal) => {
45
- console.log(`Worker ${worker.process.pid} died`);
62
+ console.info(`Process ${worker.process.pid} exited with code ${code} (${signal})`);
63
+ const instance = workerInstances.get(worker.id);
64
+ workerInstances.delete(worker.id);
65
+ if (startedWorkers.delete(worker.id)) {
66
+ // Worker had started successfully; restart it
67
+ const newWorker = spawnWorker(instance);
68
+ newWorker.once('listening', () => {
69
+ startedWorkers.add(newWorker.id);
70
+ });
71
+ }
46
72
  });
47
73
 
48
74
  } else {
@@ -43,6 +43,7 @@ export default async (env, argv) => {
43
43
  INPUT_FILE,
44
44
  PORT = 8080,
45
45
  NUM_WORKERS,
46
+ INSTANCE_VAR,
46
47
  } = env;
47
48
 
48
49
  const serverConfig = await (async () => {
@@ -213,6 +214,16 @@ export default async (env, argv) => {
213
214
  server: config.moduleSuffixes?.server ?? ['.node', '.server', '.web', ''],
214
215
  };
215
216
 
217
+ const clientWorkers = [];
218
+ const serverWorkers = [];
219
+ for (const [name, { entry, client }] of _.entries(config.workers || {})) {
220
+ if (client) {
221
+ clientWorkers.push({ name, entry });
222
+ } else {
223
+ serverWorkers.push({ name, entry });
224
+ }
225
+ }
226
+
216
227
  return [
217
228
  ..._.map(inputs, ({ entry }, name) => ({
218
229
  ...webpackConfiguration,
@@ -249,12 +260,40 @@ export default async (env, argv) => {
249
260
  ]
250
261
  }
251
262
  })),
263
+ {
264
+ ...webpackConfiguration,
265
+ optimization: webpackOptimization({ server: false }),
266
+ plugins: webpackPlugins,
267
+ entry: {
268
+ ..._.fromPairs(clientWorkers.map(({ name, entry }) => ([`workers/${name}`, [
269
+ path.resolve(__dirname, 'node_modules/core-js/stable'),
270
+ path.resolve(process.cwd(), entry),
271
+ ]]))),
272
+ },
273
+ output: {
274
+ path: path.join(OUTPUT_DIR, 'public'),
275
+ },
276
+ resolve: {
277
+ ...webpackConfiguration.resolve,
278
+ extensions: [
279
+ ...moduleSuffixes.client.flatMap(x => [`${x}.tsx`, `${x}.jsx`]),
280
+ ...moduleSuffixes.client.flatMap(x => [`${x}.ts`, `${x}.mjs`, `${x}.js`]),
281
+ '...'
282
+ ],
283
+ },
284
+ module: {
285
+ rules: [
286
+ babelLoaderConfiguration({ server: false }),
287
+ ...config.options?.module?.rules ?? [],
288
+ ]
289
+ }
290
+ },
252
291
  {
253
292
  ...webpackConfiguration,
254
293
  optimization: webpackOptimization({ server: true }),
255
294
  plugins: _.compact([
256
295
  ...webpackPlugins,
257
- new webpack.EnvironmentPlugin({ PORT, NUM_WORKERS }),
296
+ new webpack.EnvironmentPlugin({ PORT, NUM_WORKERS, INSTANCE_VAR }),
258
297
  new webpack.DefinePlugin({
259
298
  __applications__: JSON.stringify(_.mapValues(inputs, x => ({
260
299
  path: x.basepath,
@@ -268,6 +307,10 @@ export default async (env, argv) => {
268
307
  path.resolve(__dirname, 'node_modules/core-js/stable'),
269
308
  path.resolve(__dirname, 'src/server/index.js'),
270
309
  ],
310
+ ..._.fromPairs(serverWorkers.map(({ name, entry }) => ([`workers/${name}`, [
311
+ path.resolve(__dirname, 'node_modules/core-js/stable'),
312
+ path.resolve(process.cwd(), entry),
313
+ ]]))),
271
314
  },
272
315
  output: {
273
316
  path: OUTPUT_DIR,