create-kelpie 0.14.0 → 0.15.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
@@ -20,6 +20,8 @@ package.json @kelpie/server and @kelpie/ui, pinned to this version
20
20
  kelpie.config.ts the server module list
21
21
  kelpie.ui.config.ts the UI module list
22
22
  src/server.ts the entry point: config, modules, migrations, serve
23
+ src/migrate.ts `npm run migrate`: applies core and pg-boss migrations
24
+ src/worker.ts `npm run worker`: pg-boss consumer, for scaling jobs apart from HTTP
23
25
  src/reseal.ts re-seals stored secrets after a SECRET_ENCRYPTION_KEY change
24
26
  web/index.html the web entry
25
27
  web/main.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wEAAwE;AACxE,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AA6DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkBtD;AAED,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CAsBpE"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wEAAwE;AACxE,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AA8DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkBtD;AAED,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CAsBpE"}
package/dist/scaffold.js CHANGED
@@ -30,6 +30,7 @@ const TEMPLATE_FILES = new Map([
30
30
  ['kelpie.ui.config.ts', 'kelpie.ui.config.ts'],
31
31
  ['src/server.ts', 'src/server.ts'],
32
32
  ['src/migrate.ts', 'src/migrate.ts'],
33
+ ['src/worker.ts', 'src/worker.ts'],
33
34
  ['src/reseal.ts', 'src/reseal.ts'],
34
35
  ['web/index.html', 'web/index.html'],
35
36
  ['web/main.tsx', 'web/main.tsx'],
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kelpie",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Scaffold a self-hosted Kelpie assembly. Run it with npm create kelpie.",
5
5
  "keywords": [
6
6
  "kelpie",
package/src/scaffold.ts CHANGED
@@ -51,6 +51,7 @@ const TEMPLATE_FILES: ReadonlyMap<string, string> = new Map([
51
51
  ['kelpie.ui.config.ts', 'kelpie.ui.config.ts'],
52
52
  ['src/server.ts', 'src/server.ts'],
53
53
  ['src/migrate.ts', 'src/migrate.ts'],
54
+ ['src/worker.ts', 'src/worker.ts'],
54
55
  ['src/reseal.ts', 'src/reseal.ts'],
55
56
  ['web/index.html', 'web/index.html'],
56
57
  ['web/main.tsx', 'web/main.tsx'],
@@ -128,7 +128,21 @@ stops rather than answering every browser with a blank page.
128
128
 
129
129
  Migrations apply at boot. Running more than one instance makes that a race, so
130
130
  migrate once with `npm run migrate` in a release step, then start the instances
131
- with `--no-migrate`. That command is forward-only and safe to re-run.
131
+ with `--no-migrate`. That command is forward-only and safe to re-run. It also
132
+ applies pg-boss's own schema, so the worker has the tables it needs.
133
+
134
+ Background jobs run on pg-boss. The default deployment runs the work loops
135
+ inline from `npm start`, so one process both serves and works. To scale the two
136
+ sides on their own — for example when a slow handler must not compete with
137
+ request handling — run a second process alongside:
138
+
139
+ ```bash
140
+ WEB_BUNDLE_DIR=./dist npm start -- --no-worker # HTTP only, still enqueues
141
+ npm run worker # pg-boss consumer, no HTTP
142
+ ```
143
+
144
+ Both processes read the same `.env`, connect to the same database, and load
145
+ the same modules. Neither owns state the other needs.
132
146
 
133
147
  TLS, reverse proxies, systemd, health checks, and backups:
134
148
  [docs/self-hosting/production.md](https://github.com/velvet-tiger/kelpie/blob/main/docs/self-hosting/production.md).
@@ -12,6 +12,7 @@
12
12
  "dev:web": "vite",
13
13
  "start": "node --env-file-if-exists=.env src/server.ts",
14
14
  "migrate": "node --env-file-if-exists=.env src/migrate.ts",
15
+ "worker": "node --env-file-if-exists=.env src/worker.ts",
15
16
  "reseal": "node --env-file-if-exists=.env src/reseal.ts",
16
17
  "build": "vite build",
17
18
  "typecheck": "tsc --project tsconfig.server.json && tsc --project tsconfig.web.json"
@@ -24,10 +24,12 @@ function reportFatal(message: string): void {
24
24
  }
25
25
 
26
26
  async function migrate(): Promise<void> {
27
- const { database, contributions, logger } = await bootAssembly(kelpieConfig, process.env)
27
+ const { database, contributions, jobs, logger } = await bootAssembly(kelpieConfig, process.env)
28
28
 
29
29
  try {
30
30
  await runMigrations(database.db, contributions.schemas, logger)
31
+ await jobs.migrate()
32
+ logger.info('pg-boss schema migrated')
31
33
  } finally {
32
34
  await database.close()
33
35
  }
@@ -10,6 +10,7 @@ import {
10
10
  resolveClientIpFrom,
11
11
  runMigrations,
12
12
  serveWebBundle,
13
+ startWorker,
13
14
  } from '@kelpie/server'
14
15
 
15
16
  import kelpieConfig from '../kelpie.config.ts'
@@ -25,6 +26,11 @@ import kelpieConfig from '../kelpie.config.ts'
25
26
  *
26
27
  * `--no-migrate` skips the migration step, for deployments where `npm run migrate`
27
28
  * migrates once in a release step and many instances then start.
29
+ *
30
+ * `--no-worker` skips the pg-boss `work()` loops. This process still opens the
31
+ * boss instance so a service can enqueue jobs inside its own transaction; the
32
+ * work loops themselves live in the separate `worker` process (`src/worker.ts`).
33
+ * The default is to run both inline, so one container both serves and works.
28
34
  */
29
35
 
30
36
  function reportFatal(message: string): void {
@@ -32,15 +38,26 @@ function reportFatal(message: string): void {
32
38
  }
33
39
 
34
40
  async function start(): Promise<void> {
35
- const { config, logger, database, createId, credentials, contributions } = await bootAssembly(
36
- kelpieConfig,
37
- process.env,
38
- )
41
+ const boot = await bootAssembly(kelpieConfig, process.env)
42
+ const { config, logger, database, createId, credentials, contributions, jobs } = boot
39
43
 
40
44
  if (process.argv.includes('--no-migrate')) {
41
45
  logger.info('skipping migrations', { reason: '--no-migrate' })
42
46
  } else {
43
47
  await runMigrations(database.db, contributions.schemas, logger)
48
+ await jobs.migrate()
49
+ }
50
+
51
+ // The API always opens a boss instance: a service enqueues jobs inside its
52
+ // own transaction on request, so `boss.send` must be reachable even when
53
+ // this process runs no work loops. `startWorker` on top adds the `work()`
54
+ // calls; `--no-worker` skips them, for deployments that run a separate
55
+ // `src/worker.ts` process alongside.
56
+ if (process.argv.includes('--no-worker')) {
57
+ logger.info('skipping inline worker', { reason: '--no-worker' })
58
+ await jobs.start()
59
+ } else {
60
+ await startWorker(boot)
44
61
  }
45
62
 
46
63
  const app = createApp({
@@ -81,8 +98,12 @@ async function start(): Promise<void> {
81
98
  logger.info('shutting down', { signal })
82
99
  server.close(() => {
83
100
  // Drain before closing the pool: a handler mid-flight may still be writing.
84
- void contributions.events
85
- .drain()
101
+ // Order: stop the job worker (waits for in-flight jobs), drain the event
102
+ // bus (which may still be delivering post-commit handlers), then close
103
+ // the database.
104
+ void jobs
105
+ .stop()
106
+ .then(() => contributions.events.drain())
86
107
  .then(() => database.close())
87
108
  .then(() => process.exit(0))
88
109
  })
@@ -0,0 +1,71 @@
1
+ import {
2
+ ConfigurationError,
3
+ ModuleBootError,
4
+ ModuleConfigFileError,
5
+ bootAssembly,
6
+ startWorker,
7
+ } from '@kelpie/server'
8
+
9
+ import kelpieConfig from '../kelpie.config.ts'
10
+
11
+ /**
12
+ * The worker entry point. Runs the pg-boss consumer without ever calling
13
+ * `createApp`, so there is no HTTP surface here.
14
+ *
15
+ * The default deployment runs the work loops inline from `src/server.ts`, so
16
+ * one container both serves and works. Start this process alongside a
17
+ * `server.ts` running with `--no-worker` when the two should scale on their
18
+ * own — for example, when a slow handler must not compete with request
19
+ * handling. Both processes share the same image, the same secrets, and the
20
+ * same `@kelpie/server` build.
21
+ */
22
+
23
+ function reportFatal(message: string): void {
24
+ process.stderr.write(`${message}\n`)
25
+ }
26
+
27
+ async function main(): Promise<void> {
28
+ const boot = await bootAssembly(kelpieConfig, process.env)
29
+
30
+ await startWorker(boot)
31
+
32
+ const shutdown = (signal: string): void => {
33
+ boot.logger.info('shutting down worker', { signal })
34
+ void boot.jobs
35
+ .stop()
36
+ .then(() => boot.contributions.events.drain())
37
+ .then(() => boot.database.close())
38
+ .then(() => process.exit(0))
39
+ .catch((error: unknown) => {
40
+ boot.logger.error('worker shutdown failed', { error })
41
+ process.exit(1)
42
+ })
43
+ }
44
+
45
+ process.on('SIGINT', () => shutdown('SIGINT'))
46
+ process.on('SIGTERM', () => shutdown('SIGTERM'))
47
+ }
48
+
49
+ try {
50
+ await main()
51
+ } catch (error: unknown) {
52
+ if (error instanceof ConfigurationError) {
53
+ reportFatal(error.message)
54
+ reportFatal('Check .env against the table in README.md.')
55
+ process.exit(1)
56
+ }
57
+
58
+ if (error instanceof ModuleConfigFileError) {
59
+ reportFatal(error.message)
60
+ reportFatal('Fix the file at KELPIE_MODULE_CONFIG_PATH, or unset it to let workspaces decide for themselves.')
61
+ process.exit(1)
62
+ }
63
+
64
+ if (error instanceof ModuleBootError) {
65
+ reportFatal(error.message)
66
+ reportFatal('Fix the module list in kelpie.config.ts, or the configuration it needs.')
67
+ process.exit(1)
68
+ }
69
+
70
+ throw error
71
+ }