@bevel-software/platform-shared 0.4.1 → 0.5.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.
@@ -3,6 +3,14 @@ export interface AuthUser {
3
3
  email: string;
4
4
  name: string;
5
5
  avatarUrl?: string;
6
+ /**
7
+ * Has this person concluded the connect-your-agent onboarding (welcome
8
+ * page Done, or dismissing the reminder pill). Optional so pre-existing
9
+ * fixtures and cached user objects stay valid; the server always sends it,
10
+ * and consumers treat only an explicit `false` as "still onboarding" — an
11
+ * absent field must never resurrect the welcome flow.
12
+ */
13
+ onboardingDone?: boolean;
6
14
  }
7
15
  export interface LoginRequest {
8
16
  email: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;CAChB"}
@@ -25,4 +25,27 @@ export declare function branchAuthorLocalpart(email: string | null | undefined):
25
25
  * happens to start with their own localpart. Substring is not authorship.
26
26
  */
27
27
  export declare function isBranchAuthoredBy(branchName: string, email: string | null | undefined): boolean;
28
+ /** Keep only characters git branch segments accept; collapse the rest to '-'. */
29
+ export declare function branchSegment(raw: string): string;
30
+ /**
31
+ * The `suggestions/<who>-<id8>/` prefix every one of this user's suggestion
32
+ * branches carries (`…/knowledge` for the Knowledge bundle, a skill segment
33
+ * for skill proposals). SHARED so the client that names the branch and the
34
+ * server that judges "may this caller delete it" can never disagree. The
35
+ * user-id slice is what makes the identity collision-proof — `branchSegment`
36
+ * alone is lossy (`alex+ops@…` and `alex-ops@…` both clean to `alex-ops`).
37
+ */
38
+ export declare function suggestionsBranchPrefixFor(user: {
39
+ email: string;
40
+ id: string;
41
+ }): string;
42
+ /**
43
+ * True when `branchName` is one of THIS user's suggestion branches — the
44
+ * authorship rule for the `suggestions/…` namespace, where the plain
45
+ * `<localpart>/` convention of {@link isBranchAuthoredBy} does not apply.
46
+ */
47
+ export declare function isOwnSuggestionsBranch(branchName: string, user: {
48
+ email: string | null | undefined;
49
+ id: string | null | undefined;
50
+ }): boolean;
28
51
  //# sourceMappingURL=branchAuthor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"branchAuthor.d.ts","sourceRoot":"","sources":["../../src/git/branchAuthor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAIT"}
1
+ {"version":3,"file":"branchAuthor.d.ts","sourceRoot":"","sources":["../../src/git/branchAuthor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAIT;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAItF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,GACxE,OAAO,CAGT"}
@@ -42,4 +42,36 @@ export function isBranchAuthoredBy(branchName, email) {
42
42
  return false;
43
43
  return branchName.startsWith(`${localpart}/`);
44
44
  }
45
+ /** Keep only characters git branch segments accept; collapse the rest to '-'. */
46
+ export function branchSegment(raw) {
47
+ const cleaned = raw
48
+ .toLowerCase()
49
+ .replace(/[^a-z0-9._-]+/g, '-')
50
+ .replace(/^[-.]+/, '')
51
+ .replace(/[.]+$/, '');
52
+ return cleaned || 'user';
53
+ }
54
+ /**
55
+ * The `suggestions/<who>-<id8>/` prefix every one of this user's suggestion
56
+ * branches carries (`…/knowledge` for the Knowledge bundle, a skill segment
57
+ * for skill proposals). SHARED so the client that names the branch and the
58
+ * server that judges "may this caller delete it" can never disagree. The
59
+ * user-id slice is what makes the identity collision-proof — `branchSegment`
60
+ * alone is lossy (`alex+ops@…` and `alex-ops@…` both clean to `alex-ops`).
61
+ */
62
+ export function suggestionsBranchPrefixFor(user) {
63
+ const who = branchSegment(user.email.split('@')[0]);
64
+ const id = branchSegment(user.id).slice(0, 8) || 'user';
65
+ return `suggestions/${who}-${id}/`;
66
+ }
67
+ /**
68
+ * True when `branchName` is one of THIS user's suggestion branches — the
69
+ * authorship rule for the `suggestions/…` namespace, where the plain
70
+ * `<localpart>/` convention of {@link isBranchAuthoredBy} does not apply.
71
+ */
72
+ export function isOwnSuggestionsBranch(branchName, user) {
73
+ if (!user.email || !user.id)
74
+ return false;
75
+ return branchName.startsWith(suggestionsBranchPrefixFor({ email: user.email, id: user.id }));
76
+ }
45
77
  //# sourceMappingURL=branchAuthor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"branchAuthor.js","sourceRoot":"","sources":["../../src/git/branchAuthor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAgC;IAEhC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IACnE,gFAAgF;IAChF,oCAAoC;IACpC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAkB,EAClB,KAAgC;IAEhC,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"branchAuthor.js","sourceRoot":"","sources":["../../src/git/branchAuthor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAgC;IAEhC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IACnE,gFAAgF;IAChF,oCAAoC;IACpC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAkB,EAClB,KAAgC;IAEhC,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,OAAO,GAAG,GAAG;SAChB,WAAW,EAAE;SACb,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACxB,OAAO,OAAO,IAAI,MAAM,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAmC;IAC5E,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC;IACxD,OAAO,eAAe,GAAG,IAAI,EAAE,GAAG,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAkB,EAClB,IAAyE;IAEzE,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,UAAU,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/F,CAAC"}
@@ -1,40 +1,62 @@
1
1
  /**
2
- * Authoritative, env-overridable registry of the default branch + the set of
3
- * protected branch slugs in the KB repo, plus their user-visible display names.
2
+ * The default branch + the set of protected branch slugs in the KB repo, and
3
+ * their user-visible display names. Single source of truth for BOTH sides of
4
+ * the app, so backend enforcement and frontend affordances can never drift.
4
5
  *
5
- * Single source of truth for BOTH sides of the app:
6
- * - Backend (Node) reads `process.env.*` at runtime — the values are populated
7
- * from the environment / the `.env` loaded by `config.ts`, which runs before
8
- * this module is first evaluated (see `backend/src/index.ts` import order).
9
- * - Frontend (browser) can't read `process.env` at runtime, so Vite statically
10
- * replaces the `process.env.DEFAULT_BRANCH` / `process.env.PROTECTED_BRANCHES`
11
- * references at build time via `define` in `frontend/vite.config.ts`. Keep
12
- * those references written as the literal `process.env.<NAME>` member access
13
- * so the static replacement keeps matching.
6
+ * CONFIGURED, NOT IMPORTED. This module used to read `process.env` at import
7
+ * time and throw when it found nothing — which made the branch model something
8
+ * a deployment had to know before any code could load. On the frontend that
9
+ * meant baking it into the bundle at build time (Vite `define`), so the same
10
+ * artifact could not serve two deployments and changing a branch name meant a
11
+ * rebuild. Both sides now CALL {@link configureBranchModel} during boot:
12
+ * the backend from its environment, the browser from `GET /api/config`.
14
13
  *
15
- * Both the backend (server-side enforcement — refusing commit/push/revert and
16
- * rejecting `createBranch` on protected names) and the frontend (disabling UI
17
- * actions, showing the protected-branch banner, branch sort order) consume this
18
- * single table so the two can never drift. Don't re-declare or hard-code the
19
- * names elsewhere.
20
- *
21
- * Env vars (both REQUIRED — no fallback; the module throws at load if either is
22
- * missing/empty, so a misconfigured deploy fails fast instead of silently
23
- * defaulting to the wrong branches). Note the frontend needs them present at
24
- * BUILD time (Vite `define`), the backend at runtime:
25
- * - `DEFAULT_BRANCH` — the branch a user lands on / the default propose
26
- * target.
27
- * - `PROTECTED_BRANCHES` — comma/space-separated list of protected slugs.
28
- * Display names are auto-derived from each slug
29
- * (`target-company-state` → "Target company state").
14
+ * The exported bindings stay bindings — `DEFAULT_BRANCH` is still imported and
15
+ * read exactly as before at every call site. ES module live bindings mean a
16
+ * reader inside a function body sees whatever configuration has been applied by
17
+ * the time it runs. What does NOT work is capturing one at module scope
18
+ * (`const X = DEFAULT_BRANCH` in a file's top level), which snapshots the value
19
+ * at import — before configuration. Use a function there instead.
30
20
  */
31
21
  /**
32
22
  * The branch a logged-in user lands on when they don't explicitly pick one, and
33
- * the default destination for shared drafts. Env-overridable via `DEFAULT_BRANCH`.
23
+ * the default destination for shared drafts.
24
+ *
25
+ * Empty until {@link configureBranchModel} runs. Read it inside a function, not
26
+ * at module scope — see this file's header.
27
+ */
28
+ export declare let DEFAULT_BRANCH: string;
29
+ export declare let PROTECTED_BRANCH_DISPLAY_NAMES: Readonly<Record<string, string>>;
30
+ export declare let PROTECTED_BRANCHES: ReadonlySet<string>;
31
+ /** The shape both sides configure from, and the shape `/api/config` serves. */
32
+ export interface BranchModel {
33
+ defaultBranch: string;
34
+ /** Slugs; a comma/space-separated string is accepted for env convenience. */
35
+ protectedBranches: string[] | string;
36
+ }
37
+ /**
38
+ * Apply the branch model. Called once during boot on each side, and validated
39
+ * here rather than at either call site so the two cannot disagree about what
40
+ * counts as a valid pair.
41
+ */
42
+ export declare function branchListOf(model: BranchModel): string[];
43
+ /**
44
+ * What is wrong with a pair, or null when nothing is — the same rule
45
+ * {@link configureBranchModel} enforces, without applying anything.
46
+ *
47
+ * Separate because the setup screen has to VALIDATE a proposed pair before it
48
+ * is saved, and applying a model as a side effect of checking it would swap the
49
+ * running app onto branches nobody has confirmed yet.
50
+ */
51
+ export declare function validateBranchModel(model: BranchModel): string | null;
52
+ export declare function configureBranchModel(model: BranchModel): void;
53
+ /** Whether {@link configureBranchModel} has run — the frontend gates render on this. */
54
+ export declare function isBranchModelConfigured(): boolean;
55
+ /**
56
+ * The model as the environment describes it. Node-side only; the browser has no
57
+ * `process.env` to read and is served the same shape by `GET /api/config`.
34
58
  */
35
- export declare const DEFAULT_BRANCH: string;
36
- export declare const PROTECTED_BRANCH_DISPLAY_NAMES: Readonly<Record<string, string>>;
37
- export declare const PROTECTED_BRANCHES: ReadonlySet<string>;
59
+ export declare function branchModelFromEnv(): BranchModel;
38
60
  export declare function isProtectedBranch(name: string | null | undefined): boolean;
39
61
  /**
40
62
  * Capitalised, human-readable name for a protected branch — e.g.
@@ -1 +1 @@
1
- {"version":3,"file":"protected.d.ts","sourceRoot":"","sources":["../../src/git/protected.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA4BH;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAkD,CAAC;AA0BhF,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKzE,CAAC;AAEJ,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAkC,CAAC;AAEtF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAK1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,MAAM,GAAG,IAAI,CAGf"}
1
+ {"version":3,"file":"protected.d.ts","sourceRoot":"","sources":["../../src/git/protected.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsBH;;;;;;GAMG;AACH,eAAO,IAAI,cAAc,EAAE,MAAW,CAAC;AAEvC,eAAO,IAAI,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAqB,CAAC;AAEhG,eAAO,IAAI,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAqB,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,iBAAiB,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,CAIzD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAgBrE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAW7D;AAED,wFAAwF;AACxF,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,CAKhD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAK1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,MAAM,GAAG,IAAI,CAGf"}
@@ -1,32 +1,22 @@
1
1
  /**
2
- * Authoritative, env-overridable registry of the default branch + the set of
3
- * protected branch slugs in the KB repo, plus their user-visible display names.
2
+ * The default branch + the set of protected branch slugs in the KB repo, and
3
+ * their user-visible display names. Single source of truth for BOTH sides of
4
+ * the app, so backend enforcement and frontend affordances can never drift.
4
5
  *
5
- * Single source of truth for BOTH sides of the app:
6
- * - Backend (Node) reads `process.env.*` at runtime — the values are populated
7
- * from the environment / the `.env` loaded by `config.ts`, which runs before
8
- * this module is first evaluated (see `backend/src/index.ts` import order).
9
- * - Frontend (browser) can't read `process.env` at runtime, so Vite statically
10
- * replaces the `process.env.DEFAULT_BRANCH` / `process.env.PROTECTED_BRANCHES`
11
- * references at build time via `define` in `frontend/vite.config.ts`. Keep
12
- * those references written as the literal `process.env.<NAME>` member access
13
- * so the static replacement keeps matching.
6
+ * CONFIGURED, NOT IMPORTED. This module used to read `process.env` at import
7
+ * time and throw when it found nothing — which made the branch model something
8
+ * a deployment had to know before any code could load. On the frontend that
9
+ * meant baking it into the bundle at build time (Vite `define`), so the same
10
+ * artifact could not serve two deployments and changing a branch name meant a
11
+ * rebuild. Both sides now CALL {@link configureBranchModel} during boot:
12
+ * the backend from its environment, the browser from `GET /api/config`.
14
13
  *
15
- * Both the backend (server-side enforcement — refusing commit/push/revert and
16
- * rejecting `createBranch` on protected names) and the frontend (disabling UI
17
- * actions, showing the protected-branch banner, branch sort order) consume this
18
- * single table so the two can never drift. Don't re-declare or hard-code the
19
- * names elsewhere.
20
- *
21
- * Env vars (both REQUIRED — no fallback; the module throws at load if either is
22
- * missing/empty, so a misconfigured deploy fails fast instead of silently
23
- * defaulting to the wrong branches). Note the frontend needs them present at
24
- * BUILD time (Vite `define`), the backend at runtime:
25
- * - `DEFAULT_BRANCH` — the branch a user lands on / the default propose
26
- * target.
27
- * - `PROTECTED_BRANCHES` — comma/space-separated list of protected slugs.
28
- * Display names are auto-derived from each slug
29
- * (`target-company-state` → "Target company state").
14
+ * The exported bindings stay bindings — `DEFAULT_BRANCH` is still imported and
15
+ * read exactly as before at every call site. ES module live bindings mean a
16
+ * reader inside a function body sees whatever configuration has been applied by
17
+ * the time it runs. What does NOT work is capturing one at module scope
18
+ * (`const X = DEFAULT_BRANCH` in a file's top level), which snapshots the value
19
+ * at import — before configuration. Use a function there instead.
30
20
  */
31
21
  function parseBranchList(raw) {
32
22
  return (raw ?? '')
@@ -47,28 +37,73 @@ function deriveDisplayName(slug) {
47
37
  }
48
38
  /**
49
39
  * The branch a logged-in user lands on when they don't explicitly pick one, and
50
- * the default destination for shared drafts. Env-overridable via `DEFAULT_BRANCH`.
40
+ * the default destination for shared drafts.
41
+ *
42
+ * Empty until {@link configureBranchModel} runs. Read it inside a function, not
43
+ * at module scope — see this file's header.
44
+ */
45
+ export let DEFAULT_BRANCH = '';
46
+ export let PROTECTED_BRANCH_DISPLAY_NAMES = Object.freeze({});
47
+ export let PROTECTED_BRANCHES = new Set();
48
+ /**
49
+ * Apply the branch model. Called once during boot on each side, and validated
50
+ * here rather than at either call site so the two cannot disagree about what
51
+ * counts as a valid pair.
51
52
  */
52
- export const DEFAULT_BRANCH = (process.env.DEFAULT_BRANCH ?? '').trim();
53
- if (!DEFAULT_BRANCH) {
54
- throw new Error('DEFAULT_BRANCH env var is required (no fallback). Set it on the backend ' +
55
- 'runtime and at the frontend build (Vite reads it via define).');
53
+ export function branchListOf(model) {
54
+ return Array.isArray(model.protectedBranches)
55
+ ? model.protectedBranches.map((s) => s.trim()).filter(Boolean)
56
+ : parseBranchList(model.protectedBranches);
56
57
  }
57
- const PROTECTED_BRANCH_LIST = parseBranchList(process.env.PROTECTED_BRANCHES);
58
- if (PROTECTED_BRANCH_LIST.length === 0) {
59
- throw new Error('PROTECTED_BRANCHES env var is required (no fallback). Provide a ' +
60
- 'comma/space-separated list of protected branch slugs.');
58
+ /**
59
+ * What is wrong with a pair, or null when nothing is — the same rule
60
+ * {@link configureBranchModel} enforces, without applying anything.
61
+ *
62
+ * Separate because the setup screen has to VALIDATE a proposed pair before it
63
+ * is saved, and applying a model as a side effect of checking it would swap the
64
+ * running app onto branches nobody has confirmed yet.
65
+ */
66
+ export function validateBranchModel(model) {
67
+ const defaultBranch = (model.defaultBranch ?? '').trim();
68
+ const list = branchListOf(model);
69
+ if (!defaultBranch)
70
+ return 'A default branch is required — the branch users land on.';
71
+ if (list.length === 0)
72
+ return 'At least one protected branch is required.';
73
+ // The default branch is where users land and the default propose target — it
74
+ // must itself be protected, or `isProtectedBranch(DEFAULT_BRANCH)` is false
75
+ // and the protected-branch guards silently do not apply to it. Refuse the
76
+ // pair rather than ship that inconsistency.
77
+ if (!list.includes(defaultBranch)) {
78
+ return (`The default branch ("${defaultBranch}") must be one of the protected branches ` +
79
+ `(${list.join(', ')}).`);
80
+ }
81
+ return null;
82
+ }
83
+ export function configureBranchModel(model) {
84
+ const problem = validateBranchModel(model);
85
+ if (problem)
86
+ throw new Error(problem);
87
+ const defaultBranch = model.defaultBranch.trim();
88
+ const list = branchListOf(model);
89
+ DEFAULT_BRANCH = defaultBranch;
90
+ PROTECTED_BRANCH_DISPLAY_NAMES = Object.freeze(Object.fromEntries(list.map((slug) => [slug, deriveDisplayName(slug)])));
91
+ PROTECTED_BRANCHES = new Set(list);
92
+ }
93
+ /** Whether {@link configureBranchModel} has run — the frontend gates render on this. */
94
+ export function isBranchModelConfigured() {
95
+ return DEFAULT_BRANCH !== '';
61
96
  }
62
- // The default branch is where users land and the default propose target — it
63
- // must itself be protected, or `isProtectedBranch(DEFAULT_BRANCH)` is false and
64
- // the protected-branch guards silently don't apply to it. Fail fast on a
65
- // misconfigured pair rather than shipping that inconsistency.
66
- if (!PROTECTED_BRANCH_LIST.includes(DEFAULT_BRANCH)) {
67
- throw new Error(`DEFAULT_BRANCH ("${DEFAULT_BRANCH}") must be one of PROTECTED_BRANCHES ` +
68
- `(${PROTECTED_BRANCH_LIST.join(', ')}).`);
97
+ /**
98
+ * The model as the environment describes it. Node-side only; the browser has no
99
+ * `process.env` to read and is served the same shape by `GET /api/config`.
100
+ */
101
+ export function branchModelFromEnv() {
102
+ return {
103
+ defaultBranch: (process.env.DEFAULT_BRANCH ?? '').trim(),
104
+ protectedBranches: process.env.PROTECTED_BRANCHES ?? '',
105
+ };
69
106
  }
70
- export const PROTECTED_BRANCH_DISPLAY_NAMES = Object.freeze(Object.fromEntries(PROTECTED_BRANCH_LIST.map((slug) => [slug, deriveDisplayName(slug)])));
71
- export const PROTECTED_BRANCHES = new Set(PROTECTED_BRANCH_LIST);
72
107
  export function isProtectedBranch(name) {
73
108
  return !!name && Object.prototype.hasOwnProperty.call(PROTECTED_BRANCH_DISPLAY_NAMES, name);
74
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"protected.js","sourceRoot":"","sources":["../../src/git/protected.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAUH,SAAS,eAAe,CAAC,GAAuB;IAC9C,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;SACf,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAW,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAChF,IAAI,CAAC,cAAc,EAAE,CAAC;IACpB,MAAM,IAAI,KAAK,CACb,0EAA0E;QACxE,+DAA+D,CAClE,CAAC;AACJ,CAAC;AAED,MAAM,qBAAqB,GAAa,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AACxF,IAAI,qBAAqB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACvC,MAAM,IAAI,KAAK,CACb,kEAAkE;QAChE,uDAAuD,CAC1D,CAAC;AACJ,CAAC;AACD,6EAA6E;AAC7E,gFAAgF;AAChF,yEAAyE;AACzE,8DAA8D;AAC9D,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;IACpD,MAAM,IAAI,KAAK,CACb,oBAAoB,cAAc,uCAAuC;QACvE,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC3C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,8BAA8B,GACzC,MAAM,CAAC,MAAM,CACX,MAAM,CAAC,WAAW,CAChB,qBAAqB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CACrE,CACF,CAAC;AAEJ,MAAM,CAAC,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC,qBAAqB,CAAC,CAAC;AAEtF,MAAM,UAAU,iBAAiB,CAAC,IAA+B;IAC/D,OAAO,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CACnD,8BAA8B,EAC9B,IAAI,CACL,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CACxC,IAA+B;IAE/B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,8BAA8B,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"protected.js","sourceRoot":"","sources":["../../src/git/protected.ts"],"names":[],"mappings":"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"}
@@ -71,7 +71,16 @@ export interface IGitService {
71
71
  onlyIfNoRemote?: boolean;
72
72
  }): Promise<void>;
73
73
  commit(workspaceId: string, user: AuthUser, req: ShareChangesRequest): Promise<CommitAttribution>;
74
- push(workspaceId: string, user: AuthUser): Promise<void>;
74
+ push(workspaceId: string, user: AuthUser, opts?: {
75
+ /**
76
+ * Skip the per-user protected-branch access gate for THIS push. Only
77
+ * for system-authorized flows whose endpoint is itself the
78
+ * authorization (group provisioning: any signed-in user may claim an
79
+ * unused name under `Groups/`, and the seeded access.md governs
80
+ * everything after). Never thread a raw user request into this.
81
+ */
82
+ systemAuthorized?: boolean;
83
+ }): Promise<void>;
75
84
  fetch(workspaceId: string): Promise<void>;
76
85
  pull(workspaceId: string): Promise<void>;
77
86
  diffStat(workspaceId: string, base?: string): Promise<string[]>;
@@ -88,7 +97,6 @@ export interface IGitService {
88
97
  * count. Returns null if no protected branch can be reached.
89
98
  */
90
99
  resolveForkBase(workspaceId: string, branch: string): Promise<string | null>;
91
- revertCommit(workspaceId: string, user: AuthUser, sha: string): Promise<CommitAttribution>;
92
100
  logForFile(workspaceId: string, relativePath: string, limit?: number): Promise<CommitAttribution[]>;
93
101
  diffFileAtCommit(workspaceId: string, relativePath: string, sha: string): Promise<string>;
94
102
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/git/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/git/types.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './chat/types.js';
3
3
  export * from './workspace/types.js';
4
4
  export * from './workspace/filename.js';
5
5
  export * from './workspace/kb-layout.js';
6
+ export * from './workspace/join-request.js';
6
7
  export * from './workspace/frontmatter.js';
7
8
  export * from './git/types.js';
8
9
  export * from './git/pr.types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAG3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAG3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from './chat/types.js';
6
6
  export * from './workspace/types.js';
7
7
  export * from './workspace/filename.js';
8
8
  export * from './workspace/kb-layout.js';
9
+ export * from './workspace/join-request.js';
9
10
  export * from './workspace/frontmatter.js';
10
11
  // Git
11
12
  export * from './git/types.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,cAAc,iBAAiB,CAAC;AAEhC,OAAO;AACP,cAAc,iBAAiB,CAAC;AAEhC,YAAY;AACZ,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAE3C,MAAM;AACN,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AAEtC,yEAAyE;AACzE,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,cAAc,iBAAiB,CAAC;AAEhC,OAAO;AACP,cAAc,iBAAiB,CAAC;AAEhC,YAAY;AACZ,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAE3C,MAAM;AACN,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AAEtC,yEAAyE;AACzE,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
@@ -109,12 +109,6 @@ export interface IWorkflowService {
109
109
  commitChanges(workspaceId: string, user: AuthUser, summary: string): Promise<Change | null>;
110
110
  /** Per-file history. Newest first; clamps to ≤ 100 entries. */
111
111
  listChangesForFile(workspaceId: string, path: string, limit?: number): Promise<Change[]>;
112
- /**
113
- * Revert a single change by creating a new change that undoes it.
114
- * Refused on protected branches; refused without write permission on the
115
- * touched paths.
116
- */
117
- revertChange(workspaceId: string, user: AuthUser, sha: string): Promise<Change>;
118
112
  /**
119
113
  * Diff of one file between two branches. Both names must resolve to a
120
114
  * known branch on this workspace (local head or remote-tracking). Returns
@@ -128,6 +122,16 @@ export interface IWorkflowService {
128
122
  * Used by the File viewer's History tab.
129
123
  */
130
124
  showFileAtChange(workspaceId: string, path: string, sha: string): Promise<string>;
125
+ /**
126
+ * Full file contents on both sides of one change: `baseline` at `<sha>^`
127
+ * (null when the file — or the parent commit — doesn't exist there),
128
+ * `current` at `<sha>` (null when absent). Used by the File viewer's
129
+ * History tab to render markdown changes as a rendered-markdown diff.
130
+ */
131
+ fileAtChange(workspaceId: string, path: string, sha: string): Promise<{
132
+ baseline: string | null;
133
+ current: string | null;
134
+ }>;
131
135
  /**
132
136
  * Try to acquire an edit lock on `(branch, path)` for the caller. Returns
133
137
  * `{ acquired: true, lock }` on success; `{ acquired: false, lock }` if
@@ -182,8 +186,10 @@ export interface IWorkflowService {
182
186
  listChangeRequests(opts?: {
183
187
  fresh?: boolean;
184
188
  }): Promise<ChangeRequest[]>;
185
- /** Change requests authored by the caller (hash-matched against the body marker). */
186
- listChangeRequestsAuthoredBy(emailOrLogin: string): Promise<ChangeRequest[]>;
189
+ /** Change requests authored by the given user (matched on stored author identity). */
190
+ listChangeRequestsAuthoredBy(emailOrLogin: string, opts?: {
191
+ fresh?: boolean;
192
+ }): Promise<ChangeRequest[]>;
187
193
  /** Change requests touching files the user has write permission on. */
188
194
  listChangeRequestsForUser(workspaceId: string, email: string, opts?: {
189
195
  fresh?: boolean;
@@ -214,6 +220,29 @@ export interface IWorkflowService {
214
220
  deleteComment(commentId: string, number: number, user: AuthUser): Promise<void>;
215
221
  approveFile(number: number, path: string, user: AuthUser, files: ChangedFile[], headSha: string, baseBranch: string, authorIdHash: string | null, workspaceId: string): Promise<FileApproval[]>;
216
222
  unapproveFile(number: number, path: string, user: AuthUser, files: ChangedFile[], headSha: string, baseBranch: string, authorIdHash: string | null, workspaceId: string): Promise<FileApproval[]>;
223
+ /**
224
+ * Decline ONE file of an open change request: restore its merge-base
225
+ * version on the source branch so it drops out of the request's diff.
226
+ * Same permission as approving the file. When the last file is declined
227
+ * the request closes itself and its source branch is retired
228
+ * (`closed: true` in the result).
229
+ */
230
+ revertChangeRequestFile(number: number, user: AuthUser, repoRelPath: string): Promise<{
231
+ closed: boolean;
232
+ remainingPaths: string[];
233
+ }>;
234
+ /**
235
+ * Close an OPEN change request whose diff has emptied (authoritatively
236
+ * re-verified; a failed diff never closes) and retire its source branch.
237
+ * Returns true when this call closed it.
238
+ */
239
+ closeEmptyChangeRequest(number: number, user: AuthUser): Promise<boolean>;
240
+ /**
241
+ * Delete a change request outright: close it and retire its source branch.
242
+ * Admin-only (write on `roles.yaml` at `origin/<base>`); refuses a merged
243
+ * request.
244
+ */
245
+ deleteChangeRequest(number: number, user: AuthUser): Promise<void>;
217
246
  /**
218
247
  * Reject a change request without merging. Authorized for the author or
219
248
  * for users with write permission to every changed file on the target
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/workflow/interface.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/workflow/interface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The join-request naming convention — how "asking to join a group" rides on
3
+ * plain change requests with zero new backend state.
4
+ *
5
+ * A join request IS a change request: a draft branch carrying one commit that
6
+ * adds the requester to the group's `access.md` body `read:` list, opened
7
+ * against the default branch. What makes it recognisable — to the `Requested`
8
+ * chip, to the manager-side proposals surface, to idempotency — is only this
9
+ * branch-name convention. Both sides (backend, frontend) read it from here so
10
+ * they can never drift.
11
+ *
12
+ * <email-localpart>/join-<group-kebab>-<groupTag>-<requesterTag>
13
+ *
14
+ * follows the workspace's existing `<email-localpart>/<kebab-slug>` draft
15
+ * convention, so join branches sort with the requester's other drafts and
16
+ * the branch-authorship delete rule applies unchanged.
17
+ *
18
+ * WHY TWO TAGS: the localpart and the kebab slug are both LOSSY, and each
19
+ * loss has its own failure:
20
+ *
21
+ * - `groupTag` — over the EXACT group name, recomputable by anyone who
22
+ * knows the group. `Finance!` and `finance` share the slug `finance`;
23
+ * without this tag, listing one group's requests would match the other's
24
+ * branches, read the wrong `access.md` (unchanged on that branch), see an
25
+ * empty diff and SETTLE the request — closing a change request and
26
+ * deleting a branch that belong to a different group. The settle decision
27
+ * is "the diff is empty", so group identity must be exact BEFORE the diff
28
+ * is consulted, and `isJoinBranchFor` recomputes this tag to make it so.
29
+ * - `requesterTag` — over the full email (+ group), NOT recomputable by a
30
+ * reader (a manager listing requests does not know each requester's
31
+ * email; `isJoinBranchFor` matches its shape only). It exists so
32
+ * `ali@bevel.software` and `ali@other.com` never share a branch — else
33
+ * the second requester's commit lands on the first one's branch.
34
+ *
35
+ * The tags are collision-avoidance devices, not a security boundary —
36
+ * nothing grants access based on a branch name. Grants happen through the
37
+ * ordinary access mutation path, and settling only ever closes a request
38
+ * whose file adds nothing over the default branch.
39
+ */
40
+ /** Group name → the kebab slug used in the join branch (lossy by design). */
41
+ export declare function kebabGroupName(group: string): string;
42
+ /** The deterministic join branch for (requester, group). */
43
+ export declare function joinBranchFor(email: string, group: string): string;
44
+ /**
45
+ * Does `branch` look like SOMEBODY's join branch for EXACTLY `group`?
46
+ *
47
+ * The group half (slug + `groupTag`) is recomputed and must match — this is
48
+ * what keeps slug-colliding groups (`Finance!` vs `finance`) from seeing,
49
+ * and worse settling, each other's requests. The requester half cannot be
50
+ * recomputed (the reader has no email) and matches by shape; the requester's
51
+ * identity comes from the change request's own attribution.
52
+ */
53
+ export declare function isJoinBranchFor(branch: string, group: string): boolean;
54
+ //# sourceMappingURL=join-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-request.d.ts","sourceRoot":"","sources":["../../src/workspace/join-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpD;AA4BD,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE"}