@astram/identity 0.16.0 → 0.17.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 (128) hide show
  1. package/dist/branch.object.d.ts +9 -1
  2. package/dist/branch.object.d.ts.map +1 -1
  3. package/dist/branch.object.js +14 -1
  4. package/dist/company-branch.object.d.ts +5 -4
  5. package/dist/company-branch.object.d.ts.map +1 -1
  6. package/dist/company-branch.object.js +6 -4
  7. package/dist/company-department.object.d.ts +6 -6
  8. package/dist/company-department.object.d.ts.map +1 -1
  9. package/dist/company-department.object.js +7 -6
  10. package/dist/department.object.d.ts +9 -1
  11. package/dist/department.object.d.ts.map +1 -1
  12. package/dist/department.object.js +14 -1
  13. package/dist/features/access/access-admin.access.d.ts.map +1 -1
  14. package/dist/features/access/access-admin.access.js +8 -1
  15. package/dist/features/access/index.d.ts +1 -0
  16. package/dist/features/access/index.d.ts.map +1 -1
  17. package/dist/features/access/index.js +10 -2
  18. package/dist/features/access/org-structure.access.d.ts +3 -0
  19. package/dist/features/access/org-structure.access.d.ts.map +1 -0
  20. package/dist/features/access/org-structure.access.js +41 -0
  21. package/dist/features/access/user-management.access.d.ts.map +1 -1
  22. package/dist/features/access/user-management.access.js +5 -0
  23. package/dist/features/audit/audit-read.service.d.ts +32 -1
  24. package/dist/features/audit/audit-read.service.d.ts.map +1 -1
  25. package/dist/features/audit/audit-read.service.js +67 -14
  26. package/dist/features/audit/auth-audit-sink.d.ts +19 -0
  27. package/dist/features/audit/auth-audit-sink.d.ts.map +1 -0
  28. package/dist/features/audit/auth-audit-sink.js +24 -0
  29. package/dist/features/audit/directory-label-resolver.d.ts +10 -1
  30. package/dist/features/audit/directory-label-resolver.d.ts.map +1 -1
  31. package/dist/features/audit/directory-label-resolver.js +28 -1
  32. package/dist/features/authz/grant-admin.controller.d.ts +4 -4
  33. package/dist/features/authz/grant-admin.controller.js +4 -4
  34. package/dist/features/authz/grant-admin.service.d.ts +9 -8
  35. package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
  36. package/dist/features/authz/grant-admin.service.js +20 -24
  37. package/dist/features/authz/grant-ceiling.d.ts +35 -16
  38. package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
  39. package/dist/features/authz/grant-ceiling.js +44 -29
  40. package/dist/features/authz/role-binding.d.ts +25 -24
  41. package/dist/features/authz/role-binding.d.ts.map +1 -1
  42. package/dist/features/authz/role-binding.js +31 -44
  43. package/dist/features/identity-feature.module.d.ts +0 -11
  44. package/dist/features/identity-feature.module.d.ts.map +1 -1
  45. package/dist/features/identity-feature.module.js +90 -1
  46. package/dist/features/membership/membership.controller.d.ts.map +1 -1
  47. package/dist/features/membership/membership.controller.js +20 -6
  48. package/dist/features/membership/membership.service.d.ts +16 -10
  49. package/dist/features/membership/membership.service.d.ts.map +1 -1
  50. package/dist/features/membership/membership.service.js +61 -21
  51. package/dist/features/membership/posting.service.d.ts +25 -6
  52. package/dist/features/membership/posting.service.d.ts.map +1 -1
  53. package/dist/features/membership/posting.service.js +63 -16
  54. package/dist/features/org-bootstrap/org-bootstrap-file.d.ts +6 -2
  55. package/dist/features/org-bootstrap/org-bootstrap-file.d.ts.map +1 -1
  56. package/dist/features/org-bootstrap/org-bootstrap-file.js +54 -11
  57. package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts +30 -11
  58. package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts.map +1 -1
  59. package/dist/features/org-bootstrap/org-bootstrap-plan.js +172 -68
  60. package/dist/features/org-bootstrap/org-state.reader.d.ts.map +1 -1
  61. package/dist/features/org-bootstrap/org-state.reader.js +56 -49
  62. package/dist/features/org-bootstrap/run-org-bootstrap.d.ts +6 -1
  63. package/dist/features/org-bootstrap/run-org-bootstrap.d.ts.map +1 -1
  64. package/dist/features/org-bootstrap/run-org-bootstrap.js +81 -92
  65. package/dist/features/org-structure/org-catalog.controller.d.ts +29 -6
  66. package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -1
  67. package/dist/features/org-structure/org-catalog.controller.js +105 -17
  68. package/dist/features/org-structure/org-catalog.service.d.ts +99 -18
  69. package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -1
  70. package/dist/features/org-structure/org-catalog.service.js +327 -51
  71. package/dist/features/org-structure/org-unit-name.d.ts +31 -0
  72. package/dist/features/org-structure/org-unit-name.d.ts.map +1 -0
  73. package/dist/features/org-structure/org-unit-name.js +50 -0
  74. package/dist/features/role/role.controller.d.ts +28 -13
  75. package/dist/features/role/role.controller.d.ts.map +1 -1
  76. package/dist/features/role/role.controller.js +54 -14
  77. package/dist/features/role/role.service.d.ts +76 -21
  78. package/dist/features/role/role.service.d.ts.map +1 -1
  79. package/dist/features/role/role.service.js +204 -27
  80. package/dist/features/security-policy/security-policy-version-bump.d.ts +41 -0
  81. package/dist/features/security-policy/security-policy-version-bump.d.ts.map +1 -0
  82. package/dist/features/security-policy/security-policy-version-bump.js +65 -0
  83. package/dist/features/security-policy/security-policy.controller.d.ts +51 -0
  84. package/dist/features/security-policy/security-policy.controller.d.ts.map +1 -0
  85. package/dist/features/security-policy/security-policy.controller.js +121 -0
  86. package/dist/features/security-policy/security-policy.service.d.ts +94 -0
  87. package/dist/features/security-policy/security-policy.service.d.ts.map +1 -0
  88. package/dist/features/security-policy/security-policy.service.js +429 -0
  89. package/dist/features/service-account/service-account.service.d.ts +13 -3
  90. package/dist/features/service-account/service-account.service.d.ts.map +1 -1
  91. package/dist/features/service-account/service-account.service.js +48 -10
  92. package/dist/features/service-account/service-account.store.d.ts +11 -1
  93. package/dist/features/service-account/service-account.store.d.ts.map +1 -1
  94. package/dist/features/service-account/service-account.store.js +20 -1
  95. package/dist/features/user/signin-admin.tokens.d.ts +11 -0
  96. package/dist/features/user/signin-admin.tokens.d.ts.map +1 -0
  97. package/dist/features/user/signin-admin.tokens.js +13 -0
  98. package/dist/features/user/user-mfa-status.d.ts +38 -0
  99. package/dist/features/user/user-mfa-status.d.ts.map +1 -0
  100. package/dist/features/user/user-mfa-status.js +47 -0
  101. package/dist/features/user/user-signin-admin.service.d.ts +48 -0
  102. package/dist/features/user/user-signin-admin.service.d.ts.map +1 -0
  103. package/dist/features/user/user-signin-admin.service.js +232 -0
  104. package/dist/features/user/user.controller.d.ts +29 -2
  105. package/dist/features/user/user.controller.d.ts.map +1 -1
  106. package/dist/features/user/user.controller.js +71 -3
  107. package/dist/features/user/user.service.d.ts +40 -14
  108. package/dist/features/user/user.service.d.ts.map +1 -1
  109. package/dist/features/user/user.service.js +143 -10
  110. package/dist/index.d.ts +1 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +3 -1
  113. package/dist/objects.d.ts.map +1 -1
  114. package/dist/objects.js +2 -0
  115. package/dist/role.object.d.ts +1 -1
  116. package/dist/role.object.d.ts.map +1 -1
  117. package/dist/role.object.js +6 -15
  118. package/dist/security-policy.object.d.ts +43 -0
  119. package/dist/security-policy.object.d.ts.map +1 -0
  120. package/dist/security-policy.object.js +107 -0
  121. package/dist/user.object.d.ts +6 -0
  122. package/dist/user.object.d.ts.map +1 -1
  123. package/dist/user.object.js +13 -1
  124. package/migrations/1710000000103-AddUserLoginName.cjs +87 -0
  125. package/migrations/1710000000104-CreateSecurityPolicy.cjs +90 -0
  126. package/migrations/1710000000105-OwnDepartmentsAndBranches.cjs +327 -0
  127. package/migrations/1710000000106-OwnRolesPerCompany.cjs +622 -0
  128. package/package.json +4 -4
@@ -1,8 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PLAN_KIND_ORDER = void 0;
3
+ exports.rolePath = exports.PLAN_KIND_ORDER = exports.orgUnitDriftNote = void 0;
4
+ exports.orgUnitKey = orgUnitKey;
4
5
  exports.planOrgBootstrap = planOrgBootstrap;
5
6
  const org_names_js_1 = require("./org-names.js");
7
+ const org_unit_name_js_1 = require("../org-structure/org-unit-name.js");
8
+ const role_service_js_1 = require("../role/role.service.js");
9
+ /**
10
+ * G-012 (spec §5.A/§5.D, NAES review C): the comparison key of a department/branch name — the
11
+ * shared name rule (`normaliseOrgUnitName`: NFC, trim, collapse whitespace) then `nameKey` (case,
12
+ * accents). "Quality Assurance" in a file therefore matches "Quality Assurance" in the database.
13
+ * A name the rule refuses falls back to `nameKey` alone (the planner reports it as a conflict).
14
+ */
15
+ function orgUnitKey(name) {
16
+ const n = (0, org_unit_name_js_1.normaliseOrgUnitName)(name);
17
+ return (0, org_names_js_1.nameKey)(n.ok ? n.value : name);
18
+ }
19
+ /** The drift note of a stored department/branch the file does not list (spec §5.D). */
20
+ const orgUnitDriftNote = (name) => `in the database, not in the file: ${name} — renamed or added outside the file?`;
21
+ exports.orgUnitDriftNote = orgUnitDriftNote;
6
22
  exports.PLAN_KIND_ORDER = [
7
23
  'company',
8
24
  'department',
@@ -10,7 +26,6 @@ exports.PLAN_KIND_ORDER = [
10
26
  'company_department',
11
27
  'company_branch',
12
28
  'role',
13
- 'role_claim',
14
29
  'grant',
15
30
  'user',
16
31
  'membership',
@@ -18,7 +33,10 @@ exports.PLAN_KIND_ORDER = [
18
33
  'credential',
19
34
  'root_flag',
20
35
  ];
21
- function planOrgBootstrap(file, state, options) {
36
+ /** The plan path of a company's own role (G-013, spec §5.E): `<company path>#role:<name>`. */
37
+ const rolePath = (companyPath, name) => `${companyPath}#role:${name}`;
38
+ exports.rolePath = rolePath;
39
+ function planOrgBootstrap(file, state) {
22
40
  const out = [];
23
41
  const add = (kind, path, status, detail) => {
24
42
  out.push(detail === undefined ? { kind, path, status } : { kind, path, status, detail });
@@ -37,8 +55,6 @@ function planOrgBootstrap(file, state, options) {
37
55
  const inFileTenant = (path) => tenantKeys.has((0, org_names_js_1.nameKey)(path.split('/')[0] ?? ''));
38
56
  // ---- companies, org units ----
39
57
  const stateCompany = new Map(state.companies.map((c) => [(0, org_names_js_1.pathKey)(c.path), c]));
40
- const stateDepts = new Set(state.departments.map(org_names_js_1.nameKey));
41
- const stateBranches = new Set(state.branches.map(org_names_js_1.nameKey));
42
58
  const declaredCompanyKeys = new Set(companies.map((c) => (0, org_names_js_1.pathKey)(c.path)));
43
59
  const ambiguous = new Set(state.ambiguousCompanies.map(org_names_js_1.pathKey));
44
60
  for (const c of companies) {
@@ -53,71 +69,105 @@ function planOrgBootstrap(file, state, options) {
53
69
  add('company', c.path, 'not-in-file');
54
70
  }
55
71
  }
56
- const seenDept = new Set();
57
- const seenBranch = new Set();
58
- for (const c of companies) {
59
- for (const d of c.node.departments ?? []) {
60
- if (seenDept.has((0, org_names_js_1.nameKey)(d)))
61
- continue;
62
- seenDept.add((0, org_names_js_1.nameKey)(d));
63
- add('department', `dept:${d}`, stateDepts.has((0, org_names_js_1.nameKey)(d)) ? 'unchanged' : 'create');
64
- }
65
- for (const b of c.node.branches ?? []) {
66
- if (seenBranch.has((0, org_names_js_1.nameKey)(b)))
67
- continue;
68
- seenBranch.add((0, org_names_js_1.nameKey)(b));
69
- add('branch', `branch:${b}`, stateBranches.has((0, org_names_js_1.nameKey)(b)) ? 'unchanged' : 'create');
72
+ // G-012: departments and branches are owned per company (no shared row), keyed
73
+ // `<company path>#dept:<name>` / `#branch:<name>`. A created row comes with its one junction row
74
+ // (`OrgCatalogService.ensure*`), so the junction entry mirrors the row's status.
75
+ const UNIT_KINDS = [
76
+ {
77
+ kind: 'department',
78
+ junction: 'company_department',
79
+ tag: 'dept',
80
+ declared: (c) => c.departments ?? [],
81
+ stored: (c) => c?.departments ?? [],
82
+ },
83
+ {
84
+ kind: 'branch',
85
+ junction: 'company_branch',
86
+ tag: 'branch',
87
+ declared: (c) => c.branches ?? [],
88
+ stored: (c) => c?.branches ?? [],
89
+ },
90
+ ];
91
+ /** Per company path key: the unit keys the file declares or the database holds. */
92
+ const unitsHere = new Map();
93
+ const unitsOf = (path) => {
94
+ let u = unitsHere.get((0, org_names_js_1.pathKey)(path));
95
+ if (u === undefined) {
96
+ u = { department: new Set(), branch: new Set() };
97
+ unitsHere.set((0, org_names_js_1.pathKey)(path), u);
70
98
  }
71
- }
99
+ return u;
100
+ };
72
101
  for (const c of companies) {
73
102
  const sc = stateCompany.get((0, org_names_js_1.pathKey)(c.path));
74
- const haveD = new Set((sc?.departments ?? []).map(org_names_js_1.nameKey));
75
- const haveB = new Set((sc?.branches ?? []).map(org_names_js_1.nameKey));
76
- for (const d of c.node.departments ?? []) {
77
- add('company_department', `${c.path}#dept:${d}`, haveD.has((0, org_names_js_1.nameKey)(d)) ? 'unchanged' : 'create');
103
+ for (const k of UNIT_KINDS) {
104
+ const have = new Set(k.stored(sc).map(orgUnitKey));
105
+ for (const key of have)
106
+ unitsOf(c.path)[k.kind].add(key);
107
+ const seen = new Set();
108
+ for (const name of k.declared(c.node)) {
109
+ const path = `${c.path}#${k.tag}:${name}`;
110
+ const n = (0, org_unit_name_js_1.normaliseOrgUnitName)(name);
111
+ if (!n.ok) {
112
+ add(k.kind, path, 'conflict', `${k.kind} ${JSON.stringify(name)}: ${(0, org_unit_name_js_1.orgUnitNameMessage)(k.kind, n.reason)}`);
113
+ continue;
114
+ }
115
+ const key = (0, org_names_js_1.nameKey)(n.value);
116
+ if (seen.has(key))
117
+ continue;
118
+ seen.add(key);
119
+ unitsOf(c.path)[k.kind].add(key);
120
+ const status = have.has(key) ? 'unchanged' : 'create';
121
+ add(k.kind, path, status);
122
+ add(k.junction, path, status);
123
+ }
78
124
  }
79
- for (const b of c.node.branches ?? []) {
80
- add('company_branch', `${c.path}#branch:${b}`, haveB.has((0, org_names_js_1.nameKey)(b)) ? 'unchanged' : 'create');
125
+ }
126
+ // Drift note (spec §5.D, NAES review #2): per company of the file's tenants, the stored units the
127
+ // file does not list. Informational only: the bootstrap is additive and never renames or deletes.
128
+ const declaredCompany = new Map(companies.map((c) => [(0, org_names_js_1.pathKey)(c.path), c.node]));
129
+ for (const sc of state.companies) {
130
+ if (!inFileTenant(sc.path))
131
+ continue;
132
+ const node = declaredCompany.get((0, org_names_js_1.pathKey)(sc.path));
133
+ for (const k of UNIT_KINDS) {
134
+ const declared = new Set((node ? k.declared(node) : []).map(orgUnitKey));
135
+ for (const name of k.stored(sc)) {
136
+ if (declared.has(orgUnitKey(name)))
137
+ continue;
138
+ add(k.kind, `${sc.path}#${k.tag}:${name}`, 'not-in-file', (0, exports.orgUnitDriftNote)(name));
139
+ }
81
140
  }
82
141
  }
83
142
  // ---- roles and grants ----
84
- const stateRole = new Map(state.roles.map((r) => [(0, org_names_js_1.nameKey)(r.name), r]));
85
- const declaredRoles = new Set();
143
+ // G-013 (spec §5.E): every role is OWNED by the company that declares it, keyed
144
+ // `<company path>#role:<name>` (name rule + `nameKey`, as the per-company unique index compares).
145
+ // A name another company uses is simply that company's own role: no cross-company conflict.
146
+ const stateRoles = new Map();
147
+ for (const r of state.roles)
148
+ stateRoles.set(`${(0, org_names_js_1.pathKey)(r.companyPath)}\u0000${orgUnitKey(r.name)}`, r);
149
+ const roleOf = (companyPath, name) => stateRoles.get(`${(0, org_names_js_1.pathKey)(companyPath)}\u0000${orgUnitKey(name)}`);
86
150
  for (const c of companies) {
151
+ const seen = new Set();
87
152
  for (const r of c.node.roles ?? []) {
88
- const key = (0, org_names_js_1.nameKey)(r.name);
89
- declaredRoles.add(key);
90
- const path = `role:${r.name}`;
91
- const existing = stateRole.get(key);
153
+ const path = (0, exports.rolePath)(c.path, r.name);
154
+ const n = (0, org_unit_name_js_1.normaliseOrgUnitName)(r.name);
155
+ if (!n.ok) {
156
+ add('role', path, 'conflict', `role ${JSON.stringify(r.name)}: ${(0, role_service_js_1.roleNameMessage)(n.reason)}`);
157
+ continue;
158
+ }
159
+ const key = (0, org_names_js_1.nameKey)(n.value);
160
+ if (seen.has(key))
161
+ continue; // the parser refuses a duplicate; never planned twice
162
+ seen.add(key);
163
+ const existing = roleOf(c.path, r.name);
92
164
  if (!existing) {
93
165
  add('role', path, 'create');
94
166
  for (const k of r.permissions)
95
167
  add('grant', `${path}#${k}`, 'create');
96
168
  continue;
97
169
  }
98
- if (existing.ownerTenant !== null && !tenantKeys.has((0, org_names_js_1.nameKey)(existing.ownerTenant))) {
99
- add('role', path, 'conflict', `role "${r.name}" is owned by another tenant`);
100
- continue;
101
- }
102
- if (existing.ownerTenant !== null && (0, org_names_js_1.nameKey)(existing.ownerTenant) !== (0, org_names_js_1.nameKey)(c.tenant)) {
103
- add('role', path, 'conflict', `role "${r.name}" is owned by tenant "${existing.ownerTenant}", not by "${c.tenant}" which declares it`);
104
- continue;
105
- }
106
- if (existing.ownerTenant === null) {
107
- if (existing.heldOutside) {
108
- add('role', path, 'conflict', `role "${r.name}" has no owner and is held outside the file's tenants`);
109
- continue;
110
- }
111
- if (!options.claimUnownedRoles) {
112
- add('role', path, 'conflict', `role "${r.name}" has no owner; pass --claim-unowned-roles to claim it`);
113
- continue;
114
- }
115
- add('role', path, 'unchanged');
116
- add('role_claim', path, 'create', `claim "${r.name}" for tenant ${c.tenant}`);
117
- }
118
- else {
119
- add('role', path, 'unchanged');
120
- }
170
+ add('role', path, 'unchanged');
121
171
  const have = new Set(existing.permissions);
122
172
  for (const k of r.permissions)
123
173
  add('grant', `${path}#${k}`, have.has(k) ? 'unchanged' : 'create');
@@ -127,20 +177,50 @@ function planOrgBootstrap(file, state, options) {
127
177
  add('grant', `${path}#${k}`, 'not-in-file');
128
178
  }
129
179
  }
180
+ // Drift note (as G-012's units): per company of the file's tenants, the stored roles the file
181
+ // does not declare on it. Informational only: the bootstrap is additive and never renames or
182
+ // deletes a role.
130
183
  for (const r of state.roles) {
131
- if (r.ownerTenant !== null &&
132
- tenantKeys.has((0, org_names_js_1.nameKey)(r.ownerTenant)) &&
133
- !declaredRoles.has((0, org_names_js_1.nameKey)(r.name))) {
134
- add('role', `role:${r.name}`, 'not-in-file');
135
- }
184
+ if (!inFileTenant(r.companyPath))
185
+ continue;
186
+ const node = declaredCompany.get((0, org_names_js_1.pathKey)(r.companyPath));
187
+ const declared = new Set((node?.roles ?? []).map((x) => orgUnitKey(x.name)));
188
+ if (declared.has(orgUnitKey(r.name)))
189
+ continue;
190
+ add('role', (0, exports.rolePath)(r.companyPath, r.name), 'not-in-file', (0, exports.orgUnitDriftNote)(r.name));
136
191
  }
137
192
  // ---- users, memberships, postings, credentials ----
138
193
  const stateUser = new Map(state.users.map((u) => [(0, org_names_js_1.nameKey)(u.username), u]));
139
194
  const emailHolder = new Map(state.emailHolders.map((h) => [(0, org_names_js_1.nameKey)(h.email), h.username]));
195
+ const loginNameHolder = new Map((state.loginNameHolders ?? []).map((h) => [h.loginName.toLowerCase(), h.username]));
140
196
  const declaredUsers = new Set();
141
- const postingKey = (p) => [(0, org_names_js_1.nameKey)(p.role), (0, org_names_js_1.nameKey)(p.department ?? ''), (0, org_names_js_1.nameKey)(p.branch ?? '')].join('|');
197
+ const postingKey = (p) => [orgUnitKey(p.role), orgUnitKey(p.department ?? ''), orgUnitKey(p.branch ?? '')].join('|');
142
198
  const postingPath = (user, company, p) => `user:${user}@${company} role:${p.role}${p.department ? ` dept:${p.department}` : ''}${p.branch ? ` branch:${p.branch}` : ''}`;
143
- const planUser = (username, email, company, postings, isRoot) => {
199
+ /**
200
+ * G-012: a posting resolves its department/branch within its OWN company (the posting check
201
+ * reads that company's junction rows). One declared only on an ancestor in the file, and not
202
+ * held by the company in the database, could never be posted: refuse it at plan time.
203
+ */
204
+ const postingUnitProblem = (company, p) => {
205
+ const units = unitsOf(company);
206
+ for (const [kind, name] of [
207
+ ['department', p.department],
208
+ ['branch', p.branch],
209
+ ]) {
210
+ if (name !== undefined && !units[kind].has(orgUnitKey(name))) {
211
+ return `${kind} "${name}" is not one of company "${company}"'s own ${kind === 'department' ? 'departments' : 'branches'}; declare it on that company`;
212
+ }
213
+ }
214
+ return undefined;
215
+ };
216
+ const addPostingCreate = (path, company, p) => {
217
+ const problem = postingUnitProblem(company, p);
218
+ if (problem === undefined)
219
+ add('posting', path, 'create');
220
+ else
221
+ add('posting', path, 'conflict', problem);
222
+ };
223
+ const planUser = (username, email, company, postings, isRoot, loginName) => {
144
224
  declaredUsers.add((0, org_names_js_1.nameKey)(username));
145
225
  const path = `user:${username}`;
146
226
  const existing = stateUser.get((0, org_names_js_1.nameKey)(username));
@@ -150,11 +230,17 @@ function planOrgBootstrap(file, state, options) {
150
230
  add('user', path, 'conflict', `email of "${username}" is already held by another user`);
151
231
  return;
152
232
  }
233
+ // Auth capabilities R1.10: login names are global; a new user cannot take a held one.
234
+ const nameHolder = loginName === undefined ? undefined : loginNameHolder.get(loginName);
235
+ if (nameHolder !== undefined && (0, org_names_js_1.nameKey)(nameHolder) !== (0, org_names_js_1.nameKey)(username)) {
236
+ add('user', path, 'conflict', `login name of "${username}" is already held by another user`);
237
+ return;
238
+ }
153
239
  add('user', path, 'create');
154
240
  if (isRoot)
155
241
  add('credential', path, 'create', 'password credential from the operator-supplied root password');
156
242
  for (const p of postings)
157
- add('posting', postingPath(username, company, p), 'create');
243
+ addPostingCreate(postingPath(username, company, p), company, p);
158
244
  if (isRoot)
159
245
  add('root_flag', path, 'create');
160
246
  return;
@@ -186,7 +272,21 @@ function planOrgBootstrap(file, state, options) {
186
272
  add('user', path, 'drift', 'user exists with no membership; add it from the admin console, the bootstrap plans nothing for it');
187
273
  return;
188
274
  }
189
- add('user', path, emailDiffers ? 'drift' : 'unchanged', emailDiffers ? 'email differs from the file; not overwritten' : undefined);
275
+ // Auth capabilities R1.7: an existing user's login name is never set or changed here (as its
276
+ // email): a stored one that differs from the file's, or none stored, is reported only.
277
+ const storedLoginName = typeof existing.loginName === 'string' && existing.loginName !== ''
278
+ ? existing.loginName.toLowerCase()
279
+ : null;
280
+ const loginNameDiffers = loginName !== undefined && storedLoginName !== loginName;
281
+ const drifts = [
282
+ ...(emailDiffers ? ['email differs from the file; not overwritten'] : []),
283
+ ...(loginNameDiffers
284
+ ? [
285
+ 'login name differs from the file; not set or overwritten (set it from the admin console)',
286
+ ]
287
+ : []),
288
+ ];
289
+ add('user', path, drifts.length > 0 ? 'drift' : 'unchanged', drifts.length > 0 ? drifts.join('; ') : undefined);
190
290
  // A root holds no membership: root is barred from the data plane, and its home company only
191
291
  // anchors the credential (`ic_tenant_id`). Planning one would be denied and never converge.
192
292
  const declaredKey = (0, org_names_js_1.pathKey)(company);
@@ -198,7 +298,7 @@ function planOrgBootstrap(file, state, options) {
198
298
  else if (!membership) {
199
299
  add('membership', mpath, 'create');
200
300
  for (const p of postings)
201
- add('posting', postingPath(username, company, p), 'create');
301
+ addPostingCreate(postingPath(username, company, p), company, p);
202
302
  }
203
303
  else if (!membership.active) {
204
304
  add('membership', mpath, 'drift', 'inactive membership; the bootstrap never activates it');
@@ -211,7 +311,11 @@ function planOrgBootstrap(file, state, options) {
211
311
  add('membership', mpath, 'unchanged');
212
312
  const have = new Set(membership.postings.map(postingKey));
213
313
  for (const p of postings) {
214
- add('posting', postingPath(username, company, p), have.has(postingKey(p)) ? 'unchanged' : 'create');
314
+ const path = postingPath(username, company, p);
315
+ if (have.has(postingKey(p)))
316
+ add('posting', path, 'unchanged');
317
+ else
318
+ addPostingCreate(path, company, p);
215
319
  }
216
320
  }
217
321
  if (isRoot) {
@@ -258,11 +362,11 @@ function planOrgBootstrap(file, state, options) {
258
362
  };
259
363
  const tenantPath = (tenantName, rel) => rel === undefined ? tenantName : `${tenantName}/${rel}`;
260
364
  for (const r of file.roots)
261
- planUser(r.username, r.email, r.homeCompany, [], true);
365
+ planUser(r.username, r.email, r.homeCompany, [], true, r.loginName);
262
366
  for (const c of companies) {
263
367
  for (const u of c.node.users ?? []) {
264
368
  const company = u.company === undefined ? c.path : tenantPath(c.tenant, u.company);
265
- planUser(u.username, u.email, company, u.postings ?? [], false);
369
+ planUser(u.username, u.email, company, u.postings ?? [], false, u.loginName);
266
370
  }
267
371
  }
268
372
  for (const u of state.users) {
@@ -1 +1 @@
1
- {"version":3,"file":"org-state.reader.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/org-state.reader.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAuB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAErF,OAAO,KAAK,EAEV,QAAQ,EAMT,MAAM,yBAAyB,CAAC;AAEjC,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3D,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAC;CAC/D;AA6CD,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAElD,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;CA6NtD"}
1
+ {"version":3,"file":"org-state.reader.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/org-state.reader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAuB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAErF,OAAO,KAAK,EAEV,QAAQ,EAMT,MAAM,yBAAyB,CAAC;AAEjC,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3D,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAC;CAC/D;AAuCD,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAElD,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;CA2OtD"}
@@ -6,8 +6,9 @@ exports.OrgStateReader = void 0;
6
6
  *
7
7
  * Issues SELECTs only, every name bound as a parameter, database names taken from the injected
8
8
  * `schemas` (never a literal). Scope: the file's tenants' subtrees (top-level companies matched by name,
9
- * then `scope_closure`), plus the global lookups the planner's conflicts need (roles by name and the
10
- * tenant that owns them, usernames, emails, credential state). The row-to-state mapping is proven live.
9
+ * then `scope_closure`), plus the global lookups the planner's conflicts need (usernames, emails,
10
+ * credential state). Roles, like departments and branches, are read per owner company inside the
11
+ * subtrees (G-013: names are unique per company only). The row-to-state mapping is proven live.
11
12
  * A query failure propagates: a partial state is never returned.
12
13
  */
13
14
  const guard_1 = require("@astram/guard");
@@ -15,26 +16,19 @@ const org_names_js_1 = require("./org-names.js");
15
16
  /** `?, ?, ?`; an empty list reads as `NULL` so the clause stays valid SQL and matches nothing. */
16
17
  const marks = (n) => n === 0 ? 'NULL' : Array.from({ length: n }, () => '?').join(', ');
17
18
  const str = (v) => (v === null || v === undefined ? '' : String(v));
18
- const strOrNull = (v) => (v === null || v === undefined ? null : String(v));
19
19
  function collect(file) {
20
20
  const names = {
21
21
  tenants: [],
22
- roles: new Set(),
23
- departments: new Set(),
24
- branches: new Set(),
25
22
  usernames: new Set(),
26
23
  emails: new Set(),
24
+ loginNames: new Set(),
27
25
  };
28
26
  const walk = (c) => {
29
- for (const d of c.departments ?? [])
30
- names.departments.add(d);
31
- for (const b of c.branches ?? [])
32
- names.branches.add(b);
33
- for (const r of c.roles ?? [])
34
- names.roles.add(r.name);
35
27
  for (const u of c.users ?? []) {
36
28
  names.usernames.add(u.username);
37
29
  names.emails.add(u.email);
30
+ if (u.loginName !== undefined)
31
+ names.loginNames.add(u.loginName);
38
32
  }
39
33
  for (const ch of c.children ?? [])
40
34
  walk(ch);
@@ -46,14 +40,14 @@ function collect(file) {
46
40
  for (const r of file.roots) {
47
41
  names.usernames.add(r.username);
48
42
  names.emails.add(r.email);
43
+ if (r.loginName !== undefined)
44
+ names.loginNames.add(r.loginName);
49
45
  }
50
46
  return {
51
47
  tenants: names.tenants,
52
- roles: [...names.roles],
53
- departments: [...names.departments],
54
- branches: [...names.branches],
55
48
  usernames: [...names.usernames],
56
49
  emails: [...names.emails],
50
+ loginNames: [...names.loginNames],
57
51
  };
58
52
  }
59
53
  class OrgStateReader {
@@ -100,30 +94,22 @@ class OrgStateReader {
100
94
  };
101
95
  markAmbiguous(tenantRows, (r) => (0, org_names_js_1.nameKey)(str(r.c_name)), (r) => str(r.c_name));
102
96
  markAmbiguous(subtreeRows.filter((r) => r.c_parent_uuid !== null && r.c_parent_uuid !== undefined), (r) => `${str(r.c_parent_uuid)}\u0000${(0, org_names_js_1.nameKey)(str(r.c_name))}`, (r) => pathOf(str(r.c_uuid)));
103
- // 3. catalogs and the company junctions
104
- const deptCatalog = await query(`SELECT d_name FROM ${app}.departments WHERE d_name IN (${marks(names.departments.length)})`, names.departments);
105
- const branchCatalog = await query(`SELECT b_name FROM ${app}.branches WHERE b_name IN (${marks(names.branches.length)})`, names.branches);
106
- const deptJunction = await query(`SELECT cd.cd_company_uuid, d.d_name FROM ${app}.company_departments cd ` +
107
- `JOIN ${app}.departments d ON d.d_uuid = cd.cd_department_uuid WHERE cd.cd_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
108
- const branchJunction = await query(`SELECT cb.cb_company_uuid, b.b_name FROM ${app}.company_branches cb ` +
109
- `JOIN ${app}.branches b ON b.b_uuid = cb.cb_branch_uuid WHERE cb.cb_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
97
+ // 3. each company's OWN departments and branches (G-012: the owner column, migration 105;
98
+ // no row is shared between companies, so there is no global catalog to read)
99
+ const deptRows = await query(`SELECT d_company_uuid, d_name FROM ${app}.departments WHERE d_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
100
+ const branchRows = await query(`SELECT b_company_uuid, b_name FROM ${app}.branches WHERE b_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
110
101
  const companies = new Map();
111
102
  for (const uuid of subtreeUuids) {
112
103
  companies.set(uuid, { path: pathOf(uuid), departments: [], branches: [] });
113
104
  }
114
- for (const r of deptJunction)
115
- companies.get(str(r.cd_company_uuid))?.departments.push(str(r.d_name));
116
- for (const r of branchJunction)
117
- companies.get(str(r.cb_company_uuid))?.branches.push(str(r.b_name));
118
- // 4. roles by name or owned in the subtrees; owner's top-level tenant; held outside the subtrees
119
- const roleRows = await query(`SELECT r.r_uuid, r.r_name, r.r_owner_company_uuid, t.c_name AS owner_tenant, ` +
120
- `EXISTS (SELECT 1 FROM ${app}.membership_posting mp JOIN ${app}.company_membership cm ON cm.cm_uuid = mp.mp_membership_uuid ` +
121
- `WHERE mp.mp_role_uuid = r.r_uuid AND cm.cm_company_uuid NOT IN (` +
122
- `SELECT sc2.descendant_uuid FROM ${authz}.scope_closure sc2 WHERE sc2.ancestor_uuid IN (${marks(tenantUuids.length)}))) AS held_outside ` +
123
- `FROM ${app}.roles r ` +
124
- `LEFT JOIN ${authz}.scope_closure oc ON oc.descendant_uuid = r.r_owner_company_uuid ` +
125
- `LEFT JOIN ${app}.companies t ON t.c_uuid = oc.ancestor_uuid AND t.c_parent_uuid IS NULL ` +
126
- `WHERE r.r_name IN (${marks(names.roles.length)}) OR r.r_owner_company_uuid IN (${marks(subtreeUuids.length)})`, [...tenantUuids, ...names.roles, ...subtreeUuids]);
105
+ for (const r of deptRows)
106
+ companies.get(str(r.d_company_uuid))?.departments.push(str(r.d_name));
107
+ for (const r of branchRows)
108
+ companies.get(str(r.b_company_uuid))?.branches.push(str(r.b_name));
109
+ // 4. each company's OWN roles (G-013: the owner column; a name is unique per company only, so
110
+ // roles are read by (owner, name) and never merged by name across companies)
111
+ const roleRows = await query(`SELECT r_uuid, r_name, r_owner_company_uuid FROM ${app}.roles ` +
112
+ `WHERE r_owner_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
127
113
  const roleUuids = roleRows.map((r) => str(r.r_uuid));
128
114
  const grantRows = await query(`SELECT rp_role_uuid, rp_perm_key FROM ${authz}.role_permission WHERE rp_role_uuid IN (${marks(roleUuids.length)})`, roleUuids);
129
115
  const grants = new Map();
@@ -132,19 +118,13 @@ class OrgStateReader {
132
118
  list.push(str(g.rp_perm_key));
133
119
  grants.set(str(g.rp_role_uuid), list);
134
120
  }
135
- const roles = [];
136
- for (const r of roleRows) {
137
- if (roles.some((x) => x.name === str(r.r_name)))
138
- continue; // duplicate rows from closure joins
139
- roles.push({
140
- name: str(r.r_name),
141
- ownerTenant: strOrNull(r.owner_tenant),
142
- heldOutside: Number(r.held_outside) === 1,
143
- permissions: grants.get(str(r.r_uuid)) ?? [],
144
- });
145
- }
121
+ const roles = roleRows.map((r) => ({
122
+ companyPath: pathOf(str(r.r_owner_company_uuid)),
123
+ name: str(r.r_name),
124
+ permissions: grants.get(str(r.r_uuid)) ?? [],
125
+ }));
146
126
  // 5. users (by username, or a member of the subtrees) with their credential
147
- const userRows = await query(`SELECT u.u_uuid, u.u_username, u.u_email, u.u_is_platform_superuser, ic.ic_password_hash ` +
127
+ const userRows = await query(`SELECT u.u_uuid, u.u_username, u.u_email, u.u_login_name, u.u_is_platform_superuser, ic.ic_password_hash ` +
148
128
  `FROM ${app}.users u LEFT JOIN ${identity}.identity_credential ic ON ic.ic_principal_id = u.u_uuid ` +
149
129
  `WHERE u.u_username IN (${marks(names.usernames.length)}) ` +
150
130
  `OR u.u_uuid IN (SELECT cm_user_uuid FROM ${app}.company_membership WHERE cm_company_uuid IN (${marks(subtreeUuids.length)}))`, [...names.usernames, ...subtreeUuids]);
@@ -156,6 +136,18 @@ class OrgStateReader {
156
136
  `SELECT sc3.descendant_uuid FROM ${authz}.scope_closure sc3 WHERE sc3.ancestor_uuid IN (${marks(tenantUuids.length)}))`, [...userUuids, ...tenantUuids]);
157
137
  const outsideUsers = new Set(outsideRows.map((r) => str(r.cm_user_uuid)));
158
138
  const emailRows = await query(`SELECT u_email, u_username FROM ${app}.users WHERE u_email IN (${marks(names.emails.length)})`, names.emails);
139
+ // Auth capabilities R1.10: who holds the file's login names (read only when it declares any).
140
+ const loginNameRows = names.loginNames.length === 0
141
+ ? null
142
+ : await query(`SELECT u_login_name, u_username FROM ${app}.users WHERE u_login_name IN (${marks(names.loginNames.length)})`, names.loginNames);
143
+ // Review round 1, item F: a legacy (non-email) sign-in hint equal to a file login name holds it
144
+ // too (the sign-in lookup would match two accounts). Compared under the column's collation; an
145
+ // orphan credential (no user row) is a holder with no username.
146
+ const hintRows = names.loginNames.length === 0
147
+ ? []
148
+ : await query(`SELECT ic.ic_identity_hint AS hint, u.u_username FROM ${identity}.identity_credential ic ` +
149
+ `LEFT JOIN ${app}.users u ON u.u_uuid = ic.ic_principal_id ` +
150
+ `WHERE ic.ic_identity_hint IN (${marks(names.loginNames.length)})`, names.loginNames);
159
151
  // 6. memberships and postings inside the subtrees
160
152
  const memberRows = await query(`SELECT cm_uuid, cm_user_uuid, cm_company_uuid, cm_is_active FROM ${app}.company_membership ` +
161
153
  `WHERE cm_company_uuid IN (${marks(subtreeUuids.length)})`, subtreeUuids);
@@ -205,6 +197,9 @@ class OrgStateReader {
205
197
  users.set(id, {
206
198
  username: str(u.u_username),
207
199
  email: str(u.u_email),
200
+ ...(u.u_login_name !== null && u.u_login_name !== undefined
201
+ ? { loginName: str(u.u_login_name) }
202
+ : {}),
208
203
  credential,
209
204
  isPlatformSuperuser: Number(u.u_is_platform_superuser) === 1,
210
205
  memberships: memberships.get(id) ?? [],
@@ -213,11 +208,23 @@ class OrgStateReader {
213
208
  }
214
209
  return {
215
210
  companies: [...companies.values()],
216
- departments: deptCatalog.map((r) => str(r.d_name)),
217
- branches: branchCatalog.map((r) => str(r.b_name)),
218
211
  roles,
219
212
  users: [...users.values()],
220
213
  emailHolders: emailRows.map((r) => ({ email: str(r.u_email), username: str(r.u_username) })),
214
+ ...(loginNameRows === null
215
+ ? {}
216
+ : {
217
+ loginNameHolders: [
218
+ ...loginNameRows.map((r) => ({
219
+ loginName: str(r.u_login_name),
220
+ username: str(r.u_username),
221
+ })),
222
+ ...hintRows.map((r) => ({
223
+ loginName: str(r.hint),
224
+ username: str(r.u_username),
225
+ })),
226
+ ],
227
+ }),
221
228
  ambiguousCompanies,
222
229
  };
223
230
  }
@@ -11,7 +11,10 @@ export interface RunOrgBootstrapOptions {
11
11
  /** The human operator (audit `onBehalfOf`); else `ASTRAM_BOOTSTRAP_OPERATOR`. Required with `apply`. */
12
12
  operator?: string;
13
13
  env?: NodeJS.ProcessEnv;
14
- /** Claim roles that have no owner and that nobody outside the file's tenants holds. */
14
+ /**
15
+ * No longer needed (G-013): roles belong to the company that declares them, so there is nothing
16
+ * to claim. Accepted for compatibility; it changes nothing and prints {@link CLAIM_UNOWNED_NOTICE}.
17
+ */
15
18
  claimUnownedRoles?: boolean;
16
19
  /** Issue a fresh set-password link to users whose credential is still locked. */
17
20
  reissueLinks?: boolean;
@@ -57,5 +60,7 @@ export interface OrgBootstrapReport {
57
60
  /** Uuid of every root in the file that exists after the run. */
58
61
  rootIds: string[];
59
62
  }
63
+ /** Printed when `claimUnownedRoles` is passed (G-013, spec §5.E): the flag now does nothing. */
64
+ export declare const CLAIM_UNOWNED_NOTICE = "--claim-unowned-roles is no longer needed: roles belong to the company that declares them.";
60
65
  export declare function runOrgBootstrap(app: INestApplicationContext, opts: RunOrgBootstrapOptions): Promise<OrgBootstrapReport>;
61
66
  //# sourceMappingURL=run-org-bootstrap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-org-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/run-org-bootstrap.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,uBAAuB,EAAQ,MAAM,gBAAgB,CAAC;AA+BpE,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAEL,KAAK,OAAO,EAIb,MAAM,yBAAyB,CAAC;AAiBjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,kDAAkD;IAClD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAwFD,wBAAsB,eAAe,CACnC,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqtB7B"}
1
+ {"version":3,"file":"run-org-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/run-org-bootstrap.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,uBAAuB,EAAQ,MAAM,gBAAgB,CAAC;AA8BpE,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAIL,KAAK,OAAO,EAIb,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,kDAAkD;IAClD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAwCD,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,+FAC6D,CAAC;AA8D/F,wBAAsB,eAAe,CACnC,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAmqB7B"}