@bevel-software/platform-core-backend 0.9.1 → 0.11.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/THIRD-PARTY-NOTICES.md +2 -2
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +16 -1
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +25 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +45 -1
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core-config.d.ts +19 -1
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +44 -4
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +54 -28
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +129 -14
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +435 -66
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts.map +1 -1
- package/dist/modules/access/access-declarations.js +5 -3
- package/dist/modules/access/access-declarations.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +39 -6
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +78 -18
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-splice.d.ts +31 -4
- package/dist/modules/access/access-splice.d.ts.map +1 -1
- package/dist/modules/access/access-splice.js +40 -16
- package/dist/modules/access/access-splice.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +204 -82
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts +134 -0
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -0
- package/dist/modules/access/admin-locked-commit.js +277 -0
- package/dist/modules/access/admin-locked-commit.js.map +1 -0
- package/dist/modules/access/admin-route-helpers.d.ts +32 -0
- package/dist/modules/access/admin-route-helpers.d.ts.map +1 -0
- package/dist/modules/access/admin-route-helpers.js +44 -0
- package/dist/modules/access/admin-route-helpers.js.map +1 -0
- package/dist/modules/access/capability-registry.d.ts +41 -0
- package/dist/modules/access/capability-registry.d.ts.map +1 -0
- package/dist/modules/access/capability-registry.js +46 -0
- package/dist/modules/access/capability-registry.js.map +1 -0
- package/dist/modules/access/directory-sync-bot.d.ts +13 -0
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -0
- package/dist/modules/access/directory-sync-bot.js +64 -0
- package/dist/modules/access/directory-sync-bot.js.map +1 -0
- package/dist/modules/access/group-files.d.ts +83 -0
- package/dist/modules/access/group-files.d.ts.map +1 -0
- package/dist/modules/access/group-files.js +167 -0
- package/dist/modules/access/group-files.js.map +1 -0
- package/dist/modules/access/groups-admin.routes.d.ts +19 -0
- package/dist/modules/access/groups-admin.routes.d.ts.map +1 -0
- package/dist/modules/access/groups-admin.routes.js +98 -0
- package/dist/modules/access/groups-admin.routes.js.map +1 -0
- package/dist/modules/access/groups-admin.service.d.ts +166 -0
- package/dist/modules/access/groups-admin.service.d.ts.map +1 -0
- package/dist/modules/access/groups-admin.service.js +442 -0
- package/dist/modules/access/groups-admin.service.js.map +1 -0
- package/dist/modules/access/groups-edit.d.ts +58 -0
- package/dist/modules/access/groups-edit.d.ts.map +1 -0
- package/dist/modules/access/groups-edit.js +162 -0
- package/dist/modules/access/groups-edit.js.map +1 -0
- package/dist/modules/access/reference-scan.d.ts +141 -0
- package/dist/modules/access/reference-scan.d.ts.map +1 -0
- package/dist/modules/access/reference-scan.js +440 -0
- package/dist/modules/access/reference-scan.js.map +1 -0
- package/dist/modules/access/roles-admin.service.d.ts +88 -119
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +230 -384
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/roles-edit.d.ts +51 -25
- package/dist/modules/access/roles-edit.d.ts.map +1 -1
- package/dist/modules/access/roles-edit.js +133 -59
- package/dist/modules/access/roles-edit.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.d.ts +28 -0
- package/dist/modules/access/synced-groups-committer.d.ts.map +1 -0
- package/dist/modules/access/synced-groups-committer.js +139 -0
- package/dist/modules/access/synced-groups-committer.js.map +1 -0
- package/dist/modules/access/synced-groups-writer.d.ts +78 -0
- package/dist/modules/access/synced-groups-writer.d.ts.map +1 -0
- package/dist/modules/access/synced-groups-writer.js +219 -0
- package/dist/modules/access/synced-groups-writer.js.map +1 -0
- package/dist/modules/database/core-schema.d.ts +17 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +9 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +13 -2
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +61 -2
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +9 -3
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +126 -2
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +14 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +6 -1
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/update-check/update-check.routes.d.ts +13 -0
- package/dist/modules/update-check/update-check.routes.d.ts.map +1 -0
- package/dist/modules/update-check/update-check.routes.js +21 -0
- package/dist/modules/update-check/update-check.routes.js.map +1 -0
- package/dist/modules/update-check/update-check.service.d.ts +57 -0
- package/dist/modules/update-check/update-check.service.d.ts.map +1 -0
- package/dist/modules/update-check/update-check.service.js +109 -0
- package/dist/modules/update-check/update-check.service.js.map +1 -0
- package/dist/modules/workflow/file-lock.service.d.ts +11 -1
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +15 -1
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +34 -11
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +173 -37
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/locking-filesystem.d.ts +4 -0
- package/dist/modules/workflow/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/workflow/locking-filesystem.js +181 -28
- package/dist/modules/workflow/locking-filesystem.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +10 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +19 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +17 -2
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +113 -19
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/version.d.ts +14 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +25 -0
- package/dist/version.js.map +1 -1
- package/kb-template/AGENTS.md +4 -1
- package/migrations/0004_file_lock_mode.sql +2 -0
- package/migrations/meta/0004_snapshot.json +1564 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +4 -4
- package/src/__tests__/core-config.domain.test.ts +92 -0
- package/src/core/create-core-server.ts +21 -0
- package/src/core/create-core-services.ts +82 -0
- package/src/core-config.ts +48 -4
- package/src/index.ts +10 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +9 -3
- package/src/modules/access/__tests__/access-groups.test.ts +427 -0
- package/src/modules/access/__tests__/access-mutation.service.test.ts +171 -5
- package/src/modules/access/__tests__/access-splice.test.ts +65 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +337 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +48 -0
- package/src/modules/access/__tests__/admin-locked-commit.test.ts +221 -0
- package/src/modules/access/__tests__/admin-route-helpers.test.ts +61 -0
- package/src/modules/access/__tests__/directory-sync-bot.test.ts +106 -0
- package/src/modules/access/__tests__/grant-sources.test.ts +67 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +440 -0
- package/src/modules/access/__tests__/reference-scan.test.ts +288 -0
- package/src/modules/access/__tests__/roles-admin.service.test.ts +161 -83
- package/src/modules/access/__tests__/roles-capabilities.test.ts +325 -0
- package/src/modules/access/__tests__/roles-edit.test.ts +104 -28
- package/src/modules/access/__tests__/roles.routes.test.ts +63 -40
- package/src/modules/access/__tests__/synced-groups-committer.test.ts +238 -0
- package/src/modules/access/__tests__/synced-groups-writer.test.ts +249 -0
- package/src/modules/access/access-control.interface.ts +66 -32
- package/src/modules/access/access-control.service.ts +536 -73
- package/src/modules/access/access-declarations.ts +5 -2
- package/src/modules/access/access-mutation.service.ts +88 -14
- package/src/modules/access/access-splice.ts +55 -17
- package/src/modules/access/access.routes.ts +227 -93
- package/src/modules/access/admin-locked-commit.ts +331 -0
- package/src/modules/access/admin-route-helpers.ts +55 -0
- package/src/modules/access/capability-registry.ts +74 -0
- package/src/modules/access/directory-sync-bot.ts +76 -0
- package/src/modules/access/group-files.ts +212 -0
- package/src/modules/access/groups-admin.routes.ts +113 -0
- package/src/modules/access/groups-admin.service.ts +551 -0
- package/src/modules/access/groups-edit.ts +187 -0
- package/src/modules/access/reference-scan.ts +513 -0
- package/src/modules/access/roles-admin.service.ts +290 -418
- package/src/modules/access/roles-edit.ts +134 -61
- package/src/modules/access/synced-groups-committer.ts +177 -0
- package/src/modules/access/synced-groups-writer.ts +303 -0
- package/src/modules/database/core-schema.ts +9 -0
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +116 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
- package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +6 -1
- package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +301 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +61 -1
- package/src/modules/mcp/mcp.routes.ts +137 -2
- package/src/modules/mcp/mcp.service.ts +6 -1
- package/src/modules/update-check/__tests__/update-check.routes.test.ts +69 -0
- package/src/modules/update-check/__tests__/update-check.service.test.ts +170 -0
- package/src/modules/update-check/update-check.routes.ts +28 -0
- package/src/modules/update-check/update-check.service.ts +130 -0
- package/src/modules/workflow/__tests__/locking-filesystem.test.ts +336 -0
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +32 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +13 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +139 -1
- package/src/modules/workflow/file-lock.service.ts +15 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +0 -1
- package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +132 -0
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +0 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +0 -1
- package/src/modules/workflow/git/git.service.ts +182 -35
- package/src/modules/workflow/locking-filesystem.ts +188 -26
- package/src/modules/workflow/pending-commits.service.ts +27 -1
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +0 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +0 -1
- package/src/modules/workflow/workflow.service.ts +140 -20
- package/src/version.ts +28 -0
|
@@ -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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"}
|