@bevel-software/platform-core-backend 0.8.0 → 0.9.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.
Files changed (84) hide show
  1. package/dist/core/core-ports.d.ts +7 -0
  2. package/dist/core/core-ports.d.ts.map +1 -1
  3. package/dist/core/core-ports.js.map +1 -1
  4. package/dist/core/create-core-server.d.ts.map +1 -1
  5. package/dist/core/create-core-server.js +24 -11
  6. package/dist/core/create-core-server.js.map +1 -1
  7. package/dist/core/create-core-services.d.ts +7 -2
  8. package/dist/core/create-core-services.d.ts.map +1 -1
  9. package/dist/core/create-core-services.js +39 -18
  10. package/dist/core/create-core-services.js.map +1 -1
  11. package/dist/modules/access/access.routes.d.ts +3 -1
  12. package/dist/modules/access/access.routes.d.ts.map +1 -1
  13. package/dist/modules/access/access.routes.js +4 -2
  14. package/dist/modules/access/access.routes.js.map +1 -1
  15. package/dist/modules/access/render-roles-yaml.d.ts +22 -0
  16. package/dist/modules/access/render-roles-yaml.d.ts.map +1 -0
  17. package/dist/modules/access/render-roles-yaml.js +56 -0
  18. package/dist/modules/access/render-roles-yaml.js.map +1 -0
  19. package/dist/modules/access/roles-admin.service.d.ts +15 -1
  20. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  21. package/dist/modules/access/roles-admin.service.js +30 -15
  22. package/dist/modules/access/roles-admin.service.js.map +1 -1
  23. package/dist/modules/settings/setup.routes.d.ts +10 -1
  24. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  25. package/dist/modules/settings/setup.routes.js +111 -6
  26. package/dist/modules/settings/setup.routes.js.map +1 -1
  27. package/dist/modules/workspace/startup/kb-git.d.ts +23 -0
  28. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -0
  29. package/dist/modules/workspace/startup/kb-git.js +86 -0
  30. package/dist/modules/workspace/startup/kb-git.js.map +1 -0
  31. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +74 -0
  32. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -0
  33. package/dist/modules/workspace/startup/kb-startup-runner.js +528 -0
  34. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -0
  35. package/dist/modules/workspace/startup/on-server-start.d.ts +105 -0
  36. package/dist/modules/workspace/startup/on-server-start.d.ts.map +1 -0
  37. package/dist/modules/workspace/startup/on-server-start.js +21 -0
  38. package/dist/modules/workspace/startup/on-server-start.js.map +1 -0
  39. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +46 -0
  40. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -0
  41. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +492 -0
  42. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -0
  43. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +23 -0
  44. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -0
  45. package/dist/modules/workspace/startup/steps/roles-yaml.step.js +69 -0
  46. package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -0
  47. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +17 -0
  48. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -0
  49. package/dist/modules/workspace/startup/steps/seed-tree.js +109 -0
  50. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -0
  51. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +103 -0
  52. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -0
  53. package/dist/modules/workspace/startup/steps/template-files.step.js +337 -0
  54. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -0
  55. package/dist/modules/workspace/workspace.service.d.ts +0 -35
  56. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  57. package/dist/modules/workspace/workspace.service.js +2 -101
  58. package/dist/modules/workspace/workspace.service.js.map +1 -1
  59. package/kb-template/gitignore.template +16 -0
  60. package/package.json +3 -3
  61. package/src/core/core-ports.ts +7 -0
  62. package/src/core/create-core-server.ts +30 -11
  63. package/src/core/create-core-services.ts +43 -22
  64. package/src/modules/access/__tests__/roles-admin.service.test.ts +24 -2
  65. package/src/modules/access/access.routes.ts +3 -0
  66. package/src/modules/access/render-roles-yaml.ts +65 -0
  67. package/src/modules/access/roles-admin.service.ts +32 -14
  68. package/src/modules/settings/__tests__/setup.routes.test.ts +124 -3
  69. package/src/modules/settings/setup.routes.ts +112 -5
  70. package/src/modules/workspace/__tests__/workspace.service.test.ts +5 -94
  71. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +495 -0
  72. package/src/modules/workspace/startup/kb-git.ts +94 -0
  73. package/src/modules/workspace/startup/kb-startup-runner.ts +597 -0
  74. package/src/modules/workspace/startup/on-server-start.ts +97 -0
  75. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +636 -0
  76. package/src/modules/workspace/{plugins-migration.ts → startup/steps/groups-to-plugins.step.ts} +342 -260
  77. package/src/modules/workspace/startup/steps/roles-yaml.step.ts +71 -0
  78. package/src/modules/workspace/startup/steps/seed-tree.ts +115 -0
  79. package/src/modules/workspace/startup/steps/template-files.step.ts +360 -0
  80. package/src/modules/workspace/workspace.service.ts +2 -106
  81. package/src/modules/workspace/__tests__/kb-seed.service.test.ts +0 -512
  82. package/src/modules/workspace/__tests__/plugins-migration.test.ts +0 -427
  83. package/src/modules/workspace/kb-seed.interface.ts +0 -36
  84. package/src/modules/workspace/kb-seed.service.ts +0 -584
@@ -0,0 +1,86 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ const execFileAsync = promisify(execFile);
7
+ /**
8
+ * The startup phase's own git plumbing — the runner owns every remote and
9
+ * repository operation of the phase (steps only declare buffered ops), so
10
+ * this is deliberately small and self-contained rather than borrowed from
11
+ * the runtime workspace service.
12
+ */
13
+ /** Fallback committer identity; workflow commits override with `--author`. */
14
+ export const BOT_NAME = 'Bevel Workflow';
15
+ export const BOT_EMAIL = 'bevel-workflow@bevel.software';
16
+ /**
17
+ * Scrub credentials from anything that reaches a log or an error message:
18
+ * the configured token wherever it appears, and URL userinfo — a remote
19
+ * spelled `https://user:pass@host` would otherwise leak `pass` verbatim
20
+ * through every git failure that quotes the URL back.
21
+ */
22
+ export function redactSecret(text) {
23
+ const token = process.env.GITHUB_TOKEN;
24
+ const scrubbed = token ? text.replaceAll(token, '***') : text;
25
+ return scrubbed.replace(/:\/\/[^/@\s]+@/g, '://***@');
26
+ }
27
+ /**
28
+ * Per-invocation `-c` config. Long paths always (Windows checkouts of deep
29
+ * KB trees); when a token is present, an inline credential helper that reads
30
+ * it from the environment at call time — the secret never appears in argv.
31
+ */
32
+ function credArgs(gitUsername) {
33
+ const args = ['-c', 'core.longpaths=true'];
34
+ if (process.env.GITHUB_TOKEN) {
35
+ args.push('-c', `credential.helper=!f() { echo "username=${gitUsername}"; echo "password=$GITHUB_TOKEN"; }; f`);
36
+ }
37
+ return args;
38
+ }
39
+ export async function git(cwd, gitUsername, args) {
40
+ try {
41
+ const { stdout } = await execFileAsync('git', [...credArgs(gitUsername), ...args], {
42
+ cwd,
43
+ // A stalled remote must FAIL the phase, not hang the boot forever —
44
+ // fail-closed (and KB_SAFE_BOOT's demotion) can only engage on an error
45
+ // that actually arrives. 10 minutes is generous for the largest clone.
46
+ timeout: 600_000,
47
+ // The 1MiB default truncates `ls-remote --heads` on remotes with very
48
+ // many branches, which would silently drop heads from the phase's view.
49
+ maxBuffer: 64 * 1024 * 1024,
50
+ // A credential prompt must fail the phase, not hang the boot forever
51
+ // waiting on a terminal nobody is watching.
52
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
53
+ });
54
+ return stdout.toString();
55
+ }
56
+ catch (err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ throw new Error(redactSecret(`git ${args[0]} failed: ${msg}`));
59
+ }
60
+ }
61
+ export async function stampIdentity(repo, gitUsername) {
62
+ await git(repo, gitUsername, ['config', 'user.name', BOT_NAME]);
63
+ await git(repo, gitUsername, ['config', 'user.email', BOT_EMAIL]);
64
+ }
65
+ /** Branch names present on the remote, from `ls-remote --heads`. */
66
+ export async function lsRemoteHeads(repoUrl, gitUsername) {
67
+ const out = await git(os.tmpdir(), gitUsername, ['ls-remote', '--heads', repoUrl]);
68
+ const heads = new Set();
69
+ for (const line of out.split('\n')) {
70
+ const m = /\srefs\/heads\/(.+)$/.exec(line.trim());
71
+ if (m)
72
+ heads.add(m[1]);
73
+ }
74
+ return heads;
75
+ }
76
+ /** A temp dir that is always removed, success or failure. */
77
+ export async function withTempDir(fn) {
78
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-startup-'));
79
+ try {
80
+ return await fn(dir);
81
+ }
82
+ finally {
83
+ await fs.rm(dir, { recursive: true, force: true }).catch(() => { });
84
+ }
85
+ }
86
+ //# sourceMappingURL=kb-git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kb-git.js","sourceRoot":"","sources":["../../../../src/modules/workspace/startup/kb-git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import type { OnServerStart } from './on-server-start.js';
2
+ /**
3
+ * The KB startup phase: run every registered {@link OnServerStart} step, in
4
+ * order, against lazily-cloned branch handles, then land one commit per
5
+ * dirty branch. Invoked at the deployment's two quiet moments — boot (before
6
+ * routes mount) and first-time setup completion (the app is gated shut until
7
+ * then) — and never again while the process serves.
8
+ *
9
+ * Fully fail-closed: any failure this phase cannot DECLARE (an unhandled
10
+ * step throw, an unreachable remote, a clone that will not come down, a
11
+ * refused write) throws out of `runAll` and stops the boot. The container's
12
+ * restart policy is the retry — each attempt at boot time on quiet trees —
13
+ * so an environmental failure converges without a human the moment the
14
+ * environment returns. The one carve-out: a push rejected because a
15
+ * concurrent replica won rolls back and continues; the winner already landed
16
+ * the same idempotent changes, and stopping the loser would make every
17
+ * multi-replica deploy flappy by design.
18
+ *
19
+ * `KB_SAFE_BOOT=1` is the break-glass demotion: on the first failure the
20
+ * phase resets every uncommitted tree, abandons the rest of the phase, and
21
+ * lets the server boot UNMAINTAINED so an admin can get in and rescue —
22
+ * loudly, at boot and in the log, because an env var outlives the emergency.
23
+ */
24
+ export interface KbStartupRunnerOptions {
25
+ kbRepoUrl: () => string;
26
+ gitUsername: () => string;
27
+ workspacesRoot: string;
28
+ kbDirName: string;
29
+ templateDir: string;
30
+ defaultBranch: () => string;
31
+ protectedBranches: () => readonly string[];
32
+ /** Admins written into a freshly-seeded repo's roles.yaml (`ADMIN_EMAIL`). */
33
+ seedAdminEmails: readonly string[];
34
+ /** The ordered step chain — core's steps plus whatever the distribution appends. */
35
+ steps: readonly OnServerStart[];
36
+ /** The empty-remote seed commit builder (template tree + roles.yaml), injected
37
+ so the runner stays free of template knowledge. Receives the temp dir to
38
+ fill; the runner handles init/commit/push around it. Resolves to the
39
+ repo-relative paths the builder GENERATED itself (rather than copied from
40
+ the template) — the runner force-adds them after `git add -A`, so a
41
+ template `.gitignore` rule can never silently drop a required seed file
42
+ from the commit. */
43
+ buildSeedTree: (dir: string) => Promise<string[]>;
44
+ }
45
+ export declare class KbStartupRunner {
46
+ private readonly opts;
47
+ constructor(opts: KbStartupRunnerOptions);
48
+ /**
49
+ * Run the whole phase. Throws to stop the boot; returns normally when the
50
+ * KB is fully maintained (or safe boot abandoned the phase, loudly).
51
+ */
52
+ runAll(): Promise<void>;
53
+ /**
54
+ * Remote preparation — runner machinery, not a step, because every remote
55
+ * failure mode here is the runner's to own: an EMPTY remote gets the full
56
+ * seed commit pushed to every protected branch; missing protected refs are
57
+ * created from the best base. Returns the remote's head names (post-seed).
58
+ */
59
+ private ensureRemote;
60
+ private buildContext;
61
+ /**
62
+ * The branch's working copy at the runtime layout
63
+ * (`<workspacesRoot>/<id>/<kbDirName>`), so the workspace service finds it
64
+ * on disk afterwards. A surviving clone is fast-forwarded to origin when
65
+ * that is a pure fast-forward; a clone that is AHEAD (a crash before push
66
+ * left committed work) is left alone — maintenance lands on top and the
67
+ * push either carries both or rejects and rolls back, and the pending-
68
+ * commit recovery owns that work, not this phase.
69
+ */
70
+ private ensureClone;
71
+ /** One commit per dirty branch; push; the replica carve-out on rejection. */
72
+ private finalize;
73
+ }
74
+ //# sourceMappingURL=kb-startup-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kb-startup-runner.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/startup/kb-startup-runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAY,aAAa,EAAsB,MAAM,sBAAsB,CAAC;AAGxF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;IAC3C,8EAA8E;IAC9E,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oFAAoF;IACpF,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC;;;;;;2BAMuB;IACvB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACnD;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,sBAAsB;IAEzD;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA+G7B;;;;;OAKG;YACW,YAAY;IAyE1B,OAAO,CAAC,YAAY;IAkBpB;;;;;;;;OAQG;YACW,WAAW;IA0BzB,6EAA6E;YAC/D,QAAQ;CAoFvB"}