@astram/identity 0.1.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 +16 -0
- package/dist/branch.object.d.ts +6 -0
- package/dist/branch.object.d.ts.map +1 -0
- package/dist/branch.object.js +37 -0
- package/dist/company-branch.object.d.ts +11 -0
- package/dist/company-branch.object.d.ts.map +1 -0
- package/dist/company-branch.object.js +42 -0
- package/dist/company-department.object.d.ts +13 -0
- package/dist/company-department.object.d.ts.map +1 -0
- package/dist/company-department.object.js +44 -0
- package/dist/company-membership.object.d.ts +16 -0
- package/dist/company-membership.object.d.ts.map +1 -0
- package/dist/company-membership.object.js +63 -0
- package/dist/company.object.d.ts +10 -0
- package/dist/company.object.d.ts.map +1 -0
- package/dist/company.object.js +64 -0
- package/dist/department.object.d.ts +6 -0
- package/dist/department.object.d.ts.map +1 -0
- package/dist/department.object.js +37 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/membership-posting.object.d.ts +16 -0
- package/dist/membership-posting.object.d.ts.map +1 -0
- package/dist/membership-posting.object.js +76 -0
- package/dist/migrations-dir.d.ts +2 -0
- package/dist/migrations-dir.d.ts.map +1 -0
- package/dist/migrations-dir.js +14 -0
- package/dist/role.object.d.ts +7 -0
- package/dist/role.object.d.ts.map +1 -0
- package/dist/role.object.js +42 -0
- package/dist/schema.d.ts +3 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +5 -0
- package/dist/user.object.d.ts +13 -0
- package/dist/user.object.d.ts.map +1 -0
- package/dist/user.object.js +59 -0
- package/migrations/1710000000001-CreateIdentityOrgModel.cjs +100 -0
- package/migrations/1710000000002-CreateMembershipAndPostings.cjs +111 -0
- package/migrations/1710000000003-CreateIdentityCredential.cjs +58 -0
- package/migrations/1710000000004-CreateSessionTables.cjs +79 -0
- package/migrations/1710000000005-CreateMfaAndTokenSurface.cjs +88 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Astram
|
|
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,16 @@
|
|
|
1
|
+
# @astram/identity
|
|
2
|
+
|
|
3
|
+
`@astram/identity` is the framework-owned home for Astram's identity and tenancy
|
|
4
|
+
model — the user, company, membership, and posting objects that every tenant
|
|
5
|
+
application is built on. It exists as its own package (rather than living inside
|
|
6
|
+
`@astram/engine`) because only a package positioned **above** `@astram/engine` in
|
|
7
|
+
the dependency graph can host `@ObjectType`/`@Field`-decorated classes without
|
|
8
|
+
creating a circular dependency: `engine` defines those decorators, so anything
|
|
9
|
+
that uses them to declare objects must depend on `engine`, not the other way
|
|
10
|
+
around. `@astram/identity` depends on `@astram/engine` (for the object-declaration
|
|
11
|
+
decorators) and on `@astram/guard` / `@astram/core` (for the authentication and
|
|
12
|
+
authorization ports the identity model plugs into). This package is the intended
|
|
13
|
+
destination for the identity model currently declared in consuming applications —
|
|
14
|
+
once relocated, apps declare only role-maps, extra columns, and session
|
|
15
|
+
extensions, while the canonical identity objects, migrations, and resolver live
|
|
16
|
+
here in the framework.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch.object.d.ts","sourceRoot":"","sources":["../src/branch.object.ts"],"names":[],"mappings":"AAGA,iCAAiC;AACjC,qBAOa,MAAM;IAEjB,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.Branch = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/** branches — global catalog. */
|
|
16
|
+
let Branch = class Branch {
|
|
17
|
+
uuid;
|
|
18
|
+
name;
|
|
19
|
+
};
|
|
20
|
+
exports.Branch = Branch;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, engine_1.Field)({ column: 'b_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], Branch.prototype, "uuid", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, engine_1.Field)({ column: 'b_name', type: 'string', required: true }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], Branch.prototype, "name", void 0);
|
|
29
|
+
exports.Branch = Branch = __decorate([
|
|
30
|
+
(0, engine_1.ObjectType)({
|
|
31
|
+
typeKey: 'branch',
|
|
32
|
+
table: 'branches',
|
|
33
|
+
schema: schema_1.SCHEMA,
|
|
34
|
+
access: { plane: 'control', system: true },
|
|
35
|
+
}),
|
|
36
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'b_uuid' })
|
|
37
|
+
], Branch);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* company_branches — the per-company branch catalog junction (the branch dropdown's
|
|
3
|
+
* option source). Same shape/role as `company_department`: listed by `companyUuid`, each
|
|
4
|
+
* `branchUuid` resolved to a name server-side for the editor's company-scoped Branch
|
|
5
|
+
* dropdown.
|
|
6
|
+
*/
|
|
7
|
+
export declare class CompanyBranch {
|
|
8
|
+
companyUuid: string;
|
|
9
|
+
branchUuid: string;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=company-branch.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company-branch.object.d.ts","sourceRoot":"","sources":["../src/company-branch.object.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAOa,aAAa;IAExB,WAAW,EAAG,MAAM,CAAC;IAGrB,UAAU,EAAG,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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.CompanyBranch = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/**
|
|
16
|
+
* company_branches — the per-company branch catalog junction (the branch dropdown's
|
|
17
|
+
* option source). Same shape/role as `company_department`: listed by `companyUuid`, each
|
|
18
|
+
* `branchUuid` resolved to a name server-side for the editor's company-scoped Branch
|
|
19
|
+
* dropdown.
|
|
20
|
+
*/
|
|
21
|
+
let CompanyBranch = class CompanyBranch {
|
|
22
|
+
companyUuid;
|
|
23
|
+
branchUuid;
|
|
24
|
+
};
|
|
25
|
+
exports.CompanyBranch = CompanyBranch;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, engine_1.Field)({ column: 'cb_company_uuid', type: 'uuid', required: true }),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], CompanyBranch.prototype, "companyUuid", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, engine_1.Field)({ column: 'cb_branch_uuid', type: 'uuid', required: true }),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], CompanyBranch.prototype, "branchUuid", void 0);
|
|
34
|
+
exports.CompanyBranch = CompanyBranch = __decorate([
|
|
35
|
+
(0, engine_1.ObjectType)({
|
|
36
|
+
typeKey: 'company_branch',
|
|
37
|
+
table: 'company_branches',
|
|
38
|
+
schema: schema_1.SCHEMA,
|
|
39
|
+
access: { plane: 'control', system: true },
|
|
40
|
+
}),
|
|
41
|
+
(0, engine_1.PrimaryKey)({ kind: 'composite', columns: ['cb_company_uuid', 'cb_branch_uuid'] })
|
|
42
|
+
], CompanyBranch);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* company_departments — the per-company department catalog junction (which departments a
|
|
3
|
+
* company offers). Registered (Phase 5, Task 14 seam) so the Assignments editor's
|
|
4
|
+
* company-scoped Department dropdown can be read THROUGH THE PIPE: list this junction by
|
|
5
|
+
* `companyUuid`, then resolve each `departmentUuid` → a display name via `engine.get`
|
|
6
|
+
* (never a raw uuid label). A composite-PK, read-only-through-the-engine catalog; the
|
|
7
|
+
* service only lists it — no create/update from this feature.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CompanyDepartment {
|
|
10
|
+
companyUuid: string;
|
|
11
|
+
departmentUuid: string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=company-department.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company-department.object.d.ts","sourceRoot":"","sources":["../src/company-department.object.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,qBAOa,iBAAiB;IAE5B,WAAW,EAAG,MAAM,CAAC;IAGrB,cAAc,EAAG,MAAM,CAAC;CACzB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
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.CompanyDepartment = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/**
|
|
16
|
+
* company_departments — the per-company department catalog junction (which departments a
|
|
17
|
+
* company offers). Registered (Phase 5, Task 14 seam) so the Assignments editor's
|
|
18
|
+
* company-scoped Department dropdown can be read THROUGH THE PIPE: list this junction by
|
|
19
|
+
* `companyUuid`, then resolve each `departmentUuid` → a display name via `engine.get`
|
|
20
|
+
* (never a raw uuid label). A composite-PK, read-only-through-the-engine catalog; the
|
|
21
|
+
* service only lists it — no create/update from this feature.
|
|
22
|
+
*/
|
|
23
|
+
let CompanyDepartment = class CompanyDepartment {
|
|
24
|
+
companyUuid;
|
|
25
|
+
departmentUuid;
|
|
26
|
+
};
|
|
27
|
+
exports.CompanyDepartment = CompanyDepartment;
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, engine_1.Field)({ column: 'cd_company_uuid', type: 'uuid', required: true }),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], CompanyDepartment.prototype, "companyUuid", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, engine_1.Field)({ column: 'cd_department_uuid', type: 'uuid', required: true }),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], CompanyDepartment.prototype, "departmentUuid", void 0);
|
|
36
|
+
exports.CompanyDepartment = CompanyDepartment = __decorate([
|
|
37
|
+
(0, engine_1.ObjectType)({
|
|
38
|
+
typeKey: 'company_department',
|
|
39
|
+
table: 'company_departments',
|
|
40
|
+
schema: schema_1.SCHEMA,
|
|
41
|
+
access: { plane: 'control', system: true },
|
|
42
|
+
}),
|
|
43
|
+
(0, engine_1.PrimaryKey)({ kind: 'composite', columns: ['cd_company_uuid', 'cd_department_uuid'] })
|
|
44
|
+
], CompanyDepartment);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* company_membership — the first-class membership card: a `(user, company)` scope with
|
|
3
|
+
* `is_active` (exactly one active per user, enforced service-side) + `status`. Scoping
|
|
4
|
+
* is via the membership, not a tenant column; `createdBy` is server-derived (actorId-
|
|
5
|
+
* trusted, anti-forgery). Registered here (Phase 2, Task 4) so the membership service
|
|
6
|
+
* (Phase 3) CRUDs it through the pipe.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CompanyMembership {
|
|
9
|
+
uuid: string;
|
|
10
|
+
userUuid: string;
|
|
11
|
+
companyUuid: string;
|
|
12
|
+
isActive: boolean;
|
|
13
|
+
status: string;
|
|
14
|
+
createdBy: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=company-membership.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company-membership.object.d.ts","sourceRoot":"","sources":["../src/company-membership.object.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,qBAOa,iBAAiB;IAE5B,IAAI,EAAG,MAAM,CAAC;IAGd,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAGrB,QAAQ,EAAG,OAAO,CAAC;IAGnB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
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.CompanyMembership = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/**
|
|
16
|
+
* company_membership — the first-class membership card: a `(user, company)` scope with
|
|
17
|
+
* `is_active` (exactly one active per user, enforced service-side) + `status`. Scoping
|
|
18
|
+
* is via the membership, not a tenant column; `createdBy` is server-derived (actorId-
|
|
19
|
+
* trusted, anti-forgery). Registered here (Phase 2, Task 4) so the membership service
|
|
20
|
+
* (Phase 3) CRUDs it through the pipe.
|
|
21
|
+
*/
|
|
22
|
+
let CompanyMembership = class CompanyMembership {
|
|
23
|
+
uuid;
|
|
24
|
+
userUuid;
|
|
25
|
+
companyUuid;
|
|
26
|
+
isActive;
|
|
27
|
+
status;
|
|
28
|
+
createdBy;
|
|
29
|
+
};
|
|
30
|
+
exports.CompanyMembership = CompanyMembership;
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, engine_1.Field)({ column: 'cm_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
33
|
+
__metadata("design:type", String)
|
|
34
|
+
], CompanyMembership.prototype, "uuid", void 0);
|
|
35
|
+
__decorate([
|
|
36
|
+
(0, engine_1.Field)({ column: 'cm_user_uuid', type: 'uuid', required: true }),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], CompanyMembership.prototype, "userUuid", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, engine_1.Field)({ column: 'cm_company_uuid', type: 'uuid', required: true }),
|
|
41
|
+
__metadata("design:type", String)
|
|
42
|
+
], CompanyMembership.prototype, "companyUuid", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
(0, engine_1.Field)({ column: 'cm_is_active', type: 'boolean', role: 'active' }),
|
|
45
|
+
__metadata("design:type", Boolean)
|
|
46
|
+
], CompanyMembership.prototype, "isActive", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, engine_1.Field)({ column: 'cm_status', type: 'string', role: 'status' }),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], CompanyMembership.prototype, "status", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, engine_1.Field)({ column: 'cm_created_by', type: 'uuid', trusted: 'actorId' }),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], CompanyMembership.prototype, "createdBy", void 0);
|
|
55
|
+
exports.CompanyMembership = CompanyMembership = __decorate([
|
|
56
|
+
(0, engine_1.ObjectType)({
|
|
57
|
+
typeKey: 'company_membership',
|
|
58
|
+
table: 'company_membership',
|
|
59
|
+
schema: schema_1.SCHEMA,
|
|
60
|
+
access: { plane: 'data', system: true },
|
|
61
|
+
}),
|
|
62
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'cm_uuid' })
|
|
63
|
+
], CompanyMembership);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** companies — the tenant. A company's own tenant boundary is itself (c_uuid). */
|
|
2
|
+
export declare class Company {
|
|
3
|
+
uuid: string;
|
|
4
|
+
name: string;
|
|
5
|
+
parentUuid: string;
|
|
6
|
+
type: string;
|
|
7
|
+
status: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=company.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"company.object.d.ts","sourceRoot":"","sources":["../src/company.object.ts"],"names":[],"mappings":"AAGA,kFAAkF;AAClF,qBAca,OAAO;IAElB,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAGd,UAAU,EAAG,MAAM,CAAC;IAGpB,IAAI,EAAG,MAAM,CAAC;IAGd,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
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.Company = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/** companies — the tenant. A company's own tenant boundary is itself (c_uuid). */
|
|
16
|
+
let Company = class Company {
|
|
17
|
+
uuid;
|
|
18
|
+
name;
|
|
19
|
+
parentUuid;
|
|
20
|
+
type;
|
|
21
|
+
status;
|
|
22
|
+
createdAt;
|
|
23
|
+
};
|
|
24
|
+
exports.Company = Company;
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, engine_1.Field)({ column: 'c_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], Company.prototype, "uuid", void 0);
|
|
29
|
+
__decorate([
|
|
30
|
+
(0, engine_1.Field)({ column: 'c_name', type: 'string', required: true }),
|
|
31
|
+
__metadata("design:type", String)
|
|
32
|
+
], Company.prototype, "name", void 0);
|
|
33
|
+
__decorate([
|
|
34
|
+
(0, engine_1.Field)({ column: 'c_parent_uuid', type: 'uuid' }),
|
|
35
|
+
__metadata("design:type", String)
|
|
36
|
+
], Company.prototype, "parentUuid", void 0);
|
|
37
|
+
__decorate([
|
|
38
|
+
(0, engine_1.Field)({ column: 'c_type', type: 'string' }),
|
|
39
|
+
__metadata("design:type", String)
|
|
40
|
+
], Company.prototype, "type", void 0);
|
|
41
|
+
__decorate([
|
|
42
|
+
(0, engine_1.Field)({ column: 'c_status', type: 'string', role: 'status' }),
|
|
43
|
+
__metadata("design:type", String)
|
|
44
|
+
], Company.prototype, "status", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, engine_1.Field)({ column: 'c_created_at', type: 'timestamp' }),
|
|
47
|
+
__metadata("design:type", String)
|
|
48
|
+
], Company.prototype, "createdAt", void 0);
|
|
49
|
+
exports.Company = Company = __decorate([
|
|
50
|
+
(0, engine_1.ObjectType)({
|
|
51
|
+
typeKey: 'company',
|
|
52
|
+
table: 'companies',
|
|
53
|
+
schema: schema_1.SCHEMA,
|
|
54
|
+
// plane:'control' — a company IS a tenant (the org record: name/type/status/parent), and
|
|
55
|
+
// managing tenants (onboarding/renaming/deactivating an org) is PLATFORM ADMINISTRATION —
|
|
56
|
+
// creating/onboarding an organization is an operator action — so the capped root, the
|
|
57
|
+
// platform administrator, may do it. This is NOT a data-plane leak: a tenant's BUSINESS CONTENT lives on separate
|
|
58
|
+
// `tenantScoped` types (e.g. note) that stay plane:'data' and stay walled by tenantId; the
|
|
59
|
+
// `company` row carries no business data. Consistent with company_department/company_branch,
|
|
60
|
+
// which are already control (managing a company's org structure is administration too).
|
|
61
|
+
access: { plane: 'control', system: true },
|
|
62
|
+
}),
|
|
63
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'c_uuid' })
|
|
64
|
+
], Company);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"department.object.d.ts","sourceRoot":"","sources":["../src/department.object.ts"],"names":[],"mappings":"AAGA,oCAAoC;AACpC,qBAOa,UAAU;IAErB,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.Department = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/** departments — global catalog. */
|
|
16
|
+
let Department = class Department {
|
|
17
|
+
uuid;
|
|
18
|
+
name;
|
|
19
|
+
};
|
|
20
|
+
exports.Department = Department;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, engine_1.Field)({ column: 'd_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], Department.prototype, "uuid", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, engine_1.Field)({ column: 'd_name', type: 'string', required: true }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], Department.prototype, "name", void 0);
|
|
29
|
+
exports.Department = Department = __decorate([
|
|
30
|
+
(0, engine_1.ObjectType)({
|
|
31
|
+
typeKey: 'department',
|
|
32
|
+
table: 'departments',
|
|
33
|
+
schema: schema_1.SCHEMA,
|
|
34
|
+
access: { plane: 'control', system: true },
|
|
35
|
+
}),
|
|
36
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'd_uuid' })
|
|
37
|
+
], Department);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @astram/identity — the framework-owned identity + tenancy model.
|
|
3
|
+
* The 9 identity object classes (relocated from the consumer app in Sub-plan B) +
|
|
4
|
+
* the `identityObjects` array the engine registers. Physical names unchanged from the
|
|
5
|
+
* app's originals (rename is a later sub-plan).
|
|
6
|
+
*/
|
|
7
|
+
import type { ObjectTypeInput } from '@astram/engine';
|
|
8
|
+
export { Company } from './company.object.js';
|
|
9
|
+
export { User } from './user.object.js';
|
|
10
|
+
export { Role } from './role.object.js';
|
|
11
|
+
export { Department } from './department.object.js';
|
|
12
|
+
export { Branch } from './branch.object.js';
|
|
13
|
+
export { CompanyMembership } from './company-membership.object.js';
|
|
14
|
+
export { MembershipPosting } from './membership-posting.object.js';
|
|
15
|
+
export { CompanyDepartment } from './company-department.object.js';
|
|
16
|
+
export { CompanyBranch } from './company-branch.object.js';
|
|
17
|
+
export { identityMigrationsDir } from './migrations-dir.js';
|
|
18
|
+
/** All identity objects, registered with the engine at boot (same set + order as before). */
|
|
19
|
+
export declare const identityObjects: readonly ObjectTypeInput[];
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAWtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,6FAA6F;AAC7F,eAAO,MAAM,eAAe,EAAE,SAAS,eAAe,EAGrD,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.identityObjects = exports.identityMigrationsDir = exports.CompanyBranch = exports.CompanyDepartment = exports.MembershipPosting = exports.CompanyMembership = exports.Branch = exports.Department = exports.Role = exports.User = exports.Company = void 0;
|
|
4
|
+
const company_object_js_1 = require("./company.object.js");
|
|
5
|
+
const user_object_js_1 = require("./user.object.js");
|
|
6
|
+
const role_object_js_1 = require("./role.object.js");
|
|
7
|
+
const department_object_js_1 = require("./department.object.js");
|
|
8
|
+
const branch_object_js_1 = require("./branch.object.js");
|
|
9
|
+
const company_membership_object_js_1 = require("./company-membership.object.js");
|
|
10
|
+
const membership_posting_object_js_1 = require("./membership-posting.object.js");
|
|
11
|
+
const company_department_object_js_1 = require("./company-department.object.js");
|
|
12
|
+
const company_branch_object_js_1 = require("./company-branch.object.js");
|
|
13
|
+
var company_object_js_2 = require("./company.object.js");
|
|
14
|
+
Object.defineProperty(exports, "Company", { enumerable: true, get: function () { return company_object_js_2.Company; } });
|
|
15
|
+
var user_object_js_2 = require("./user.object.js");
|
|
16
|
+
Object.defineProperty(exports, "User", { enumerable: true, get: function () { return user_object_js_2.User; } });
|
|
17
|
+
var role_object_js_2 = require("./role.object.js");
|
|
18
|
+
Object.defineProperty(exports, "Role", { enumerable: true, get: function () { return role_object_js_2.Role; } });
|
|
19
|
+
var department_object_js_2 = require("./department.object.js");
|
|
20
|
+
Object.defineProperty(exports, "Department", { enumerable: true, get: function () { return department_object_js_2.Department; } });
|
|
21
|
+
var branch_object_js_2 = require("./branch.object.js");
|
|
22
|
+
Object.defineProperty(exports, "Branch", { enumerable: true, get: function () { return branch_object_js_2.Branch; } });
|
|
23
|
+
var company_membership_object_js_2 = require("./company-membership.object.js");
|
|
24
|
+
Object.defineProperty(exports, "CompanyMembership", { enumerable: true, get: function () { return company_membership_object_js_2.CompanyMembership; } });
|
|
25
|
+
var membership_posting_object_js_2 = require("./membership-posting.object.js");
|
|
26
|
+
Object.defineProperty(exports, "MembershipPosting", { enumerable: true, get: function () { return membership_posting_object_js_2.MembershipPosting; } });
|
|
27
|
+
var company_department_object_js_2 = require("./company-department.object.js");
|
|
28
|
+
Object.defineProperty(exports, "CompanyDepartment", { enumerable: true, get: function () { return company_department_object_js_2.CompanyDepartment; } });
|
|
29
|
+
var company_branch_object_js_2 = require("./company-branch.object.js");
|
|
30
|
+
Object.defineProperty(exports, "CompanyBranch", { enumerable: true, get: function () { return company_branch_object_js_2.CompanyBranch; } });
|
|
31
|
+
var migrations_dir_js_1 = require("./migrations-dir.js");
|
|
32
|
+
Object.defineProperty(exports, "identityMigrationsDir", { enumerable: true, get: function () { return migrations_dir_js_1.identityMigrationsDir; } });
|
|
33
|
+
/** All identity objects, registered with the engine at boot (same set + order as before). */
|
|
34
|
+
exports.identityObjects = [
|
|
35
|
+
company_object_js_1.Company, user_object_js_1.User, role_object_js_1.Role, department_object_js_1.Department, branch_object_js_1.Branch,
|
|
36
|
+
company_membership_object_js_1.CompanyMembership, membership_posting_object_js_1.MembershipPosting, company_department_object_js_1.CompanyDepartment, company_branch_object_js_1.CompanyBranch,
|
|
37
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* membership_posting — the bundled, time-bounded assignment on a membership: a role
|
|
3
|
+
* (+ optional department + optional branch) with an optional effective window. Postings
|
|
4
|
+
* are replace-set synced per membership (Phase 3). `createdBy` is actorId-trusted.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MembershipPosting {
|
|
7
|
+
uuid: string;
|
|
8
|
+
membershipUuid: string;
|
|
9
|
+
roleUuid: string;
|
|
10
|
+
departmentUuid: string;
|
|
11
|
+
branchUuid: string;
|
|
12
|
+
effectiveFrom: number;
|
|
13
|
+
effectiveUntil: number;
|
|
14
|
+
createdBy: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=membership-posting.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-posting.object.d.ts","sourceRoot":"","sources":["../src/membership-posting.object.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBAOa,iBAAiB;IAE5B,IAAI,EAAG,MAAM,CAAC;IAGd,cAAc,EAAG,MAAM,CAAC;IAGxB,QAAQ,EAAG,MAAM,CAAC;IAGlB,cAAc,EAAG,MAAM,CAAC;IAGxB,UAAU,EAAG,MAAM,CAAC;IAQpB,aAAa,EAAG,MAAM,CAAC;IAGvB,cAAc,EAAG,MAAM,CAAC;IAGxB,SAAS,EAAG,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
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.MembershipPosting = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/**
|
|
16
|
+
* membership_posting — the bundled, time-bounded assignment on a membership: a role
|
|
17
|
+
* (+ optional department + optional branch) with an optional effective window. Postings
|
|
18
|
+
* are replace-set synced per membership (Phase 3). `createdBy` is actorId-trusted.
|
|
19
|
+
*/
|
|
20
|
+
let MembershipPosting = class MembershipPosting {
|
|
21
|
+
uuid;
|
|
22
|
+
membershipUuid;
|
|
23
|
+
roleUuid;
|
|
24
|
+
departmentUuid;
|
|
25
|
+
branchUuid;
|
|
26
|
+
// Effective window as BIGINT epoch-ms (NOT timestamp): a `number` field binds an INTEGER
|
|
27
|
+
// to the driver, never a `Date`. A `Date` serialized to host-local wall-clock (the driver's
|
|
28
|
+
// `timezone:'Z'` pool) vs a UTC-compared `NOW()` was the fail-OPEN skew — epoch-ms is
|
|
29
|
+
// timezone-immune (mirrors reset-token `prt_expires_at_ms` / grant `ctg_expires_at_ms`).
|
|
30
|
+
// NULL = open-ended (PostingService omits the key so the column stays SQL NULL).
|
|
31
|
+
effectiveFrom;
|
|
32
|
+
effectiveUntil;
|
|
33
|
+
createdBy;
|
|
34
|
+
};
|
|
35
|
+
exports.MembershipPosting = MembershipPosting;
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, engine_1.Field)({ column: 'mp_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
38
|
+
__metadata("design:type", String)
|
|
39
|
+
], MembershipPosting.prototype, "uuid", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, engine_1.Field)({ column: 'mp_membership_uuid', type: 'uuid', required: true }),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], MembershipPosting.prototype, "membershipUuid", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, engine_1.Field)({ column: 'mp_role_uuid', type: 'uuid', required: true }),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], MembershipPosting.prototype, "roleUuid", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, engine_1.Field)({ column: 'mp_department_uuid', type: 'uuid' }),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], MembershipPosting.prototype, "departmentUuid", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
(0, engine_1.Field)({ column: 'mp_branch_uuid', type: 'uuid' }),
|
|
54
|
+
__metadata("design:type", String)
|
|
55
|
+
], MembershipPosting.prototype, "branchUuid", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
(0, engine_1.Field)({ column: 'mp_effective_from_ms', type: 'number', role: 'effectiveFrom' }),
|
|
58
|
+
__metadata("design:type", Number)
|
|
59
|
+
], MembershipPosting.prototype, "effectiveFrom", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
(0, engine_1.Field)({ column: 'mp_effective_until_ms', type: 'number', role: 'effectiveUntil' }),
|
|
62
|
+
__metadata("design:type", Number)
|
|
63
|
+
], MembershipPosting.prototype, "effectiveUntil", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
(0, engine_1.Field)({ column: 'mp_created_by', type: 'uuid', trusted: 'actorId' }),
|
|
66
|
+
__metadata("design:type", String)
|
|
67
|
+
], MembershipPosting.prototype, "createdBy", void 0);
|
|
68
|
+
exports.MembershipPosting = MembershipPosting = __decorate([
|
|
69
|
+
(0, engine_1.ObjectType)({
|
|
70
|
+
typeKey: 'membership_posting',
|
|
71
|
+
table: 'membership_posting',
|
|
72
|
+
schema: schema_1.SCHEMA,
|
|
73
|
+
access: { plane: 'data', system: true },
|
|
74
|
+
}),
|
|
75
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'mp_uuid' })
|
|
76
|
+
], MembershipPosting);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations-dir.d.ts","sourceRoot":"","sources":["../src/migrations-dir.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,EAAE,MAA4C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.identityMigrationsDir = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Absolute path to the framework identity migration files (`packages/identity/migrations`).
|
|
6
|
+
*
|
|
7
|
+
* The consuming app resolves this to MERGE the framework's identity migrations with its
|
|
8
|
+
* own (via `@astram/migrate`'s `loadMigrationsFromDirs`), so it never hardcodes a path
|
|
9
|
+
* into the framework package. Resolved relative to THIS compiled module's directory:
|
|
10
|
+
* `dist/migrations-dir.js` → `../migrations` (the migrations dir sits beside `dist/`,
|
|
11
|
+
* at the package root, and is published via package.json `files`).
|
|
12
|
+
*/
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
exports.identityMigrationsDir = (0, node_path_1.join)(__dirname, '..', 'migrations');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role.object.d.ts","sourceRoot":"","sources":["../src/role.object.ts"],"names":[],"mappings":"AAGA,8BAA8B;AAC9B,qBAOa,IAAI;IAEf,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAGd,SAAS,EAAG,MAAM,CAAC;CACpB"}
|