@endora-commerce/mod-admin-roles 0.100.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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/admin/index.d.ts +47 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +27 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +20 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +13 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +64 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +74 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +20 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +29 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/entities/admin-role.entity.d.ts +20 -0
- package/dist/backend/entities/admin-role.entity.d.ts.map +1 -0
- package/dist/backend/entities/admin-role.entity.js +65 -0
- package/dist/backend/entities/admin-role.entity.js.map +1 -0
- package/dist/backend/index.d.ts +96 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +114 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/permission-inventory.d.ts +117 -0
- package/dist/backend/permission-inventory.d.ts.map +1 -0
- package/dist/backend/permission-inventory.js +585 -0
- package/dist/backend/permission-inventory.js.map +1 -0
- package/dist/backend/services/admin-role-ports.d.ts +39 -0
- package/dist/backend/services/admin-role-ports.d.ts.map +1 -0
- package/dist/backend/services/admin-role-ports.js +72 -0
- package/dist/backend/services/admin-role-ports.js.map +1 -0
- package/dist/backend/services/admin-role-service.d.ts +77 -0
- package/dist/backend/services/admin-role-service.d.ts.map +1 -0
- package/dist/backend/services/admin-role-service.js +219 -0
- package/dist/backend/services/admin-role-service.js.map +1 -0
- package/dist/backend/services/permission-catalogue.service.d.ts +130 -0
- package/dist/backend/services/permission-catalogue.service.d.ts.map +1 -0
- package/dist/backend/services/permission-catalogue.service.js +175 -0
- package/dist/backend/services/permission-catalogue.service.js.map +1 -0
- package/dist/backend/services/permission-service.d.ts +24 -0
- package/dist/backend/services/permission-service.d.ts.map +1 -0
- package/dist/backend/services/permission-service.js +40 -0
- package/dist/backend/services/permission-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +192 -0
- package/dist/manifest.js.map +1 -0
- package/docs/admin_roles.md +38 -0
- package/i18n/en.json +10 -0
- package/i18n/pl.json +10 -0
- package/package.json +73 -0
- package/tailwind.css +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
|
|
2
|
+
|
|
3
|
+
# @endora-commerce/mod-admin-roles
|
|
4
|
+
|
|
5
|
+
Admin RBAC — roles, permissions, and policy enforcement for admin sessions.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
An **Endora Commerce module package**. Its module id is `admin_roles` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
|
|
10
|
+
|
|
11
|
+
A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
|
|
12
|
+
|
|
13
|
+
## Entry points
|
|
14
|
+
|
|
15
|
+
| Import | Contents |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@endora-commerce/mod-admin-roles` | the module manifest — its id, version, dependencies, settings and activation |
|
|
18
|
+
| `@endora-commerce/mod-admin-roles/admin` | the Admin UI contribution: the screens, navigation and palette actions it adds |
|
|
19
|
+
| `@endora-commerce/mod-admin-roles/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
|
|
20
|
+
| `@endora-commerce/mod-admin-roles/tailwind.css` | the Tailwind source declaration for the admin code above |
|
|
21
|
+
|
|
22
|
+
## Depends on
|
|
23
|
+
|
|
24
|
+
Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
|
|
25
|
+
|
|
26
|
+
**Endora packages**
|
|
27
|
+
|
|
28
|
+
- `@endora-commerce/admin-kit` — *optional*
|
|
29
|
+
- `@endora-commerce/contracts`
|
|
30
|
+
- `@endora-commerce/platform`
|
|
31
|
+
|
|
32
|
+
**Third-party**
|
|
33
|
+
|
|
34
|
+
- `@mikro-orm/core` ^6
|
|
35
|
+
- `@mikro-orm/postgresql` ^6
|
|
36
|
+
|
|
37
|
+
## What the tarball carries
|
|
38
|
+
|
|
39
|
+
- `dist/` — the compiled JavaScript and its type declarations
|
|
40
|
+
- `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
|
|
41
|
+
- `docs/` — the package’s own pages in the Endora Commerce documentation site
|
|
42
|
+
- `tailwind.css` — the Tailwind source declaration
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm add @endora-commerce/mod-admin-roles
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Licence
|
|
51
|
+
|
|
52
|
+
MIT — the text is in `LICENSE`, beside this file.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin_roles`' admin surface — **one sidebar entry and no route of its own**,
|
|
3
|
+
* declared by the module that owns it (feature 091, Phase 4, batch four;
|
|
4
|
+
* `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
|
|
5
|
+
*
|
|
6
|
+
* **This module ships a nav entry pointing at a route another module
|
|
7
|
+
* declares**, which the registry supports by design —
|
|
8
|
+
* `AdminContributions`' three arrays are each optional, and
|
|
9
|
+
* `admin/test/lib/module-registry.test.ts` reads *"a module that contributes
|
|
10
|
+
* only nav as contributing no route"* as legal. It is not a shortcut taken to
|
|
11
|
+
* make a move cheap; it is the honest shape of a screen the two hand-written
|
|
12
|
+
* registries have disagreed about since it was written: `App.tsx` imported
|
|
13
|
+
* `AdminRolesPage` out of `admin_users`' surface directory while
|
|
14
|
+
* `AppShell.tsx`'s row declared `module: 'admin_roles'`, and
|
|
15
|
+
* `backend/scripts/ledgers/admin-registrations.ts` records both facts rather
|
|
16
|
+
* than picking one.
|
|
17
|
+
*
|
|
18
|
+
* `plan.md`'s open question 3 recommended keeping exactly that attribution and
|
|
19
|
+
* this is where the recommendation is made explicit. The **route** belongs to
|
|
20
|
+
* `admin_users`, because the screen is served by
|
|
21
|
+
* `GET /api/v1/admin/admin-roles` in that module's `routes.admin.ts` and the
|
|
22
|
+
* component lives in its package; the **sidebar entry and the palette action**
|
|
23
|
+
* belong here, because they advertise the roles capability, which is this
|
|
24
|
+
* module's whole subject. Both modules declare `nonDeactivatable`, so nothing
|
|
25
|
+
* an operator can do makes the split observable today. It stops being inert the
|
|
26
|
+
* day either is unlocked: switching `admin_roles` off would withdraw the
|
|
27
|
+
* advertisement and leave the deep link working, which is the right answer for
|
|
28
|
+
* a module that owns the *roles* capability and not the screen that edits it.
|
|
29
|
+
*
|
|
30
|
+
* **`admin_users:manage`, not an `admin_roles:*` code.** This module registers
|
|
31
|
+
* no admin route and declares no permission of its own, and the endpoints the
|
|
32
|
+
* screen calls all enforce that one code
|
|
33
|
+
* (`packages/modules/admin_users/src/backend/routes.admin.ts`). The comment the
|
|
34
|
+
* hand-written NAV entry carried said so, and it survives the move rather than
|
|
35
|
+
* being re-derived by whoever reads the declaration next. The module
|
|
36
|
+
* attribution and the permission answer two different questions here, which is
|
|
37
|
+
* why they are two fields.
|
|
38
|
+
*
|
|
39
|
+
* **This entry exports data and nothing else** (R2), like every `./admin`
|
|
40
|
+
* layer: `check:module-boundary`'s D-171 rule designates a subpath as contract
|
|
41
|
+
* surface when it emits no runtime binding, and `./admin` deliberately does not
|
|
42
|
+
* qualify, so a consumer reaching into another module's `./admin` stays a
|
|
43
|
+
* counted boundary reach.
|
|
44
|
+
*/
|
|
45
|
+
import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
|
|
46
|
+
export declare const contributions: AdminContributions;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAKnF,eAAO,MAAM,aAAa,EAAE,kBAuB3B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** The roles editor. `admin_users` declares the route; this is where it goes. */
|
|
2
|
+
const ROLES_PATH = '/admin-roles';
|
|
3
|
+
export const contributions = {
|
|
4
|
+
nav: [
|
|
5
|
+
{
|
|
6
|
+
to: ROLES_PATH,
|
|
7
|
+
// Module-relative (R8), resolved in this module's own namespace out of
|
|
8
|
+
// `packages/modules/admin_roles/i18n/`. It was `appShell.nav.roles` in
|
|
9
|
+
// the shared `_i18n` bundle, one of the four shared files a module author
|
|
10
|
+
// had to edit.
|
|
11
|
+
labelKey: 'nav.adminRoles.label',
|
|
12
|
+
// The icon the hand-written NAV entry carried. It joins
|
|
13
|
+
// `KnownIconNameSchema` and `icon-map.ts` in this merge request: a nav
|
|
14
|
+
// entry names its glyph now, so keeping the one the sidebar already drew
|
|
15
|
+
// meant adding the name rather than substituting one already on the list.
|
|
16
|
+
icon: 'ShieldCheck',
|
|
17
|
+
section: 'system',
|
|
18
|
+
// The hand-written table's position times a hundred — see the note in
|
|
19
|
+
// `packages/modules/audit_logs/src/admin/index.ts`. `/admin-roles` was
|
|
20
|
+
// third of thirteen, directly after `/admin-users` (200), and that
|
|
21
|
+
// relative order is preserved.
|
|
22
|
+
weight: 300,
|
|
23
|
+
requiredPermission: 'admin_users:manage',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AA8CA,iFAAiF;AACjF,MAAM,UAAU,GAAG,cAAc,CAAC;AAElC,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,uEAAuE;YACvE,0EAA0E;YAC1E,eAAe;YACf,QAAQ,EAAE,sBAAsB;YAChC,wDAAwD;YACxD,uEAAuE;YACvE,yEAAyE;YACzE,0EAA0E;YAC1E,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,QAAQ;YACjB,sEAAsE;YACtE,uEAAuE;YACvE,mEAAmE;YACnE,+BAA+B;YAC/B,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,oBAAoB;SACzC;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Withdraw `admin_roles`' demo data (feature 113, T222 — contract §2.5).
|
|
3
|
+
*
|
|
4
|
+
* By the fixed codes `seed` assigns, never by a predicate over the table. That
|
|
5
|
+
* is not a precaution here, it is a measured one: `blog` and `cms` seed a
|
|
6
|
+
* `blog_manager` and a `content_manager` role from their own boot hooks, and an
|
|
7
|
+
* operator's own role is indistinguishable from a demo one by every other
|
|
8
|
+
* column. This is the withdrawal that replaces `admin_roles`' line in the host's
|
|
9
|
+
* `truncate … cascade`, which took all four with it — and, through the cascade,
|
|
10
|
+
* the `admin_users` rows pointing at them.
|
|
11
|
+
*
|
|
12
|
+
* The demo administrators' **assignment** to these roles is withdrawn first, by
|
|
13
|
+
* the composition: `reset` runs the composition's withdrawal before any module's
|
|
14
|
+
* (§5.5), which is also what leaves no `admin_users` row referencing a role
|
|
15
|
+
* deleted below.
|
|
16
|
+
*/
|
|
17
|
+
import type { DemoResetResult, ModuleDemoContext } from '@endora-commerce/contracts';
|
|
18
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
19
|
+
export declare function resetDemo(context: ModuleDemoContext<ModuleContext>): Promise<DemoResetResult>;
|
|
20
|
+
//# sourceMappingURL=reset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/reset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAStE,wBAAsB,SAAS,CAC7B,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,GACxC,OAAO,CAAC,eAAe,CAAC,CAS1B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AdminRole } from '../entities/admin-role.entity.js';
|
|
2
|
+
import { DEMO_ADMIN_ROLE_CODES } from './rows.js';
|
|
3
|
+
export async function resetDemo(context) {
|
|
4
|
+
// command-coverage-ignore: the withdrawal half of the demo data above. Same
|
|
5
|
+
// entry point, same `mustBeNonProduction()` guard, same absence of an
|
|
6
|
+
// operator to attribute the write to (contract §2.7).
|
|
7
|
+
const em = context.ctx.cradle().emFactory();
|
|
8
|
+
const removed = await em.nativeDelete(AdminRole, {
|
|
9
|
+
code: { $in: [...DEMO_ADMIN_ROLE_CODES] },
|
|
10
|
+
});
|
|
11
|
+
return { removed: [{ entity: 'AdminRole', count: removed }] };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=reset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.js","sourceRoot":"","sources":["../../../src/backend/demo/reset.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAMlD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAyC;IAEzC,4EAA4E;IAC5E,sEAAsE;IACtE,sDAAsD;IACtD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAwB,CAAC,SAAS,EAAE,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE;QAC/C,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,qBAAqB,CAAC,EAAE;KAC1C,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The admin roles this module's demo data creates (feature 113, T222).
|
|
3
|
+
*
|
|
4
|
+
* One place holding the data, imported by both bodies: `seed.ts` creates these
|
|
5
|
+
* roles and `reset.ts` withdraws exactly them, by the `code` they are keyed on
|
|
6
|
+
* (contract §2.5). A predicate over the table would take `blog_manager` and
|
|
7
|
+
* `content_manager` with it — two roles `blog` and `cms` seed from their own
|
|
8
|
+
* boot hooks, which share this table with the demo and are not the demo's.
|
|
9
|
+
*
|
|
10
|
+
* The values are the host seed's verbatim, moved rather than rewritten. The
|
|
11
|
+
* frozen copy the parity comparison read them against is deleted with that
|
|
12
|
+
* comparison (T226); what holds them now is
|
|
13
|
+
* `test/integration/demo/demo-shop.test.ts`' recorded delta.
|
|
14
|
+
*
|
|
15
|
+
* ## Why the sales representative's permission list is here and is exported
|
|
16
|
+
*
|
|
17
|
+
* It was `backend/src/seeds/seeded-role-permissions.ts`, and it is a constant
|
|
18
|
+
* rather than eight lines inside a seed for a reason that survives the move
|
|
19
|
+
* unchanged: `platform_admin` and the bootstrap admin hold `['*']`, which
|
|
20
|
+
* short-circuits in `PermissionService.hasPermission` before any code is
|
|
21
|
+
* compared, and the two content roles hold their own module's codes. So a change
|
|
22
|
+
* to what a catalogue code grants is felt by exactly this list, and the six
|
|
23
|
+
* `permission-authority` contract tests that assert what the seeded sales
|
|
24
|
+
* representative may reach have to read **the list the seed
|
|
25
|
+
* actually writes** rather than a copy of it — a copy agrees with the seed on
|
|
26
|
+
* the day it is written and never again.
|
|
27
|
+
*
|
|
28
|
+
* Which list that is moved with this batch. The composed path writes this one,
|
|
29
|
+
* so it is re-exported from `../index.js` — the package's own `./backend`
|
|
30
|
+
* subpath — and the six tests name it there. The frozen reference under
|
|
31
|
+
* `backend/src/seeds/` keeps a literal copy of its own on purpose: the two sides
|
|
32
|
+
* of the parity comparison have to be different code, or the comparison cannot
|
|
33
|
+
* see a change to either.
|
|
34
|
+
*/
|
|
35
|
+
/** One demo admin role, in `AdminRole`'s own field names. */
|
|
36
|
+
export interface DemoAdminRoleRow {
|
|
37
|
+
readonly code: string;
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly permissions: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* What the seeded `sales_representative` role may reach.
|
|
43
|
+
*
|
|
44
|
+
* Assignment-scoped visibility (feature 008): the quickstart signs in as this
|
|
45
|
+
* role to exercise it.
|
|
46
|
+
*/
|
|
47
|
+
export declare const SALES_REPRESENTATIVE_PERMISSIONS: readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* The two roles the demo shop has.
|
|
50
|
+
*
|
|
51
|
+
* `['*']` on `platform_admin` is the wildcard `PermissionService` answers before
|
|
52
|
+
* it compares a code, which is what makes the demo administrator able to open
|
|
53
|
+
* every screen a fresh install ships.
|
|
54
|
+
*
|
|
55
|
+
* **Which account holds which role is not here.** An `admin_users` row carrying
|
|
56
|
+
* an `admin_roles` id is two modules' rows in one statement, so the assignment
|
|
57
|
+
* is a composition step (contract §5.1) and lives in
|
|
58
|
+
* `backend/src/seeds/demo-composition.ts`. This body creates the roles that step
|
|
59
|
+
* assigns.
|
|
60
|
+
*/
|
|
61
|
+
export declare const DEMO_ADMIN_ROLES: readonly DemoAdminRoleRow[];
|
|
62
|
+
/** The codes `reset` withdraws — derived from the rows, never a second list. */
|
|
63
|
+
export declare const DEMO_ADMIN_ROLE_CODES: readonly string[];
|
|
64
|
+
//# sourceMappingURL=rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,EAAE,SAAS,MAAM,EAS7D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,gBAAgB,EAOvD,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAA4C,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The admin roles this module's demo data creates (feature 113, T222).
|
|
3
|
+
*
|
|
4
|
+
* One place holding the data, imported by both bodies: `seed.ts` creates these
|
|
5
|
+
* roles and `reset.ts` withdraws exactly them, by the `code` they are keyed on
|
|
6
|
+
* (contract §2.5). A predicate over the table would take `blog_manager` and
|
|
7
|
+
* `content_manager` with it — two roles `blog` and `cms` seed from their own
|
|
8
|
+
* boot hooks, which share this table with the demo and are not the demo's.
|
|
9
|
+
*
|
|
10
|
+
* The values are the host seed's verbatim, moved rather than rewritten. The
|
|
11
|
+
* frozen copy the parity comparison read them against is deleted with that
|
|
12
|
+
* comparison (T226); what holds them now is
|
|
13
|
+
* `test/integration/demo/demo-shop.test.ts`' recorded delta.
|
|
14
|
+
*
|
|
15
|
+
* ## Why the sales representative's permission list is here and is exported
|
|
16
|
+
*
|
|
17
|
+
* It was `backend/src/seeds/seeded-role-permissions.ts`, and it is a constant
|
|
18
|
+
* rather than eight lines inside a seed for a reason that survives the move
|
|
19
|
+
* unchanged: `platform_admin` and the bootstrap admin hold `['*']`, which
|
|
20
|
+
* short-circuits in `PermissionService.hasPermission` before any code is
|
|
21
|
+
* compared, and the two content roles hold their own module's codes. So a change
|
|
22
|
+
* to what a catalogue code grants is felt by exactly this list, and the six
|
|
23
|
+
* `permission-authority` contract tests that assert what the seeded sales
|
|
24
|
+
* representative may reach have to read **the list the seed
|
|
25
|
+
* actually writes** rather than a copy of it — a copy agrees with the seed on
|
|
26
|
+
* the day it is written and never again.
|
|
27
|
+
*
|
|
28
|
+
* Which list that is moved with this batch. The composed path writes this one,
|
|
29
|
+
* so it is re-exported from `../index.js` — the package's own `./backend`
|
|
30
|
+
* subpath — and the six tests name it there. The frozen reference under
|
|
31
|
+
* `backend/src/seeds/` keeps a literal copy of its own on purpose: the two sides
|
|
32
|
+
* of the parity comparison have to be different code, or the comparison cannot
|
|
33
|
+
* see a change to either.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* What the seeded `sales_representative` role may reach.
|
|
37
|
+
*
|
|
38
|
+
* Assignment-scoped visibility (feature 008): the quickstart signs in as this
|
|
39
|
+
* role to exercise it.
|
|
40
|
+
*/
|
|
41
|
+
export const SALES_REPRESENTATIVE_PERMISSIONS = [
|
|
42
|
+
'rfqs:handle',
|
|
43
|
+
'organizations:read.assigned',
|
|
44
|
+
'catalog:read',
|
|
45
|
+
// D-173 — the RFQ create screen prefills the agreed unit price from
|
|
46
|
+
// `GET /admin/products/:id/resolved-price`, which is `price_lists`' own
|
|
47
|
+
// endpoint and is gated on `price_lists:read` rather than on
|
|
48
|
+
// `rfqs:handle`. Without this code the prefill answers 403.
|
|
49
|
+
'price_lists:read',
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* The two roles the demo shop has.
|
|
53
|
+
*
|
|
54
|
+
* `['*']` on `platform_admin` is the wildcard `PermissionService` answers before
|
|
55
|
+
* it compares a code, which is what makes the demo administrator able to open
|
|
56
|
+
* every screen a fresh install ships.
|
|
57
|
+
*
|
|
58
|
+
* **Which account holds which role is not here.** An `admin_users` row carrying
|
|
59
|
+
* an `admin_roles` id is two modules' rows in one statement, so the assignment
|
|
60
|
+
* is a composition step (contract §5.1) and lives in
|
|
61
|
+
* `backend/src/seeds/demo-composition.ts`. This body creates the roles that step
|
|
62
|
+
* assigns.
|
|
63
|
+
*/
|
|
64
|
+
export const DEMO_ADMIN_ROLES = [
|
|
65
|
+
{ code: 'platform_admin', name: 'Platform Admin', permissions: ['*'] },
|
|
66
|
+
{
|
|
67
|
+
code: 'sales_representative',
|
|
68
|
+
name: 'Sales representative',
|
|
69
|
+
permissions: SALES_REPRESENTATIVE_PERMISSIONS,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
/** The codes `reset` withdraws — derived from the rows, never a second list. */
|
|
73
|
+
export const DEMO_ADMIN_ROLE_CODES = DEMO_ADMIN_ROLES.map((row) => row.code);
|
|
74
|
+
//# sourceMappingURL=rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/backend/demo/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AASH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAsB;IACjE,aAAa;IACb,6BAA6B;IAC7B,cAAc;IACd,oEAAoE;IACpE,wEAAwE;IACxE,6DAA6D;IAC7D,4DAA4D;IAC5D,kBAAkB;CACnB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAgC;IAC3D,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,GAAG,CAAC,EAAE;IACtE;QACE,IAAI,EAAE,sBAAsB;QAC5B,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,gCAAgC;KAC9C;CACF,CAAC;AAEF,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAsB,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin_roles`' demo data (feature 113, T222 — contract §2).
|
|
3
|
+
*
|
|
4
|
+
* The two roles the demo shop signs in with, in the module that owns the table.
|
|
5
|
+
* It writes `admin_roles` and nothing else (§2.1), reads no other module's table
|
|
6
|
+
* and resolves no port (§2.2), so declaring it added no entry to this module's
|
|
7
|
+
* manifest `dependencies` (§2.3).
|
|
8
|
+
*
|
|
9
|
+
* **Who holds which role is not this body's.** An `admin_users` row carrying an
|
|
10
|
+
* `admin_roles` id is two modules' rows in one statement — a composition step
|
|
11
|
+
* (§5.1), in `backend/src/seeds/demo-composition.ts`, which runs after every
|
|
12
|
+
* module's `seed` and so finds both sides of the assignment already there.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent by an existence probe on the natural key (§2.4): a second run
|
|
15
|
+
* creates nothing and reports the same count.
|
|
16
|
+
*/
|
|
17
|
+
import type { DemoSeedResult, ModuleDemoContext } from '@endora-commerce/contracts';
|
|
18
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
19
|
+
export declare function seedDemo(context: ModuleDemoContext<ModuleContext>): Promise<DemoSeedResult>;
|
|
20
|
+
//# sourceMappingURL=seed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAUtE,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CA0BzB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AdminRole } from '../entities/admin-role.entity.js';
|
|
2
|
+
import { DEMO_ADMIN_ROLES } from './rows.js';
|
|
3
|
+
export async function seedDemo(context) {
|
|
4
|
+
// command-coverage-ignore: demo data, reached only by `endora demo seed`,
|
|
5
|
+
// whose entry point calls `mustBeNonProduction()` as its first statement and
|
|
6
|
+
// outside every `try` (contract §2.7, §3.3). The guard is the enforcement:
|
|
7
|
+
// this write has no operator, no tenant and no audit reader, and the command
|
|
8
|
+
// refuses to run against a production database at all.
|
|
9
|
+
const em = context.ctx.cradle().emFactory();
|
|
10
|
+
for (const row of DEMO_ADMIN_ROLES) {
|
|
11
|
+
const existing = await em.findOne(AdminRole, { code: row.code });
|
|
12
|
+
// Present already: left exactly as it is. An operator may have narrowed
|
|
13
|
+
// what the demo sales representative may reach, and overwriting it here
|
|
14
|
+
// would be this body deciding its own literal outranks their change —
|
|
15
|
+
// which is `reset`'s question, not `seed`'s.
|
|
16
|
+
if (existing)
|
|
17
|
+
continue;
|
|
18
|
+
em.create(AdminRole, {
|
|
19
|
+
code: row.code,
|
|
20
|
+
name: row.name,
|
|
21
|
+
permissions: [...row.permissions],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
await em.flush();
|
|
25
|
+
// What the demo *holds* after the run, not what this call inserted: seeding
|
|
26
|
+
// twice must report the same counts (`DemoEntityCount`).
|
|
27
|
+
return { created: [{ entity: 'AdminRole', count: DEMO_ADMIN_ROLES.length }] };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/backend/demo/seed.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAO7C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAyC;IAEzC,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,6EAA6E;IAC7E,uDAAuD;IACvD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAwB,CAAC,SAAS,EAAE,CAAC;IAElE,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACjE,wEAAwE;QACxE,wEAAwE;QACxE,sEAAsE;QACtE,6CAA6C;QAC7C,IAAI,QAAQ;YAAE,SAAS;QACvB,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;YACnB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC;SAClC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IAEjB,4EAA4E;IAC5E,yDAAyD;IACzD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OptionalProps } from '@mikro-orm/core';
|
|
2
|
+
/**
|
|
3
|
+
* AdminRole — a named permission bundle assigned to AdminUsers. Permissions
|
|
4
|
+
* are screaming-snake `module:action` strings (e.g. `catalog:write`,
|
|
5
|
+
* `orders:read`, `customers:impersonate`). Stored as JSONB array.
|
|
6
|
+
*
|
|
7
|
+
* `requiresTwoFactor` flips the requireAdmin pre-handler into a strict mode
|
|
8
|
+
* (T188 hook) — accounts mapped to such a Role MUST have 2FA confirmed.
|
|
9
|
+
*/
|
|
10
|
+
export declare class AdminRole {
|
|
11
|
+
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'requiresTwoFactor' | 'permissions';
|
|
12
|
+
id: string;
|
|
13
|
+
code: string;
|
|
14
|
+
name: string;
|
|
15
|
+
permissions: string[];
|
|
16
|
+
requiresTwoFactor: boolean;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
updatedAt: Date;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=admin-role.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-role.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/admin-role.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAItF;;;;;;;GAOG;AACH,qBAEa,SAAS;IACpB,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,WAAW,GACX,mBAAmB,GACnB,aAAa,CAAC;IAGlB,EAAE,EAAE,MAAM,CAAgB;IAI1B,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAGd,WAAW,EAAE,MAAM,EAAE,CAAM;IAG3B,iBAAiB,EAAE,OAAO,CAAS;IAGnC,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Entity, OptionalProps, PrimaryKey, Property, Unique } from '@mikro-orm/core';
|
|
11
|
+
import { GlobalEntity } from '@endora-commerce/platform/tenancy';
|
|
12
|
+
import { randomUUID } from 'crypto';
|
|
13
|
+
/**
|
|
14
|
+
* AdminRole — a named permission bundle assigned to AdminUsers. Permissions
|
|
15
|
+
* are screaming-snake `module:action` strings (e.g. `catalog:write`,
|
|
16
|
+
* `orders:read`, `customers:impersonate`). Stored as JSONB array.
|
|
17
|
+
*
|
|
18
|
+
* `requiresTwoFactor` flips the requireAdmin pre-handler into a strict mode
|
|
19
|
+
* (T188 hook) — accounts mapped to such a Role MUST have 2FA confirmed.
|
|
20
|
+
*/
|
|
21
|
+
let AdminRole = class AdminRole {
|
|
22
|
+
[OptionalProps];
|
|
23
|
+
id = randomUUID();
|
|
24
|
+
code;
|
|
25
|
+
name;
|
|
26
|
+
permissions = [];
|
|
27
|
+
requiresTwoFactor = false;
|
|
28
|
+
createdAt = new Date();
|
|
29
|
+
updatedAt = new Date();
|
|
30
|
+
};
|
|
31
|
+
__decorate([
|
|
32
|
+
PrimaryKey({ type: 'uuid' }),
|
|
33
|
+
__metadata("design:type", String)
|
|
34
|
+
], AdminRole.prototype, "id", void 0);
|
|
35
|
+
__decorate([
|
|
36
|
+
Property({ type: 'string', length: 64 }),
|
|
37
|
+
Unique(),
|
|
38
|
+
__metadata("design:type", String)
|
|
39
|
+
], AdminRole.prototype, "code", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
Property({ type: 'string', length: 160 }),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], AdminRole.prototype, "name", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
Property({ type: 'json' }),
|
|
46
|
+
__metadata("design:type", Array)
|
|
47
|
+
], AdminRole.prototype, "permissions", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
Property({ type: 'boolean' }),
|
|
50
|
+
__metadata("design:type", Boolean)
|
|
51
|
+
], AdminRole.prototype, "requiresTwoFactor", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
Property({ type: 'datetime', onCreate: () => new Date() }),
|
|
54
|
+
__metadata("design:type", Date)
|
|
55
|
+
], AdminRole.prototype, "createdAt", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
Property({ type: 'datetime', onUpdate: () => new Date() }),
|
|
58
|
+
__metadata("design:type", Date)
|
|
59
|
+
], AdminRole.prototype, "updatedAt", void 0);
|
|
60
|
+
AdminRole = __decorate([
|
|
61
|
+
GlobalEntity(),
|
|
62
|
+
Entity({ tableName: 'admin_roles' })
|
|
63
|
+
], AdminRole);
|
|
64
|
+
export { AdminRole };
|
|
65
|
+
//# sourceMappingURL=admin-role.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-role.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/admin-role.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;GAOG;AAGI,IAAM,SAAS,GAAf,MAAM,SAAS;IACpB,CAAC,aAAa,CAAC,CAKG;IAGlB,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,IAAI,CAAU;IAGd,IAAI,CAAU;IAGd,WAAW,GAAa,EAAE,CAAC;IAG3B,iBAAiB,GAAY,KAAK,CAAC;IAGnC,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AApBC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qCACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxC,MAAM,EAAE;;uCACK;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;uCAC5B;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;8CACA;AAG3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;oDACK;AAGnC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;4CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;4CAAc;AA5BlB,SAAS;IAFrB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;GACxB,SAAS,CA6BrB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
3
|
+
import type { ModuleManifest, SystemRoleCodePort } from '@endora-commerce/contracts';
|
|
4
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { AdminRoleService } from './services/admin-role-service.js';
|
|
6
|
+
import { PermissionCatalogueService } from './services/permission-catalogue.service.js';
|
|
7
|
+
import { AdminRole } from './entities/admin-role.entity.js';
|
|
8
|
+
/**
|
|
9
|
+
* `admin_roles` — the permission model behind every admin guard (feature 072,
|
|
10
|
+
* wave 1).
|
|
11
|
+
*
|
|
12
|
+
* It owns no routes. Its admin surface is registered by `admin_users`, which is
|
|
13
|
+
* shape D's usual shape seen from the other side: the module that owns the
|
|
14
|
+
* behaviour and the module that owns the URL are different, and only the second
|
|
15
|
+
* one has a `plugin.ts`. Converting it needs no route seam, because there is
|
|
16
|
+
* nothing to gate here — what moves is which composition root constructs the
|
|
17
|
+
* three services, and the answer is now "neither".
|
|
18
|
+
*
|
|
19
|
+
* `permissionService` is the reason this module goes first in wave 1. `auth`
|
|
20
|
+
* resolves it to build `requireAdmin`, and until now both composition roots
|
|
21
|
+
* registered it as a **host value** on `admin_roles`' behalf — a declared edge
|
|
22
|
+
* (D-32) standing in for a registration. That entry leaves
|
|
23
|
+
* `HOST_REGISTERED_PORTS` with this commit.
|
|
24
|
+
*
|
|
25
|
+
* The catalogue takes the **resolved** registry — core manifests plus the
|
|
26
|
+
* active deployment's overlay modules — which is a composition-root input, not
|
|
27
|
+
* something this module can see. It stays a host value for that reason, and the
|
|
28
|
+
* reason is durable rather than temporary: which modules a deployment ships is
|
|
29
|
+
* exactly the kind of thing a module must not decide for itself.
|
|
30
|
+
*/
|
|
31
|
+
export interface AdminRolesCradle {
|
|
32
|
+
readonly emFactory: () => EntityManager;
|
|
33
|
+
readonly auditLogService: AuditPort;
|
|
34
|
+
/**
|
|
35
|
+
* Core manifests + this deployment's overlay modules (feature 057).
|
|
36
|
+
*
|
|
37
|
+
* Typed by what this module reads rather than by `_lifecycle`'s
|
|
38
|
+
* `RegisteredManifestEntry` (feature 075, Phase C). The entries the root
|
|
39
|
+
* contributes carry a `filePath` and the install hooks as well, and none of
|
|
40
|
+
* that is any of this module's business: the catalogue walks
|
|
41
|
+
* `manifest.permissions` and nothing else, and it already declared that
|
|
42
|
+
* shape for itself.
|
|
43
|
+
*/
|
|
44
|
+
readonly resolvedModuleRegistry: ReadonlyArray<{
|
|
45
|
+
manifest: ModuleManifest;
|
|
46
|
+
}>;
|
|
47
|
+
readonly permissionCatalogueService: PermissionCatalogueService;
|
|
48
|
+
readonly adminRoleService: AdminRoleService;
|
|
49
|
+
/** Feature 075, Phase P — the deletion-protection contribution seam. */
|
|
50
|
+
readonly systemRoleCodePort: SystemRoleCodePort;
|
|
51
|
+
}
|
|
52
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
53
|
+
/**
|
|
54
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
55
|
+
* array and **no named class export** (D-168).
|
|
56
|
+
*
|
|
57
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
58
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
59
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
60
|
+
* which is the third source of `check:module-boundary`'s `table->owner` map and
|
|
61
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
62
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
63
|
+
*
|
|
64
|
+
* The order is the one `db/entities-registry.generated.ts` declared before this
|
|
65
|
+
* module became a package, so the registered set is the same list in the same
|
|
66
|
+
* sequence.
|
|
67
|
+
*/
|
|
68
|
+
export declare const entities: (typeof AdminRole)[];
|
|
69
|
+
/**
|
|
70
|
+
* The permission catalogue and the inventory scanner, published **by name**.
|
|
71
|
+
*
|
|
72
|
+
* Three host programs read them — the acceptance instance probe,
|
|
73
|
+
* `check:action-route-permissions`, and the permission-inventory contract test
|
|
74
|
+
* — and each of those processes also holds this package's published artefact,
|
|
75
|
+
* so a filesystem path into this module's source would give them a second copy
|
|
76
|
+
* of everything on that file's graph (D-160.6.1). None of these is an entity,
|
|
77
|
+
* which is the one thing D-168 keeps off this door; what they are is the
|
|
78
|
+
* host-facing half of the permission estate, and it has to be the same half the
|
|
79
|
+
* platform composed or a scanner reports on a catalogue nobody enforces.
|
|
80
|
+
*/
|
|
81
|
+
/**
|
|
82
|
+
* The permission list the demo's `sales_representative` role carries (feature
|
|
83
|
+
* 113, T222).
|
|
84
|
+
*
|
|
85
|
+
* It was `backend/src/seeds/seeded-role-permissions.ts`, and it is published
|
|
86
|
+
* here because six `test/contract/*\/permission-authority.test.ts` files assert
|
|
87
|
+
* what that role may reach and must read **the list the seed actually writes**,
|
|
88
|
+
* not a copy of it. Since T222 the seed that writes it is this module's own
|
|
89
|
+
* demo body, so this is where the list lives and this is the door those tests
|
|
90
|
+
* name. It is a `readonly string[]` of catalogue codes — no entity, so D-168
|
|
91
|
+
* does not bar it — and `demo/rows.ts` carries nothing else on its graph.
|
|
92
|
+
*/
|
|
93
|
+
export { SALES_REPRESENTATIVE_PERMISSIONS } from './demo/rows.js';
|
|
94
|
+
export { PermissionCatalogueService, listAssignablePermissionCodes } from './services/permission-catalogue.service.js';
|
|
95
|
+
export { ConstantResolver, defaultScanRoots, scanEnforcedPermissionCodes, scanEnforcedPermissionGates } from './permission-inventory.js';
|
|
96
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAGV,cAAc,EAGd,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4CAA4C,CAAC;AAExF,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,sBAAsB,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;IAC7E,QAAQ,CAAC,0BAA0B,EAAE,0BAA0B,CAAC;IAChE,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;CACjD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2FvD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,sBAEpB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,0BAA0B,EAAE,6BAA6B,EAAE,MAAM,4CAA4C,CAAC;AACvH,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC"}
|