@bevel-software/platform-core-backend 0.10.0 → 0.11.1

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 (188) hide show
  1. package/dist/core/create-core-server.d.ts.map +1 -1
  2. package/dist/core/create-core-server.js +12 -1
  3. package/dist/core/create-core-server.js.map +1 -1
  4. package/dist/core/create-core-services.d.ts +22 -0
  5. package/dist/core/create-core-services.d.ts.map +1 -1
  6. package/dist/core/create-core-services.js +34 -1
  7. package/dist/core/create-core-services.js.map +1 -1
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/modules/access/access-control.interface.d.ts +54 -28
  13. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  14. package/dist/modules/access/access-control.service.d.ts +129 -14
  15. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  16. package/dist/modules/access/access-control.service.js +435 -66
  17. package/dist/modules/access/access-control.service.js.map +1 -1
  18. package/dist/modules/access/access-declarations.d.ts.map +1 -1
  19. package/dist/modules/access/access-declarations.js +5 -3
  20. package/dist/modules/access/access-declarations.js.map +1 -1
  21. package/dist/modules/access/access-mutation.service.d.ts +39 -6
  22. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  23. package/dist/modules/access/access-mutation.service.js +78 -18
  24. package/dist/modules/access/access-mutation.service.js.map +1 -1
  25. package/dist/modules/access/access-splice.d.ts +31 -4
  26. package/dist/modules/access/access-splice.d.ts.map +1 -1
  27. package/dist/modules/access/access-splice.js +40 -16
  28. package/dist/modules/access/access-splice.js.map +1 -1
  29. package/dist/modules/access/access.routes.d.ts.map +1 -1
  30. package/dist/modules/access/access.routes.js +204 -82
  31. package/dist/modules/access/access.routes.js.map +1 -1
  32. package/dist/modules/access/admin-locked-commit.d.ts +134 -0
  33. package/dist/modules/access/admin-locked-commit.d.ts.map +1 -0
  34. package/dist/modules/access/admin-locked-commit.js +277 -0
  35. package/dist/modules/access/admin-locked-commit.js.map +1 -0
  36. package/dist/modules/access/admin-route-helpers.d.ts +32 -0
  37. package/dist/modules/access/admin-route-helpers.d.ts.map +1 -0
  38. package/dist/modules/access/admin-route-helpers.js +44 -0
  39. package/dist/modules/access/admin-route-helpers.js.map +1 -0
  40. package/dist/modules/access/capability-registry.d.ts +41 -0
  41. package/dist/modules/access/capability-registry.d.ts.map +1 -0
  42. package/dist/modules/access/capability-registry.js +46 -0
  43. package/dist/modules/access/capability-registry.js.map +1 -0
  44. package/dist/modules/access/directory-sync-bot.d.ts +13 -0
  45. package/dist/modules/access/directory-sync-bot.d.ts.map +1 -0
  46. package/dist/modules/access/directory-sync-bot.js +64 -0
  47. package/dist/modules/access/directory-sync-bot.js.map +1 -0
  48. package/dist/modules/access/group-files.d.ts +83 -0
  49. package/dist/modules/access/group-files.d.ts.map +1 -0
  50. package/dist/modules/access/group-files.js +167 -0
  51. package/dist/modules/access/group-files.js.map +1 -0
  52. package/dist/modules/access/groups-admin.routes.d.ts +19 -0
  53. package/dist/modules/access/groups-admin.routes.d.ts.map +1 -0
  54. package/dist/modules/access/groups-admin.routes.js +98 -0
  55. package/dist/modules/access/groups-admin.routes.js.map +1 -0
  56. package/dist/modules/access/groups-admin.service.d.ts +166 -0
  57. package/dist/modules/access/groups-admin.service.d.ts.map +1 -0
  58. package/dist/modules/access/groups-admin.service.js +442 -0
  59. package/dist/modules/access/groups-admin.service.js.map +1 -0
  60. package/dist/modules/access/groups-edit.d.ts +58 -0
  61. package/dist/modules/access/groups-edit.d.ts.map +1 -0
  62. package/dist/modules/access/groups-edit.js +162 -0
  63. package/dist/modules/access/groups-edit.js.map +1 -0
  64. package/dist/modules/access/reference-scan.d.ts +141 -0
  65. package/dist/modules/access/reference-scan.d.ts.map +1 -0
  66. package/dist/modules/access/reference-scan.js +440 -0
  67. package/dist/modules/access/reference-scan.js.map +1 -0
  68. package/dist/modules/access/roles-admin.service.d.ts +88 -119
  69. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  70. package/dist/modules/access/roles-admin.service.js +230 -384
  71. package/dist/modules/access/roles-admin.service.js.map +1 -1
  72. package/dist/modules/access/roles-edit.d.ts +51 -25
  73. package/dist/modules/access/roles-edit.d.ts.map +1 -1
  74. package/dist/modules/access/roles-edit.js +133 -59
  75. package/dist/modules/access/roles-edit.js.map +1 -1
  76. package/dist/modules/access/synced-groups-committer.d.ts +28 -0
  77. package/dist/modules/access/synced-groups-committer.d.ts.map +1 -0
  78. package/dist/modules/access/synced-groups-committer.js +139 -0
  79. package/dist/modules/access/synced-groups-committer.js.map +1 -0
  80. package/dist/modules/access/synced-groups-writer.d.ts +78 -0
  81. package/dist/modules/access/synced-groups-writer.d.ts.map +1 -0
  82. package/dist/modules/access/synced-groups-writer.js +219 -0
  83. package/dist/modules/access/synced-groups-writer.js.map +1 -0
  84. package/dist/modules/database/core-schema.d.ts +17 -0
  85. package/dist/modules/database/core-schema.d.ts.map +1 -1
  86. package/dist/modules/database/core-schema.js +9 -0
  87. package/dist/modules/database/core-schema.js.map +1 -1
  88. package/dist/modules/mcp/mcp-auth.middleware.d.ts +13 -2
  89. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  90. package/dist/modules/mcp/mcp-auth.middleware.js +61 -2
  91. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  92. package/dist/modules/mcp/mcp.routes.d.ts +9 -3
  93. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  94. package/dist/modules/mcp/mcp.routes.js +126 -2
  95. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  96. package/dist/modules/mcp/mcp.service.d.ts +14 -0
  97. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  98. package/dist/modules/mcp/mcp.service.js +6 -1
  99. package/dist/modules/mcp/mcp.service.js.map +1 -1
  100. package/dist/modules/workflow/file-lock.service.d.ts +11 -1
  101. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  102. package/dist/modules/workflow/file-lock.service.js +15 -1
  103. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  104. package/dist/modules/workflow/git/git.service.d.ts +34 -11
  105. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  106. package/dist/modules/workflow/git/git.service.js +165 -37
  107. package/dist/modules/workflow/git/git.service.js.map +1 -1
  108. package/dist/modules/workflow/locking-filesystem.d.ts +4 -0
  109. package/dist/modules/workflow/locking-filesystem.d.ts.map +1 -1
  110. package/dist/modules/workflow/locking-filesystem.js +181 -28
  111. package/dist/modules/workflow/locking-filesystem.js.map +1 -1
  112. package/dist/modules/workflow/pending-commits.service.d.ts +10 -0
  113. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  114. package/dist/modules/workflow/pending-commits.service.js +19 -1
  115. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  116. package/dist/modules/workflow/workflow.service.d.ts +17 -2
  117. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  118. package/dist/modules/workflow/workflow.service.js +113 -19
  119. package/dist/modules/workflow/workflow.service.js.map +1 -1
  120. package/kb-template/AGENTS.md +4 -1
  121. package/migrations/0004_file_lock_mode.sql +2 -0
  122. package/migrations/meta/0004_snapshot.json +1564 -0
  123. package/migrations/meta/_journal.json +7 -0
  124. package/package.json +3 -3
  125. package/src/core/create-core-server.ts +13 -0
  126. package/src/core/create-core-services.ts +68 -0
  127. package/src/index.ts +10 -0
  128. package/src/modules/access/__tests__/access-control.service.test.ts +9 -3
  129. package/src/modules/access/__tests__/access-groups.test.ts +427 -0
  130. package/src/modules/access/__tests__/access-mutation.service.test.ts +171 -5
  131. package/src/modules/access/__tests__/access-splice.test.ts +65 -0
  132. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +337 -0
  133. package/src/modules/access/__tests__/access.routes.revoke.test.ts +48 -0
  134. package/src/modules/access/__tests__/admin-locked-commit.test.ts +221 -0
  135. package/src/modules/access/__tests__/admin-route-helpers.test.ts +61 -0
  136. package/src/modules/access/__tests__/directory-sync-bot.test.ts +106 -0
  137. package/src/modules/access/__tests__/grant-sources.test.ts +67 -0
  138. package/src/modules/access/__tests__/groups-admin.service.test.ts +440 -0
  139. package/src/modules/access/__tests__/reference-scan.test.ts +288 -0
  140. package/src/modules/access/__tests__/roles-admin.service.test.ts +161 -83
  141. package/src/modules/access/__tests__/roles-capabilities.test.ts +325 -0
  142. package/src/modules/access/__tests__/roles-edit.test.ts +104 -28
  143. package/src/modules/access/__tests__/roles.routes.test.ts +63 -40
  144. package/src/modules/access/__tests__/synced-groups-committer.test.ts +238 -0
  145. package/src/modules/access/__tests__/synced-groups-writer.test.ts +249 -0
  146. package/src/modules/access/access-control.interface.ts +66 -32
  147. package/src/modules/access/access-control.service.ts +536 -73
  148. package/src/modules/access/access-declarations.ts +5 -2
  149. package/src/modules/access/access-mutation.service.ts +88 -14
  150. package/src/modules/access/access-splice.ts +55 -17
  151. package/src/modules/access/access.routes.ts +227 -93
  152. package/src/modules/access/admin-locked-commit.ts +331 -0
  153. package/src/modules/access/admin-route-helpers.ts +55 -0
  154. package/src/modules/access/capability-registry.ts +74 -0
  155. package/src/modules/access/directory-sync-bot.ts +76 -0
  156. package/src/modules/access/group-files.ts +212 -0
  157. package/src/modules/access/groups-admin.routes.ts +113 -0
  158. package/src/modules/access/groups-admin.service.ts +551 -0
  159. package/src/modules/access/groups-edit.ts +187 -0
  160. package/src/modules/access/reference-scan.ts +513 -0
  161. package/src/modules/access/roles-admin.service.ts +290 -418
  162. package/src/modules/access/roles-edit.ts +134 -61
  163. package/src/modules/access/synced-groups-committer.ts +177 -0
  164. package/src/modules/access/synced-groups-writer.ts +303 -0
  165. package/src/modules/database/core-schema.ts +9 -0
  166. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +116 -0
  167. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
  168. package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +6 -1
  169. package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +301 -0
  170. package/src/modules/mcp/mcp-auth.middleware.ts +61 -1
  171. package/src/modules/mcp/mcp.routes.ts +137 -2
  172. package/src/modules/mcp/mcp.service.ts +6 -1
  173. package/src/modules/workflow/__tests__/locking-filesystem.test.ts +336 -0
  174. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
  175. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +32 -0
  176. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +13 -2
  177. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +139 -1
  178. package/src/modules/workflow/file-lock.service.ts +15 -0
  179. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +0 -1
  180. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +132 -0
  181. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +0 -1
  182. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +0 -1
  183. package/src/modules/workflow/git/git.service.ts +174 -35
  184. package/src/modules/workflow/locking-filesystem.ts +188 -26
  185. package/src/modules/workflow/pending-commits.service.ts +27 -1
  186. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +0 -1
  187. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +0 -1
  188. package/src/modules/workflow/workflow.service.ts +140 -20
@@ -0,0 +1,440 @@
1
+ /**
2
+ * SHARED grant-reference machinery — the single implementation both admin
3
+ * services (roles + groups) drive for "which access rules name this
4
+ * principal" and "rewrite every reference atomically":
5
+ *
6
+ * - `findRoleRefsInText` / `rewriteRoleTokensInText`: the config-region
7
+ * parse that decides what IS a principal reference. One source of truth,
8
+ * so the delete/rename warning and the rename rewrite can never disagree.
9
+ * - `KbReferenceScanner`: candidate collection (every KB file with an
10
+ * access-frontmatter extension — `.md` and `.tool`, the resolver's own
11
+ * set), the advisory reference scan (cached — roster mutations must not
12
+ * rerun a full-KB read sweep, invalidated by file-changed events and a
13
+ * TTL backstop), and the fail-closed reference rewrite (returns each
14
+ * file's ORIGINAL text too, so rollback snapshots reuse the read the
15
+ * rewrite already did instead of a second full-KB pass).
16
+ *
17
+ * Tokens here are the ENTRY-GRAMMAR canonical tokens: a bare name (group
18
+ * first, then role) or an explicit `role/<name>`. Callers translate: a ROLE's
19
+ * references are its bare token plus its `role/` token; a GROUP's are its
20
+ * bare token only.
21
+ */
22
+ import path from 'node:path';
23
+ import { promises as fs } from 'node:fs';
24
+ import { KNOWN_VERBS, accessMdDeclaresBodyRules, hasAccessFrontmatterExtension, isAccessMdPath, parseAccessEntry, stripComment, } from './access-control.service.js';
25
+ /**
26
+ * Resolve the [start, end) line range that role rewrites may touch — the YAML
27
+ * config region only, NEVER the markdown body (CodeRabbit: a body line like
28
+ * `- Sales` or `owner: Sales` must not be rewritten).
29
+ *
30
+ * - A file with leading `---` frontmatter (folder `access.md`, node `.md`):
31
+ * only the lines BETWEEN the opening and closing `---` are eligible.
32
+ * - A fence-less file (e.g. a bare `roles.yaml`-style access config with no
33
+ * `---`): the whole file is config, so all lines are eligible.
34
+ * - A `.md` file with no frontmatter fence: no config region → empty range,
35
+ * nothing is rewritten.
36
+ */
37
+ function configLineRange(lines, isMarkdown) {
38
+ if (lines.length > 0 && lines[0].trim() === '---') {
39
+ for (let i = 1; i < lines.length; i++) {
40
+ if (lines[i].trim() === '---')
41
+ return { start: 1, end: i };
42
+ }
43
+ // Unterminated frontmatter — treat nothing as eligible (don't risk the body).
44
+ return { start: 0, end: 0 };
45
+ }
46
+ // No fence: a markdown file has no config region; a non-markdown access file
47
+ // (no body) is entirely config.
48
+ return isMarkdown ? { start: 0, end: 0 } : { start: 0, end: lines.length };
49
+ }
50
+ /** The line range AFTER the closing frontmatter fence — empty when there is
51
+ * no fence or it never closes, mirroring `bodyAfterFrontmatter` (a
52
+ * fence-less access.md is a hard parse error to the resolver, so it is not
53
+ * a rule source and must not be rewritten). */
54
+ function bodyLineRange(lines) {
55
+ if (lines.length > 0 && lines[0].trim() === '---') {
56
+ for (let i = 1; i < lines.length; i++) {
57
+ if (lines[i].trim() === '---')
58
+ return { start: i + 1, end: lines.length };
59
+ }
60
+ }
61
+ return { start: 0, end: 0 };
62
+ }
63
+ /**
64
+ * The line ranges that are RULE SOURCES for this file — the ranges the
65
+ * resolver actually parses rules from, so the scan/rewrite can never miss a
66
+ * rule the resolver enforces:
67
+ *
68
+ * - A body-governed `access.md` (see {@link accessMdDeclaresBodyRules}):
69
+ * the BODY carries the folder's rules and the frontmatter carries the
70
+ * file's own rules — both are rule sources.
71
+ * - Everything else: the config region as before (frontmatter for markdown,
72
+ * the whole file for a fence-less config file). A legacy `access.md`'s
73
+ * body and a node file's body are prose and stay untouchable.
74
+ */
75
+ function ruleLineRanges(text, lines, isMarkdown, isAccessMd) {
76
+ if (isAccessMd && accessMdDeclaresBodyRules(text)) {
77
+ return [configLineRange(lines, true), bodyLineRange(lines)];
78
+ }
79
+ return [configLineRange(lines, isMarkdown)];
80
+ }
81
+ /** The access verb keys the resolver reads (kept in lockstep via KNOWN_VERBS). */
82
+ const VERB_KEYS = new Set(KNOWN_VERBS);
83
+ /**
84
+ * Walk the CONFIG-REGION lines of `text`, invoking `onRoleRef` for every line
85
+ * that PARSES as a genuine role entry — both the block-list form (`- <token>`
86
+ * under a ROOT-mapping `read:`/`write:`/… key) and the inline scalar form
87
+ * (`owner: <token>`). `verb` is the access verb the reference sits under.
88
+ * This is the SINGLE source of truth for "what is a role reference" — both
89
+ * the delete-warning scan and the rename rewrite drive off it, so they cannot
90
+ * disagree. The callback may mutate `lines[i]` (the rewrite does; the scan
91
+ * does not). User entries, other keys, nested mappings' verb-looking keys,
92
+ * comments and substrings never fire it.
93
+ *
94
+ * "Root" follows the resolver's OWN semantics, not column zero: the
95
+ * YAML-subset parser's root frame sits at indent -1 (`parseYamlSubset`), so a
96
+ * uniformly-INDENTED root mapping (` read:` at column 2 with nothing
97
+ * enclosing it) is a live rule mapping the resolver enforces — a column-zero
98
+ * regex would silently skip it, and a rename would strand those grants. This
99
+ * walker mirrors the parser's frame stack: a key whose enclosing frame is the
100
+ * root is a root key; a verb key nested under some other mapping is not a
101
+ * rule. On a STRUCTURAL error the resolver rejects the whole region (an
102
+ * access.md hard-errors; node frontmatter yields no own-entries), so the walk
103
+ * stops — nothing there is an enforced rule.
104
+ *
105
+ * Comments follow the resolver's tokeniser rule (`stripComment`): they are
106
+ * invisible to MATCHING — a trailing `# note` never becomes part of the
107
+ * token, and a full-line comment neither ends a block nor is an entry — but
108
+ * the rewrite preserves them: `suffix` carries the stripped tail (trailing
109
+ * whitespace + comment) verbatim for re-append.
110
+ */
111
+ function walkRoleRefs(lines, start, end, onRoleRef) {
112
+ /** A role-entry value → its parsed role entry, else null (user/empty/other). */
113
+ const roleEntry = (rawValue) => {
114
+ const parsed = parseAccessEntry(rawValue);
115
+ return parsed.ok && parsed.entry.kind === 'role'
116
+ ? { role: parsed.entry.role, deny: parsed.entry.deny }
117
+ : null;
118
+ };
119
+ const stack = [{ indent: -1, kind: 'map', verb: null }];
120
+ for (let i = start; i < end; i++) {
121
+ const raw = lines[i];
122
+ // Match against the COMMENT-STRIPPED line — the resolver's tokeniser view.
123
+ // `suffix` is everything stripping removed (trailing whitespace + comment),
124
+ // kept so a rewrite can re-append it byte-for-byte.
125
+ const line = stripComment(raw).replace(/\s+$/, '');
126
+ const suffix = raw.slice(line.length);
127
+ // A full-line comment (or blank line) is invisible: not an entry, and it
128
+ // does NOT end the current block — the tokeniser skips it entirely.
129
+ if (!line.trim())
130
+ continue;
131
+ // Same indent rule as the tokeniser: leading SPACES only.
132
+ const indent = /^( *)/.exec(line)[1].length;
133
+ const content = line.slice(indent);
134
+ while (stack.length > 1 && stack[stack.length - 1].indent >= indent)
135
+ stack.pop();
136
+ const top = stack[stack.length - 1];
137
+ if (content === '-' || content.startsWith('- ')) {
138
+ if (top.kind === 'pending')
139
+ top.kind = 'list';
140
+ // An item with no enclosing list is a structural error — the resolver
141
+ // rejects the whole region, so nothing here is an enforced rule.
142
+ if (top.kind !== 'list')
143
+ return;
144
+ if (top.verb !== null) {
145
+ const rest = content.slice(2);
146
+ const ws = /^\s*/.exec(rest)[0];
147
+ const entry = roleEntry(rest.trim());
148
+ if (entry) {
149
+ onRoleRef({
150
+ i,
151
+ verb: top.verb,
152
+ entry,
153
+ indent: '',
154
+ prefix: line.slice(0, indent + 2) + ws,
155
+ suffix,
156
+ });
157
+ }
158
+ }
159
+ continue;
160
+ }
161
+ const colonIdx = content.indexOf(':');
162
+ // Not a `key:` or `- value` line, or an empty key: a structural error the
163
+ // resolver rejects — stop, this region enforces nothing.
164
+ if (colonIdx < 0)
165
+ return;
166
+ const key = content.slice(0, colonIdx).trim();
167
+ if (!key)
168
+ return;
169
+ if (top.kind === 'pending') {
170
+ // The previous key's value turned out to be a nested MAPPING.
171
+ top.kind = 'map';
172
+ top.verb = null;
173
+ }
174
+ // A kv inside a list is a structural error (mapping key inside a list).
175
+ if (top.kind !== 'map')
176
+ return;
177
+ // Root per the parser's frames — NOT "column zero".
178
+ const verbKey = stack.length === 1 && VERB_KEYS.has(key) ? key : null;
179
+ const value = content.slice(colonIdx + 1).trim();
180
+ if (value !== '') {
181
+ // Inline value — no frame opens. A scalar under a root verb key is the
182
+ // inline scalar reference form (`owner: Sales`); `[]` is an empty list.
183
+ if (verbKey && value !== '[]') {
184
+ const afterColon = content.slice(colonIdx + 1);
185
+ const ws = /^\s*/.exec(afterColon)[0];
186
+ const entry = roleEntry(value);
187
+ if (entry) {
188
+ onRoleRef({
189
+ i,
190
+ verb: verbKey,
191
+ entry,
192
+ indent: line.slice(0, indent + colonIdx + 1) + ws,
193
+ prefix: '',
194
+ suffix,
195
+ });
196
+ }
197
+ }
198
+ continue;
199
+ }
200
+ // Empty value: what it opens (list/map/null) is decided by the next token.
201
+ stack.push({ indent, kind: 'pending', verb: verbKey });
202
+ }
203
+ }
204
+ /**
205
+ * Every genuine role/group reference in `text`'s rule regions, as
206
+ * {role, verb} where `role` is the canonical ENTRY TOKEN (bare name or
207
+ * `role/<name>`). `isAccessMd` marks an `access.md` file, whose BODY is a
208
+ * rule source in the body-governed format — see {@link ruleLineRanges}.
209
+ */
210
+ export function findRoleRefsInText(text, isMarkdown = true, isAccessMd = false) {
211
+ const lines = text.split('\n');
212
+ const out = [];
213
+ for (const { start, end } of ruleLineRanges(text, lines, isMarkdown, isAccessMd)) {
214
+ if (start >= end)
215
+ continue;
216
+ walkRoleRefs(lines, start, end, ({ verb, entry }) => out.push({ role: entry.role, verb }));
217
+ }
218
+ return out;
219
+ }
220
+ /**
221
+ * Rewrite every RULE-REGION line that PARSES as a role reference whose
222
+ * canonical token == `oldToken`, replacing the token with `newDisplayName`
223
+ * (preserving any leading `deny ` and indentation). A prose body is never
224
+ * touched — a line like `- Sales` in a node file or a legacy `access.md`
225
+ * stays byte-for-byte intact; the body is eligible ONLY when the file is a
226
+ * body-governed `access.md` (`isAccessMd` + the body parses as rules), where
227
+ * the body IS what the resolver enforces — see {@link ruleLineRanges}. Lines
228
+ * that don't parse as a matching role entry (user entries, other keys,
229
+ * substrings) are also untouched. Exported for test.
230
+ *
231
+ * `isMarkdown` (default true) marks files that carry a markdown body below the
232
+ * frontmatter; pass false only for a pure-config file with no body.
233
+ */
234
+ export function rewriteRoleTokensInText(text, oldToken, newDisplayName, isMarkdown = true, isAccessMd = false) {
235
+ const lines = text.split('\n');
236
+ let changed = false;
237
+ for (const { start, end } of ruleLineRanges(text, lines, isMarkdown, isAccessMd)) {
238
+ if (start >= end)
239
+ continue;
240
+ walkRoleRefs(lines, start, end, ({ i, entry, indent, prefix, suffix }) => {
241
+ if (entry.role !== oldToken)
242
+ return;
243
+ // `suffix` re-appends the trailing whitespace + `# comment` the match
244
+ // ignored — a rename must not eat an entry's comment.
245
+ lines[i] = `${indent}${prefix}${entry.deny ? 'deny ' : ''}${newDisplayName}${suffix}`;
246
+ changed = true;
247
+ });
248
+ }
249
+ return changed ? lines.join('\n') : text;
250
+ }
251
+ /** How long a cached reference scan may serve after its load. Backstop only —
252
+ * reference-changing writes invalidate explicitly (the admin services after
253
+ * their own rewrites, and the event-bus tap below for everyone else's). */
254
+ const SCAN_TTL_MS = 30_000;
255
+ /**
256
+ * Candidate collection + cached scan + fail-closed rewrite over one KB.
257
+ * Constructed per admin service; the cache means roster MUTATIONS never rerun
258
+ * the full-KB reference sweep (only the roster GET pays it, at most once per
259
+ * TTL), and `invalidate` drops it whenever a write could have moved
260
+ * references (rename/delete rewrites, or any external change signal).
261
+ *
262
+ * FRESHNESS: an `access.md` grant/revoke (or any other write to a scanned
263
+ * file) lands OUTSIDE the admin services, so their explicit invalidations
264
+ * can't see it — the roster's `referencedBy` would serve up to `SCAN_TTL_MS`
265
+ * of staleness. When an `eventBus` is provided, the scanner taps its emits
266
+ * and drops the workspace's cache on every path-carrying event
267
+ * (`file-changed` from the commit pipeline, and `lock-released` — which the
268
+ * share routes emit synchronously at write time, before the async commit's
269
+ * `file-changed` lands) whose path has a scanned extension. The TTL stays as
270
+ * the backstop for deployments/tests without a bus.
271
+ */
272
+ export class KbReferenceScanner {
273
+ workspaceService;
274
+ kbDirName;
275
+ cache = new Map();
276
+ /**
277
+ * Per-workspace in-flight scan state: a scan may only CACHE its result if
278
+ * no invalidation landed after it started — otherwise the pre-change
279
+ * snapshot would repopulate the cache and stick for a whole TTL. Tracked
280
+ * ONLY while scans run (a future scan reads post-write state by nature, so
281
+ * an invalidation with nothing in flight needs only the cache drop): the
282
+ * entry is created by the first concurrent scan and removed by the last,
283
+ * so the map never accumulates workspaces.
284
+ */
285
+ inFlight = new Map();
286
+ constructor(workspaceService, kbDirName, eventBus) {
287
+ this.workspaceService = workspaceService;
288
+ this.kbDirName = kbDirName;
289
+ eventBus?.onEmit?.((event) => {
290
+ if (event.kind !== 'file-changed' && event.kind !== 'lock-released')
291
+ return;
292
+ if (!event.workspaceId || !event.path)
293
+ return;
294
+ // Paths on the bus are workspace-relative; only writes UNDER the KB dir
295
+ // to files the scan reads can move references — a `.md` elsewhere in
296
+ // the workspace is not scan material.
297
+ if (!event.path.startsWith(`${this.kbDirName}/`))
298
+ return;
299
+ if (!hasAccessFrontmatterExtension(event.path))
300
+ return;
301
+ this.invalidate(event.workspaceId);
302
+ });
303
+ }
304
+ invalidate(workspaceId) {
305
+ this.cache.delete(workspaceId);
306
+ const flight = this.inFlight.get(workspaceId);
307
+ if (flight)
308
+ flight.gen++;
309
+ }
310
+ /**
311
+ * Repo-relative paths of every file that could carry a principal reference:
312
+ * all `access.md` files plus every node file whose own frontmatter the
313
+ * resolver reads access verbs from — the shared
314
+ * `ACCESS_FRONTMATTER_EXTENSIONS` set (`.md` AND `.tool`; a rename that
315
+ * skipped `.tool` would strand a live frontmatter grant there). Sourced
316
+ * from the workspace file tree (which already skips `.git` and honours
317
+ * `.bevelignore`), filtered under the KB dir and returned bare
318
+ * repo-relative. (`roles.yaml` matches no scanned extension, so it's
319
+ * excluded; callers commit it separately.) Under save=share the working
320
+ * tree matches the committed set, so this is the same candidate list
321
+ * git-tracking would give.
322
+ */
323
+ async collectCandidateFiles(workspaceId) {
324
+ const tree = await this.workspaceService.listFiles(workspaceId);
325
+ const prefix = `${this.kbDirName}/`;
326
+ const out = [];
327
+ const visit = (node) => {
328
+ if (node.type === 'file') {
329
+ if (node.relativePath.startsWith(prefix) && hasAccessFrontmatterExtension(node.relativePath)) {
330
+ out.push(node.relativePath.slice(prefix.length));
331
+ }
332
+ return;
333
+ }
334
+ for (const child of node.children ?? [])
335
+ visit(child);
336
+ };
337
+ visit(tree);
338
+ return out;
339
+ }
340
+ /**
341
+ * Sound scan of EVERY candidate file (folder `access.md` + `.md`/`.tool`
342
+ * node frontmatter) for genuine principal references, indexed by canonical
343
+ * entry TOKEN. Shares the
344
+ * candidate set and the config-region parse with the rename rewrite, so the
345
+ * delete warning and the rewrite see the SAME references. A file we cannot
346
+ * read is skipped (this is an advisory read, not the atomic write path).
347
+ * Cached per workspace (see class doc); reads are batched with Promise.all
348
+ * rather than serial per-file awaits.
349
+ */
350
+ async scan(workspaceId) {
351
+ const hit = this.cache.get(workspaceId);
352
+ if (hit && Date.now() - hit.loadedAt < SCAN_TTL_MS)
353
+ return hit.byToken;
354
+ let flight = this.inFlight.get(workspaceId);
355
+ if (!flight) {
356
+ flight = { gen: 0, scans: 0 };
357
+ this.inFlight.set(workspaceId, flight);
358
+ }
359
+ flight.scans++;
360
+ const startedUnder = flight.gen;
361
+ try {
362
+ const repoDir = await this.repoDir(workspaceId);
363
+ const candidates = await this.collectCandidateFiles(workspaceId);
364
+ const texts = await Promise.all(candidates.map(async (repoRel) => {
365
+ try {
366
+ return await fs.readFile(path.join(repoDir, repoRel), 'utf-8');
367
+ }
368
+ catch {
369
+ return null;
370
+ }
371
+ }));
372
+ const byToken = new Map();
373
+ candidates.forEach((repoRel, i) => {
374
+ const text = texts[i];
375
+ if (text === null)
376
+ return;
377
+ for (const ref of findRoleRefsInText(text, true, isAccessMdPath(repoRel))) {
378
+ const list = byToken.get(ref.role);
379
+ if (list)
380
+ list.push({ path: repoRel, verb: ref.verb });
381
+ else
382
+ byToken.set(ref.role, [{ path: repoRel, verb: ref.verb }]);
383
+ }
384
+ });
385
+ // Cache only when no invalidation landed mid-scan; the caller still gets
386
+ // this snapshot (at worst one write stale — same as the pre-scan world),
387
+ // but a stale snapshot must never STICK for a TTL. The next call re-scans.
388
+ if (flight.gen === startedUnder) {
389
+ this.cache.set(workspaceId, { loadedAt: Date.now(), byToken });
390
+ }
391
+ return byToken;
392
+ }
393
+ finally {
394
+ flight.scans--;
395
+ if (flight.scans === 0)
396
+ this.inFlight.delete(workspaceId);
397
+ }
398
+ }
399
+ /**
400
+ * Find every genuine reference to `oldToken` and rewrite it to
401
+ * `newDisplayName`. FAIL-CLOSED: a candidate we cannot read might reference
402
+ * the old token, and skipping it would commit a partial rewrite (a
403
+ * half-renamed principal still pointed at by stragglers = silent access
404
+ * drop) — so any read failure aborts via `makeError` with nothing written.
405
+ * Returns the writes WITH each file's original text (rollback snapshots).
406
+ */
407
+ async rewriteReferences(workspaceId, oldToken, newDisplayName, makeError) {
408
+ const repoDir = await this.repoDir(workspaceId);
409
+ const writes = [];
410
+ const candidates = await this.collectCandidateFiles(workspaceId);
411
+ const texts = await Promise.all(candidates.map(async (repoRel) => {
412
+ try {
413
+ return { ok: true, text: await fs.readFile(path.join(repoDir, repoRel), 'utf-8') };
414
+ }
415
+ catch (err) {
416
+ return { ok: false, repoRel, cause: err?.message };
417
+ }
418
+ }));
419
+ for (const t of texts) {
420
+ if (!t.ok) {
421
+ throw makeError(`Cannot read ${t.repoRel} while rewriting references; rename aborted with no changes`, t.cause);
422
+ }
423
+ }
424
+ candidates.forEach((repoRel, i) => {
425
+ const entry = texts[i];
426
+ if (!entry.ok)
427
+ return; // unreachable — the loop above threw
428
+ const rewritten = rewriteRoleTokensInText(entry.text, oldToken, newDisplayName, true, isAccessMdPath(repoRel));
429
+ if (rewritten !== entry.text) {
430
+ writes.push({ repoRelativePath: repoRel, content: rewritten, original: entry.text });
431
+ }
432
+ });
433
+ return writes;
434
+ }
435
+ async repoDir(workspaceId) {
436
+ const wsDir = await this.workspaceService.getWorkspacePath(workspaceId);
437
+ return path.join(wsDir, this.kbDirName);
438
+ }
439
+ }
440
+ //# sourceMappingURL=reference-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-scan.js","sourceRoot":"","sources":["../../../src/modules/access/reference-scan.ts"],"names":[],"mappings":"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"}