@astram/identity 0.1.2 → 0.2.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.
- package/dist/auth-facts-keys.d.ts +3 -0
- package/dist/auth-facts-keys.d.ts.map +1 -0
- package/dist/auth-facts-keys.js +24 -0
- package/dist/cross-tenant-grant.object.d.ts +28 -0
- package/dist/cross-tenant-grant.object.d.ts.map +1 -0
- package/dist/cross-tenant-grant.object.js +64 -0
- package/dist/features/access/access-admin.access.d.ts +3 -0
- package/dist/features/access/access-admin.access.d.ts.map +1 -0
- package/dist/features/access/access-admin.access.js +27 -0
- package/dist/features/access/company-admin.access.d.ts +3 -0
- package/dist/features/access/company-admin.access.d.ts.map +1 -0
- package/dist/features/access/company-admin.access.js +31 -0
- package/dist/features/access/index.d.ts +15 -0
- package/dist/features/access/index.d.ts.map +1 -0
- package/dist/features/access/index.js +26 -0
- package/dist/features/access/user-management.access.d.ts +3 -0
- package/dist/features/access/user-management.access.d.ts.map +1 -0
- package/dist/features/access/user-management.access.js +35 -0
- package/dist/features/audit/audit-read.service.d.ts +92 -0
- package/dist/features/audit/audit-read.service.d.ts.map +1 -0
- package/dist/features/audit/audit-read.service.js +97 -0
- package/dist/features/audit/audit.controller.d.ts +35 -0
- package/dist/features/audit/audit.controller.d.ts.map +1 -0
- package/dist/features/audit/audit.controller.js +73 -0
- package/dist/features/audit/directory-label-resolver.d.ts +14 -0
- package/dist/features/audit/directory-label-resolver.d.ts.map +1 -0
- package/dist/features/audit/directory-label-resolver.js +46 -0
- package/dist/features/audit/mariadb-audit-reader.d.ts +46 -0
- package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -0
- package/dist/features/audit/mariadb-audit-reader.js +116 -0
- package/dist/features/authz/access-bundle-reconcile.job.d.ts +35 -0
- package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -0
- package/dist/features/authz/access-bundle-reconcile.job.js +84 -0
- package/dist/features/authz/access-bundle-store.d.ts +65 -0
- package/dist/features/authz/access-bundle-store.d.ts.map +1 -0
- package/dist/features/authz/access-bundle-store.js +154 -0
- package/dist/features/authz/catalog-bootstrap.provider.d.ts +28 -0
- package/dist/features/authz/catalog-bootstrap.provider.d.ts.map +1 -0
- package/dist/features/authz/catalog-bootstrap.provider.js +85 -0
- package/dist/features/authz/catalog-reconcile.job.d.ts +23 -0
- package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -0
- package/dist/features/authz/catalog-reconcile.job.js +47 -0
- package/dist/features/authz/db-permission-facts.adapter.d.ts +60 -0
- package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -0
- package/dist/features/authz/db-permission-facts.adapter.js +116 -0
- package/dist/features/authz/grant-admin.controller.d.ts +96 -0
- package/dist/features/authz/grant-admin.controller.d.ts.map +1 -0
- package/dist/features/authz/grant-admin.controller.js +209 -0
- package/dist/features/authz/grant-admin.service.d.ts +107 -0
- package/dist/features/authz/grant-admin.service.d.ts.map +1 -0
- package/dist/features/authz/grant-admin.service.js +297 -0
- package/dist/features/authz/mariadb-catalog-store.d.ts +16 -0
- package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -0
- package/dist/features/authz/mariadb-catalog-store.js +44 -0
- package/dist/features/authz/permission-version.bumper.d.ts +47 -0
- package/dist/features/authz/permission-version.bumper.d.ts.map +1 -0
- package/dist/features/authz/permission-version.bumper.js +68 -0
- package/dist/features/authz/record-authorization.d.ts +31 -0
- package/dist/features/authz/record-authorization.d.ts.map +1 -0
- package/dist/features/authz/record-authorization.js +59 -0
- package/dist/features/authz/session-facts.reader.d.ts +41 -0
- package/dist/features/authz/session-facts.reader.d.ts.map +1 -0
- package/dist/features/authz/session-facts.reader.js +27 -0
- package/dist/features/company/company.controller.d.ts +37 -0
- package/dist/features/company/company.controller.d.ts.map +1 -0
- package/dist/features/company/company.controller.js +100 -0
- package/dist/features/company/company.service.d.ts +25 -0
- package/dist/features/company/company.service.d.ts.map +1 -0
- package/dist/features/company/company.service.js +53 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +126 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +276 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts +65 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts.map +1 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.js +250 -0
- package/dist/features/cross-tenant/grant-authorize.d.ts +42 -0
- package/dist/features/cross-tenant/grant-authorize.d.ts.map +1 -0
- package/dist/features/cross-tenant/grant-authorize.js +17 -0
- package/dist/features/identity-feature.module.d.ts +26 -0
- package/dist/features/identity-feature.module.d.ts.map +1 -0
- package/dist/features/identity-feature.module.js +247 -0
- package/dist/features/membership/membership.controller.d.ts +77 -0
- package/dist/features/membership/membership.controller.d.ts.map +1 -0
- package/dist/features/membership/membership.controller.js +174 -0
- package/dist/features/membership/membership.service.d.ts +73 -0
- package/dist/features/membership/membership.service.d.ts.map +1 -0
- package/dist/features/membership/membership.service.js +173 -0
- package/dist/features/membership/posting-version-bump.d.ts +10 -0
- package/dist/features/membership/posting-version-bump.d.ts.map +1 -0
- package/dist/features/membership/posting-version-bump.js +4 -0
- package/dist/features/membership/posting.service.d.ts +59 -0
- package/dist/features/membership/posting.service.d.ts.map +1 -0
- package/dist/features/membership/posting.service.js +181 -0
- package/dist/features/org-structure/org-catalog.controller.d.ts +14 -0
- package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -0
- package/dist/features/org-structure/org-catalog.controller.js +62 -0
- package/dist/features/org-structure/org-catalog.service.d.ts +19 -0
- package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -0
- package/dist/features/org-structure/org-catalog.service.js +53 -0
- package/dist/features/role/role.controller.d.ts +37 -0
- package/dist/features/role/role.controller.d.ts.map +1 -0
- package/dist/features/role/role.controller.js +104 -0
- package/dist/features/role/role.service.d.ts +24 -0
- package/dist/features/role/role.service.d.ts.map +1 -0
- package/dist/features/role/role.service.js +53 -0
- package/dist/features/shared/http.d.ts +15 -0
- package/dist/features/shared/http.d.ts.map +1 -0
- package/dist/features/shared/http.js +25 -0
- package/dist/features/shared/list-query.d.ts +44 -0
- package/dist/features/shared/list-query.d.ts.map +1 -0
- package/dist/features/shared/list-query.js +53 -0
- package/dist/features/shared/named-option.d.ts +6 -0
- package/dist/features/shared/named-option.d.ts.map +1 -0
- package/dist/features/shared/named-option.js +2 -0
- package/dist/features/shared/record-resolver.d.ts +11 -0
- package/dist/features/shared/record-resolver.d.ts.map +1 -0
- package/dist/features/shared/record-resolver.js +32 -0
- package/dist/features/user/user.controller.d.ts +39 -0
- package/dist/features/user/user.controller.d.ts.map +1 -0
- package/dist/features/user/user.controller.js +104 -0
- package/dist/features/user/user.service.d.ts +26 -0
- package/dist/features/user/user.service.d.ts.map +1 -0
- package/dist/features/user/user.service.js +53 -0
- package/dist/index.d.ts +34 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -33
- package/dist/objects.d.ts +13 -0
- package/dist/objects.d.ts.map +1 -0
- package/dist/objects.js +20 -0
- package/dist/shared-resource.object.d.ts +28 -0
- package/dist/shared-resource.object.d.ts.map +1 -0
- package/dist/shared-resource.object.js +63 -0
- package/migrations/1710000000010-CreateAuditLog.cjs +42 -0
- package/migrations/1710000000011-CreateCrossTenantGrant.cjs +66 -0
- package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +86 -0
- package/migrations/1710000000013-CreateSharedResource.cjs +30 -0
- package/migrations/1710000000099-SeedDevIdentity.cjs +130 -0
- package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +212 -0
- package/package.json +5 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createIdentityAuthzPool = createIdentityAuthzPool;
|
|
7
|
+
exports.buildIdentityRecordAuthorization = buildIdentityRecordAuthorization;
|
|
8
|
+
/**
|
|
9
|
+
* Assemble the record-tier authorization wiring for `@astram/identity`: the lean grant-read
|
|
10
|
+
* pool factory + the `recordAuthorization` inputs the consumer passes to
|
|
11
|
+
* `ShieldModule.forRoot({ recordAuthorization })`. Once wired, `decide()` becomes REAL (the
|
|
12
|
+
* framework's four record-tier adapters are built internally over the supplied pool) instead
|
|
13
|
+
* of the deny-everything stub.
|
|
14
|
+
*
|
|
15
|
+
* Modeled on astram3's `buildAuthorizationInputs` (`server/src/features/authz/authz.wiring.ts`),
|
|
16
|
+
* adapted to `@astram/identity`'s own registrations (`identityObjects`) and env prefix
|
|
17
|
+
* (`APP_DB_*`, per the Task-7 rename).
|
|
18
|
+
*/
|
|
19
|
+
const mariadb_1 = __importDefault(require("mariadb"));
|
|
20
|
+
const guard_1 = require("@astram/guard");
|
|
21
|
+
const objects_js_1 = require("../../objects.js");
|
|
22
|
+
const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
|
|
23
|
+
const session_facts_reader_js_1 = require("./session-facts.reader.js");
|
|
24
|
+
/**
|
|
25
|
+
* A lean `mariadb` pool for the authz grant-reads (built from `APP_DB_*` env via guard's
|
|
26
|
+
* `loadDbConfig`). Kept small — these are pre-gate coarse-grant reads, not the main CRUD pool.
|
|
27
|
+
* Opens the pool at CALL time (never at import) — the consumer (app lifecycle) owns closing it.
|
|
28
|
+
*/
|
|
29
|
+
function createIdentityAuthzPool() {
|
|
30
|
+
const db = (0, guard_1.loadDbConfig)();
|
|
31
|
+
return mariadb_1.default.createPool({
|
|
32
|
+
host: db.host,
|
|
33
|
+
port: db.port,
|
|
34
|
+
user: db.user,
|
|
35
|
+
password: db.password,
|
|
36
|
+
database: db.database,
|
|
37
|
+
connectionLimit: Number(process.env.APP_AUTHZ_POOL_MAX ?? 4),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the `RecordAuthorizationInputs` the consumer passes to
|
|
42
|
+
* `ShieldModule.forRoot({ recordAuthorization })`. The framework owns the four record-tier
|
|
43
|
+
* adapters internally; `@astram/identity` supplies only the app-schema ports:
|
|
44
|
+
* - `permissionFacts` — coarse grants over `shield_authz.role_permission` +
|
|
45
|
+
* `company_membership`/`membership_posting`,
|
|
46
|
+
* - `sessionFacts` — the ADR-0011 ALS-backed per-request reader,
|
|
47
|
+
* - `registry` — bridged from `identityObjects`,
|
|
48
|
+
* - `operationManifest` — empty (identity declares no trusted delegated operations).
|
|
49
|
+
*/
|
|
50
|
+
function buildIdentityRecordAuthorization(pool) {
|
|
51
|
+
const registry = (0, guard_1.buildIdentityModuleRegistry)(objects_js_1.identityObjects);
|
|
52
|
+
return {
|
|
53
|
+
pool,
|
|
54
|
+
permissionFacts: new db_permission_facts_adapter_js_1.DbPermissionFactsAdapter(pool, registry),
|
|
55
|
+
sessionFacts: new session_facts_reader_js_1.AlsSessionFactsReader(),
|
|
56
|
+
registry,
|
|
57
|
+
operationManifest: [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-request session-facts reader (ADR-0011 hot path) — ported from astram3's
|
|
3
|
+
* `AlsSessionFactsReader` (`server/src/features/authz/empty-ports.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Reads THIS request's cached coarse grants out of the framework's SHARED session-facts
|
|
6
|
+
* `AsyncLocalStorage` (`SHIELD_SESSION_FACTS_ALS`, exported from `@astram/guard`) — the SAME
|
|
7
|
+
* instance the enforcement guard ENTERS around its `decide()` call. When the guard entered
|
|
8
|
+
* with the request's projected `cachedFacts`, gate 5 can serve the coarse decision from the
|
|
9
|
+
* cache instead of a DB reload — provided the cached version is still fresh; a stale/absent
|
|
10
|
+
* cache still reloads (gate 5's version gate).
|
|
11
|
+
*
|
|
12
|
+
* ⛔ FAIL-CLOSED + CROSS-PRINCIPAL ISOLATION: the reader holds NO per-request state on `this`
|
|
13
|
+
* — only the shared ALS handle. `getStore()` returns the CURRENT async context's store (the
|
|
14
|
+
* guard's `als.run(...)` around decide() binds it to this request only), so a concurrent
|
|
15
|
+
* request never sees another's facts. Outside any scope, or when the guard entered with
|
|
16
|
+
* `cachedFacts: null` (no/malformed projection), `getStore()` is `undefined` → the reader
|
|
17
|
+
* returns `{ cachedFacts: null, permissionsVersion: 0 }` → gate 5 DB-reloads. A cache miss
|
|
18
|
+
* NEVER fabricates a permit.
|
|
19
|
+
*/
|
|
20
|
+
import type { SessionFactsReader } from '@astram/core';
|
|
21
|
+
import { type SessionFactsStore } from '@astram/guard';
|
|
22
|
+
/**
|
|
23
|
+
* A {@link SessionFactsReader} whose getters read the request's facts out of the framework's
|
|
24
|
+
* shared session-facts ALS. Constructed with that exact singleton by default (imported from
|
|
25
|
+
* `@astram/guard`, the SAME one the guard enters), so the reader and the guard share one
|
|
26
|
+
* store — no duplicate ALS.
|
|
27
|
+
*/
|
|
28
|
+
export declare class AlsSessionFactsReader implements SessionFactsReader {
|
|
29
|
+
/** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
|
|
30
|
+
private readonly als;
|
|
31
|
+
constructor(
|
|
32
|
+
/** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
|
|
33
|
+
als?: {
|
|
34
|
+
getStore(): SessionFactsStore | undefined;
|
|
35
|
+
});
|
|
36
|
+
/** THIS request's cached coarse grants, or `null` (no scope / no facts → gate 5 reloads). */
|
|
37
|
+
get cachedFacts(): unknown;
|
|
38
|
+
/** The version the cached facts were projected at (0 when absent → treated as stale). */
|
|
39
|
+
get permissionsVersion(): number;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=session-facts.reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-facts.reader.d.ts","sourceRoot":"","sources":["../../../src/features/authz/session-facts.reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAA8C,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEnG;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAE5D,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,GAAG;;IADpB,uFAAuF;IACtE,GAAG,GAAE;QACpB,QAAQ,IAAI,iBAAiB,GAAG,SAAS,CAAC;KAChB;IAG9B,6FAA6F;IAC7F,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,yFAAyF;IACzF,IAAI,kBAAkB,IAAI,MAAM,CAE/B;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AlsSessionFactsReader = void 0;
|
|
4
|
+
const guard_1 = require("@astram/guard");
|
|
5
|
+
/**
|
|
6
|
+
* A {@link SessionFactsReader} whose getters read the request's facts out of the framework's
|
|
7
|
+
* shared session-facts ALS. Constructed with that exact singleton by default (imported from
|
|
8
|
+
* `@astram/guard`, the SAME one the guard enters), so the reader and the guard share one
|
|
9
|
+
* store — no duplicate ALS.
|
|
10
|
+
*/
|
|
11
|
+
class AlsSessionFactsReader {
|
|
12
|
+
als;
|
|
13
|
+
constructor(
|
|
14
|
+
/** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
|
|
15
|
+
als = guard_1.SHIELD_SESSION_FACTS_ALS) {
|
|
16
|
+
this.als = als;
|
|
17
|
+
}
|
|
18
|
+
/** THIS request's cached coarse grants, or `null` (no scope / no facts → gate 5 reloads). */
|
|
19
|
+
get cachedFacts() {
|
|
20
|
+
return (this.als.getStore() ?? guard_1.NO_SESSION_FACTS).cachedFacts;
|
|
21
|
+
}
|
|
22
|
+
/** The version the cached facts were projected at (0 when absent → treated as stale). */
|
|
23
|
+
get permissionsVersion() {
|
|
24
|
+
return (this.als.getStore() ?? guard_1.NO_SESSION_FACTS).permissionsVersion;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.AlsSessionFactsReader = AlsSessionFactsReader;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CompanyService } from './company.service.js';
|
|
2
|
+
import { type ListQueryDto } from '../shared/list-query.js';
|
|
3
|
+
export declare class CompanyController {
|
|
4
|
+
private readonly companies;
|
|
5
|
+
constructor(companies: CompanyService);
|
|
6
|
+
/**
|
|
7
|
+
* List companies — gated on `identity:company:read`. Server-side DataTable:
|
|
8
|
+
* `?page&limit&search&sortBy&sortOrder`. Returns `{ total, rows, page, limit }` —
|
|
9
|
+
* `total` reflects the FILTERED set so the client can page the true result count.
|
|
10
|
+
*/
|
|
11
|
+
listCompanies(query: ListQueryDto): Promise<{
|
|
12
|
+
total: number;
|
|
13
|
+
rows: readonly import("packages/engine/dist/types.js").RecordView[];
|
|
14
|
+
page: number;
|
|
15
|
+
limit: number;
|
|
16
|
+
}>;
|
|
17
|
+
/** Create a company (a new tenant) — gated on `identity:company:create`. */
|
|
18
|
+
createCompany(body: {
|
|
19
|
+
name?: string;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
id: import("packages/engine/dist/types.js").RecordId;
|
|
22
|
+
}>;
|
|
23
|
+
/** Read one company — gated on `identity:company:read`. */
|
|
24
|
+
getCompany(uuid: string): Promise<import("packages/engine/dist/types.js").RecordView | null>;
|
|
25
|
+
/** Update a company — gated on `identity:company:update`. */
|
|
26
|
+
updateCompany(uuid: string, body: {
|
|
27
|
+
name?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
success: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/** Delete a company — gated on `identity:company:delete`. */
|
|
33
|
+
deleteCompany(uuid: string): Promise<{
|
|
34
|
+
success: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=company.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company.controller.d.ts","sourceRoot":"","sources":["../../../src/features/company/company.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5E,qBACa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,cAAc;IAEtD;;;;OAIG;IAGG,aAAa,CAAU,KAAK,EAAE,YAAY;;;;;;IAMhD,4EAA4E;IAGtE,aAAa,CAAS,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAKnD,2DAA2D;IAG3D,UAAU,CAAgB,IAAI,EAAE,MAAM;IAItC,6DAA6D;IAGvD,aAAa,CAAgB,IAAI,EAAE,MAAM,EAAU,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAKjG,6DAA6D;IAGvD,aAAa,CAAgB,IAAI,EAAE,MAAM;;;CAIhD"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CompanyController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const guard_1 = require("@astram/guard");
|
|
18
|
+
const company_service_js_1 = require("./company.service.js");
|
|
19
|
+
const list_query_js_1 = require("../shared/list-query.js");
|
|
20
|
+
let CompanyController = class CompanyController {
|
|
21
|
+
companies;
|
|
22
|
+
constructor(companies) {
|
|
23
|
+
this.companies = companies;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* List companies — gated on `identity:company:read`. Server-side DataTable:
|
|
27
|
+
* `?page&limit&search&sortBy&sortOrder`. Returns `{ total, rows, page, limit }` —
|
|
28
|
+
* `total` reflects the FILTERED set so the client can page the true result count.
|
|
29
|
+
*/
|
|
30
|
+
async listCompanies(query) {
|
|
31
|
+
const { page, limit, params } = (0, list_query_js_1.parseListQuery)(query);
|
|
32
|
+
const result = await this.companies.listCompanies(params);
|
|
33
|
+
return { total: result.total, rows: result.rows, page, limit };
|
|
34
|
+
}
|
|
35
|
+
/** Create a company (a new tenant) — gated on `identity:company:create`. */
|
|
36
|
+
async createCompany(body) {
|
|
37
|
+
const id = await this.companies.createCompany(body?.name ?? 'Untitled');
|
|
38
|
+
return { id };
|
|
39
|
+
}
|
|
40
|
+
/** Read one company — gated on `identity:company:read`. */
|
|
41
|
+
getCompany(uuid) {
|
|
42
|
+
return this.companies.getCompany(uuid);
|
|
43
|
+
}
|
|
44
|
+
/** Update a company — gated on `identity:company:update`. */
|
|
45
|
+
async updateCompany(uuid, body) {
|
|
46
|
+
await this.companies.updateCompany(uuid, body ?? {});
|
|
47
|
+
return { success: true };
|
|
48
|
+
}
|
|
49
|
+
/** Delete a company — gated on `identity:company:delete`. */
|
|
50
|
+
async deleteCompany(uuid) {
|
|
51
|
+
await this.companies.deleteCompany(uuid);
|
|
52
|
+
return { success: true };
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
exports.CompanyController = CompanyController;
|
|
56
|
+
__decorate([
|
|
57
|
+
(0, common_1.Get)('companies'),
|
|
58
|
+
(0, guard_1.Classified)({ typeKey: 'company', action: 'read' }),
|
|
59
|
+
__param(0, (0, common_1.Query)()),
|
|
60
|
+
__metadata("design:type", Function),
|
|
61
|
+
__metadata("design:paramtypes", [Object]),
|
|
62
|
+
__metadata("design:returntype", Promise)
|
|
63
|
+
], CompanyController.prototype, "listCompanies", null);
|
|
64
|
+
__decorate([
|
|
65
|
+
(0, common_1.Post)('companies'),
|
|
66
|
+
(0, guard_1.Classified)({ typeKey: 'company', action: 'create' }),
|
|
67
|
+
__param(0, (0, common_1.Body)()),
|
|
68
|
+
__metadata("design:type", Function),
|
|
69
|
+
__metadata("design:paramtypes", [Object]),
|
|
70
|
+
__metadata("design:returntype", Promise)
|
|
71
|
+
], CompanyController.prototype, "createCompany", null);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, common_1.Get)('companies/:uuid'),
|
|
74
|
+
(0, guard_1.Classified)({ typeKey: 'company', action: 'read' }),
|
|
75
|
+
__param(0, (0, common_1.Param)('uuid')),
|
|
76
|
+
__metadata("design:type", Function),
|
|
77
|
+
__metadata("design:paramtypes", [String]),
|
|
78
|
+
__metadata("design:returntype", void 0)
|
|
79
|
+
], CompanyController.prototype, "getCompany", null);
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, common_1.Put)('companies/:uuid'),
|
|
82
|
+
(0, guard_1.Classified)({ typeKey: 'company', action: 'update' }),
|
|
83
|
+
__param(0, (0, common_1.Param)('uuid')),
|
|
84
|
+
__param(1, (0, common_1.Body)()),
|
|
85
|
+
__metadata("design:type", Function),
|
|
86
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
87
|
+
__metadata("design:returntype", Promise)
|
|
88
|
+
], CompanyController.prototype, "updateCompany", null);
|
|
89
|
+
__decorate([
|
|
90
|
+
(0, common_1.Delete)('companies/:uuid'),
|
|
91
|
+
(0, guard_1.Classified)({ typeKey: 'company', action: 'delete' }),
|
|
92
|
+
__param(0, (0, common_1.Param)('uuid')),
|
|
93
|
+
__metadata("design:type", Function),
|
|
94
|
+
__metadata("design:paramtypes", [String]),
|
|
95
|
+
__metadata("design:returntype", Promise)
|
|
96
|
+
], CompanyController.prototype, "deleteCompany", null);
|
|
97
|
+
exports.CompanyController = CompanyController = __decorate([
|
|
98
|
+
(0, common_1.Controller)('identity'),
|
|
99
|
+
__metadata("design:paramtypes", [company_service_js_1.CompanyService])
|
|
100
|
+
], CompanyController);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RecordsService, type RecordId } from '@astram/engine';
|
|
2
|
+
import { type ListParams } from '../shared/list-query.js';
|
|
3
|
+
/**
|
|
4
|
+
* CompanyService — company (tenant) CRUD on the paved road, via the framework's
|
|
5
|
+
* RecordsService facade. The facade supplies the acting identity (the request
|
|
6
|
+
* principal) — this service never threads a Principal. No raw SQL, no RECORD_ENGINE.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CompanyService {
|
|
9
|
+
private readonly records;
|
|
10
|
+
constructor(records: RecordsService);
|
|
11
|
+
/** Create a company (tenant). */
|
|
12
|
+
createCompany(name: string): Promise<RecordId>;
|
|
13
|
+
/** List companies (tenant-scoped by the pipe), with paging/search/sort. */
|
|
14
|
+
listCompanies(params?: ListParams): Promise<import("@astram/engine").RecordPage>;
|
|
15
|
+
/** Read a single company. */
|
|
16
|
+
getCompany(uuid: string): Promise<import("@astram/engine").RecordView | null>;
|
|
17
|
+
/** Update a company. */
|
|
18
|
+
updateCompany(uuid: string, patch: {
|
|
19
|
+
name?: string;
|
|
20
|
+
status?: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
/** Delete a company. */
|
|
23
|
+
deleteCompany(uuid: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=company.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company.service.d.ts","sourceRoot":"","sources":["../../../src/features/company/company.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAK1E;;;;GAIG;AACH,qBACa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD,iCAAiC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI9C,2EAA2E;IAC3E,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU;IAIjC,6BAA6B;IAC7B,UAAU,CAAC,IAAI,EAAE,MAAM;IAIvB,wBAAwB;IACxB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,wBAAwB;IACxB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CompanyService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const engine_1 = require("@astram/engine");
|
|
15
|
+
const list_query_js_1 = require("../shared/list-query.js");
|
|
16
|
+
const COMPANY_SEARCH_FIELDS = ['name'];
|
|
17
|
+
const COMPANY_SORT_FIELDS = ['name', 'status', 'createdAt'];
|
|
18
|
+
/**
|
|
19
|
+
* CompanyService — company (tenant) CRUD on the paved road, via the framework's
|
|
20
|
+
* RecordsService facade. The facade supplies the acting identity (the request
|
|
21
|
+
* principal) — this service never threads a Principal. No raw SQL, no RECORD_ENGINE.
|
|
22
|
+
*/
|
|
23
|
+
let CompanyService = class CompanyService {
|
|
24
|
+
records;
|
|
25
|
+
constructor(records) {
|
|
26
|
+
this.records = records;
|
|
27
|
+
}
|
|
28
|
+
/** Create a company (tenant). */
|
|
29
|
+
createCompany(name) {
|
|
30
|
+
return this.records.create('company', { fields: { name } });
|
|
31
|
+
}
|
|
32
|
+
/** List companies (tenant-scoped by the pipe), with paging/search/sort. */
|
|
33
|
+
listCompanies(params) {
|
|
34
|
+
return this.records.list('company', (0, list_query_js_1.buildListQuery)(params, COMPANY_SEARCH_FIELDS, COMPANY_SORT_FIELDS));
|
|
35
|
+
}
|
|
36
|
+
/** Read a single company. */
|
|
37
|
+
getCompany(uuid) {
|
|
38
|
+
return this.records.get('company', uuid);
|
|
39
|
+
}
|
|
40
|
+
/** Update a company. */
|
|
41
|
+
updateCompany(uuid, patch) {
|
|
42
|
+
return this.records.update('company', uuid, { fields: patch });
|
|
43
|
+
}
|
|
44
|
+
/** Delete a company. */
|
|
45
|
+
deleteCompany(uuid) {
|
|
46
|
+
return this.records.delete('company', uuid);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.CompanyService = CompanyService;
|
|
50
|
+
exports.CompanyService = CompanyService = __decorate([
|
|
51
|
+
(0, common_1.Injectable)(),
|
|
52
|
+
__metadata("design:paramtypes", [engine_1.RecordsService])
|
|
53
|
+
], CompanyService);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Pool } from 'mariadb';
|
|
2
|
+
import type { ObjectTypeInput } from '@astram/engine';
|
|
3
|
+
/** One row in the grantee's pending-offers inbox. */
|
|
4
|
+
export interface InboxGrantRow {
|
|
5
|
+
uuid: string;
|
|
6
|
+
owningTenantName: string;
|
|
7
|
+
granteeKind: 'user' | 'role';
|
|
8
|
+
granteeName: string;
|
|
9
|
+
typeKey: string;
|
|
10
|
+
typeLabel: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
allowedActions: string[];
|
|
13
|
+
offeredByName: string;
|
|
14
|
+
expiresAtMs: number;
|
|
15
|
+
}
|
|
16
|
+
/** One row of a grant MY tenant owns (any status) — the "outgoing" list. */
|
|
17
|
+
export interface MineGrantRow {
|
|
18
|
+
uuid: string;
|
|
19
|
+
granteeTenantName: string;
|
|
20
|
+
granteeKind: 'user' | 'role';
|
|
21
|
+
granteeName: string;
|
|
22
|
+
typeKey: string;
|
|
23
|
+
typeLabel: string;
|
|
24
|
+
scope: string;
|
|
25
|
+
allowedActions: string[];
|
|
26
|
+
status: string;
|
|
27
|
+
offeredByName: string;
|
|
28
|
+
expiresAtMs: number;
|
|
29
|
+
}
|
|
30
|
+
/** One row of a grant accepted INTO my tenant — the "incoming" (active) list. */
|
|
31
|
+
export interface IncomingGrantRow {
|
|
32
|
+
uuid: string;
|
|
33
|
+
owningTenantName: string;
|
|
34
|
+
granteeKind: 'user' | 'role';
|
|
35
|
+
granteeName: string;
|
|
36
|
+
typeKey: string;
|
|
37
|
+
typeLabel: string;
|
|
38
|
+
scope: string;
|
|
39
|
+
allowedActions: string[];
|
|
40
|
+
offeredByName: string;
|
|
41
|
+
expiresAtMs: number;
|
|
42
|
+
}
|
|
43
|
+
/** One offerable object type for the offer form's picker (decision 1: tenantScoped only). */
|
|
44
|
+
export interface OfferableTypeRow {
|
|
45
|
+
typeKey: string;
|
|
46
|
+
label: string;
|
|
47
|
+
actions: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare class CrossTenantGrantReadService {
|
|
50
|
+
private readonly pool;
|
|
51
|
+
private readonly offerableTypeInputs;
|
|
52
|
+
private readonly actionsByType;
|
|
53
|
+
/**
|
|
54
|
+
* @param pool the shared resolver pool.
|
|
55
|
+
* @param offerableTypeInputs the ENGINE registrations to filter to `access.tenantScoped
|
|
56
|
+
* === true` types for the offer form's picker (decision 1). Passed in (not imported
|
|
57
|
+
* directly) so this service stays a plain constructor-arg class like its siblings —
|
|
58
|
+
* the module supplies the same merged `[...identityObjects]` set the share-type
|
|
59
|
+
* surface uses, so a future tenantScoped type is picked up the same way it becomes
|
|
60
|
+
* shareable.
|
|
61
|
+
* @param actionsByType per-typeKey action vocabulary for the offerable-types read
|
|
62
|
+
* (decision 1: "the actions per type come from the type's own action set"). Callers
|
|
63
|
+
* pass `actionsFor`, which defaults every type to the CRUD `IDENTITY_ACTIONS` except
|
|
64
|
+
* a type with its own override (e.g. `cross_tenant_grant` -> share/accept/revoke) —
|
|
65
|
+
* the single source every action-vocabulary consumer uses, so a per-type set can
|
|
66
|
+
* never drift between callers.
|
|
67
|
+
*/
|
|
68
|
+
constructor(pool: Pool, offerableTypeInputs: readonly ObjectTypeInput[], actionsByType: (typeKey: string) => readonly string[]);
|
|
69
|
+
/**
|
|
70
|
+
* Pending offers sitting in the CALLER's tenant inbox: `ctg_grantee_tenant_uuid =
|
|
71
|
+
* tenantId AND ctg_status = 'offered' AND ctg_expires_at_ms > nowMs` (an expired
|
|
72
|
+
* offer is dead — never surfaced as actionable, mirroring the writer's own active-
|
|
73
|
+
* grant filter). Ordered soonest-expiring first (most urgent to act on).
|
|
74
|
+
*/
|
|
75
|
+
listInbox(tenantId: string, nowMs: number): Promise<InboxGrantRow[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Every grant MY tenant OWNS (`ctg_owning_tenant_uuid = tenantId`), ANY status — the
|
|
78
|
+
* "outgoing" list (what I've offered, whether still pending, accepted, or revoked).
|
|
79
|
+
* Ordered most-recently-offered first (`ctg_created_at DESC`) — the natural order
|
|
80
|
+
* for "what have I done lately" on an owner's own outgoing list.
|
|
81
|
+
*/
|
|
82
|
+
listMine(tenantId: string): Promise<MineGrantRow[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Grants ACCEPTED into my tenant (`ctg_grantee_tenant_uuid = tenantId AND ctg_status
|
|
85
|
+
* = 'accepted'`) — the "incoming" (currently-active reach-in) list, distinct from the
|
|
86
|
+
* pending inbox. Includes grants whose expiry has since passed (this reports status as
|
|
87
|
+
* stored — `CrossTenantGrantPort.findActiveGrant` is the ACTUAL enforcement of
|
|
88
|
+
* liveness; this is a read-model list, not a re-implementation of that check), so the
|
|
89
|
+
* UI can show "expired" via `expiresAtMs` if desired. Ordered soonest-expiring first.
|
|
90
|
+
*/
|
|
91
|
+
listIncoming(tenantId: string): Promise<IncomingGrantRow[]>;
|
|
92
|
+
/**
|
|
93
|
+
* The ONE source of truth for "which typeKeys are offerable across tenants" (decision
|
|
94
|
+
* 1: `access.tenantScoped === true`). Reflects the SAME engine registrations the
|
|
95
|
+
* share surface merges (`[...identityObjects]`, passed in by the module) and returns
|
|
96
|
+
* the set of tenantScoped typeKeys. Both the picker read (`listOfferableTypes`) AND the
|
|
97
|
+
* offer-endpoint guard (`isOfferableType`) derive from this — so there is no way for
|
|
98
|
+
* the two to drift (a system type can never be BOTH shown in the picker AND accepted
|
|
99
|
+
* by the offer endpoint, or neither).
|
|
100
|
+
*/
|
|
101
|
+
private offerableTypeKeys;
|
|
102
|
+
/**
|
|
103
|
+
* Is `typeKey` offerable across tenants (i.e. a registered tenantScoped type)? The
|
|
104
|
+
* offer controller calls this BEFORE `writer.offer()` so an un-offerable typeKey (any
|
|
105
|
+
* `system` type — e.g. `company`) is rejected with a 400 before any row is written.
|
|
106
|
+
*
|
|
107
|
+
* This closes the H3 defense-in-depth gap: the writer's `assertValidOffer` only checks
|
|
108
|
+
* `typeKey` is a non-empty string, so without this guard a legit own-tenant admin could
|
|
109
|
+
* mint a junk `offered` grant row for a `system` type. It's inert at evaluation TODAY
|
|
110
|
+
* only because no type is both `system` AND `tenantScoped` (system types have no
|
|
111
|
+
* `trusted:'tenantId'`, so the tenant gate never consults a cross-tenant grant for
|
|
112
|
+
* them) — but that safety is a coincidence, not an explicit rule. This makes it
|
|
113
|
+
* explicit, using the SAME tenantScoped filter as the picker (`offerableTypeKeys`).
|
|
114
|
+
*/
|
|
115
|
+
isOfferableType(typeKey: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* The object-type picker source for the offer form (decision 1): the tenantScoped
|
|
118
|
+
* registered types (from `offerableTypeKeys` — the same source `isOfferableType`
|
|
119
|
+
* uses) paired with each type's action vocabulary. No DB query — pure in-memory
|
|
120
|
+
* reflection, wrapped in a resolved promise so the controller's read-surface shape is
|
|
121
|
+
* uniform. The identity types are all `system` and are excluded by construction —
|
|
122
|
+
* only a tenantScoped type (e.g. `shared_resource`) qualifies.
|
|
123
|
+
*/
|
|
124
|
+
listOfferableTypes(): Promise<OfferableTypeRow[]>;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=cross-tenant-grant-read.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-tenant-grant-read.service.d.ts","sourceRoot":"","sources":["../../../src/features/cross-tenant/cross-tenant-grant-read.service.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA+FD,qBACa,2BAA2B;IAiBpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAlBhC;;;;;;;;;;;;;;OAcG;gBAEgB,IAAI,EAAE,IAAI,EACV,mBAAmB,EAAE,SAAS,eAAe,EAAE,EAC/C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE;IAGxE;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IA0B1E;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyBzD;;;;;;;OAOG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyBjE;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;OAOG;IACG,kBAAkB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAWxD"}
|