@nextage/era-lib 1.0.192 → 1.0.194
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/build/enums.d.ts +10 -2
- package/build/enums.d.ts.map +1 -1
- package/build/enums.js +9 -1
- package/build/enums.js.map +1 -1
- package/build/interfaces.d.ts +32 -3
- package/build/interfaces.d.ts.map +1 -1
- package/build/types/base-user/index.d.ts +2 -2
- package/build/types/base-user/index.d.ts.map +1 -1
- package/build/utils/claims.d.ts.map +1 -1
- package/build/utils/claims.interfaces.d.ts +7 -13
- package/build/utils/claims.interfaces.d.ts.map +1 -1
- package/build/utils/claims.js +4 -1
- package/build/utils/claims.js.map +1 -1
- package/build/utils/tenant.d.ts +70 -1
- package/build/utils/tenant.d.ts.map +1 -1
- package/build/utils/tenant.interfaces.d.ts +15 -0
- package/build/utils/tenant.interfaces.d.ts.map +1 -1
- package/build/utils/tenant.js +81 -0
- package/build/utils/tenant.js.map +1 -1
- package/package.json +1 -1
package/build/enums.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Identifiers for security-audit log events (login outcomes, password changes
|
|
2
|
+
* Identifiers for security-audit log events (login outcomes, password changes,
|
|
3
|
+
* cross-organization grants).
|
|
4
|
+
*
|
|
5
|
+
* `orgGrantAdded`/`orgGrantRevoked` are here and not among the CRUD events on
|
|
6
|
+
* purpose: a grant is the only way an account reaches data OUTSIDE the
|
|
7
|
+
* organization it belongs to, so opening or closing one is a security event in
|
|
8
|
+
* the same sense a login is - and belongs to the same trail (ADR-0005 section 3).
|
|
3
9
|
*/
|
|
4
10
|
export declare enum SecurityLogs {
|
|
5
11
|
changePwdFailed = "changePwdFailed",
|
|
@@ -10,7 +16,9 @@ export declare enum SecurityLogs {
|
|
|
10
16
|
loginUserNotActive = "loginUserNotActive",
|
|
11
17
|
loginSuccess = "loginSuccess",
|
|
12
18
|
loginAttempts = "loginAttempts",
|
|
13
|
-
authRequired = "authRequired"
|
|
19
|
+
authRequired = "authRequired",
|
|
20
|
+
orgGrantAdded = "orgGrantAdded",
|
|
21
|
+
orgGrantRevoked = "orgGrantRevoked"
|
|
14
22
|
}
|
|
15
23
|
export declare enum SecurityLogLevel {
|
|
16
24
|
info = "info",
|
package/build/enums.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,oBAAY,YAAY;IACtB,eAAe,oBAAsB;IACrC,gBAAgB,qBAAsB;IACtC,eAAe,oBAAsB;IACrC,eAAe,oBAAsB;IACrC,gBAAgB,qBAAsB;IACtC,kBAAkB,uBAAsB;IACxC,YAAY,iBAAsB;IAClC,aAAa,kBAAsB;IACnC,YAAY,iBAAsB;IAClC,aAAa,kBAAsB;IACnC,eAAe,oBAAsB;CACtC;AAED,oBAAY,gBAAgB;IAC1B,IAAI,SAAY;IAChB,OAAO,SAAS;IAChB,KAAK,UAAY;IACjB,KAAK,UAAY;CAClB;AAED,oBAAY,UAAU;IACpB,IAAI,SAAW;IACf,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,GAAG,WAAc;CAClB;AAED,oBAAY,YAAY;IACtB,cAAc,iBAAkB;IAChC,eAAe,kBAAkB;IACjC,YAAY,gBAAmB;IAC/B,OAAO,YAAoB;CAC5B;AAED;;;;;;;;;;GAUG;AACH,oBAAY,SAAS;IACnB,MAAM,eAAuB;IAC7B,KAAK,UAAmB;IACxB,UAAU,eAAmB;IAC7B,cAAc,mBAAmB;IACjC,WAAW,gBAAmB;IAC9B,UAAU,eAAmB;IAC7B,UAAU,eAAmB;CAC9B;AAED,oBAAY,SAAS;IACnB,GAAG,QAAQ;IACX,GAAG,QAAQ;CACZ;AAED;;;;;;;;;;GAUG;AACH,oBAAY,YAAY;IACtB,QAAQ,aAAa;IACrB,MAAM,WAAa;IACnB,OAAO,YAAa;CACrB;AAED,oBAAY,iBAAiB;IAC3B,OAAO,YAAuB;IAC9B,UAAU,eAAuB;IACjC,eAAe,oBAAuB;IACtC,kBAAkB,uBAAuB;IACzC,QAAQ,aAAuB;CAChC"}
|
package/build/enums.js
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UserCrudEventType = exports.OrgScopeTier = exports.RolesType = exports.BaseRoles = exports.LOGIN_ERRORS = exports.AuthIssuer = exports.SecurityLogLevel = exports.SecurityLogs = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Identifiers for security-audit log events (login outcomes, password changes
|
|
5
|
+
* Identifiers for security-audit log events (login outcomes, password changes,
|
|
6
|
+
* cross-organization grants).
|
|
7
|
+
*
|
|
8
|
+
* `orgGrantAdded`/`orgGrantRevoked` are here and not among the CRUD events on
|
|
9
|
+
* purpose: a grant is the only way an account reaches data OUTSIDE the
|
|
10
|
+
* organization it belongs to, so opening or closing one is a security event in
|
|
11
|
+
* the same sense a login is - and belongs to the same trail (ADR-0005 section 3).
|
|
6
12
|
*/
|
|
7
13
|
var SecurityLogs;
|
|
8
14
|
(function (SecurityLogs) {
|
|
@@ -15,6 +21,8 @@ var SecurityLogs;
|
|
|
15
21
|
SecurityLogs["loginSuccess"] = "loginSuccess";
|
|
16
22
|
SecurityLogs["loginAttempts"] = "loginAttempts";
|
|
17
23
|
SecurityLogs["authRequired"] = "authRequired";
|
|
24
|
+
SecurityLogs["orgGrantAdded"] = "orgGrantAdded";
|
|
25
|
+
SecurityLogs["orgGrantRevoked"] = "orgGrantRevoked";
|
|
18
26
|
})(SecurityLogs || (exports.SecurityLogs = SecurityLogs = {}));
|
|
19
27
|
var SecurityLogLevel;
|
|
20
28
|
(function (SecurityLogLevel) {
|
package/build/enums.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,IAAY,YAYX;AAZD,WAAY,YAAY;IACtB,mDAAqC,CAAA;IACrC,qDAAsC,CAAA;IACtC,mDAAqC,CAAA;IACrC,mDAAqC,CAAA;IACrC,qDAAsC,CAAA;IACtC,yDAAwC,CAAA;IACxC,6CAAkC,CAAA;IAClC,+CAAmC,CAAA;IACnC,6CAAkC,CAAA;IAClC,+CAAmC,CAAA;IACnC,mDAAqC,CAAA;AACvC,CAAC,EAZW,YAAY,4BAAZ,YAAY,QAYvB;AAED,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,iCAAgB,CAAA;IAChB,oCAAgB,CAAA;IAChB,mCAAiB,CAAA;IACjB,mCAAiB,CAAA;AACnB,CAAC,EALW,gBAAgB,gCAAhB,gBAAgB,QAK3B;AAED,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,2BAAe,CAAA;IACf,+BAAiB,CAAA;IACjB,+BAAiB,CAAA;IACjB,4BAAiB,CAAA;AACnB,CAAC,EALW,UAAU,0BAAV,UAAU,QAKrB;AAED,IAAY,YAKX;AALD,WAAY,YAAY;IACtB,+CAAgC,CAAA;IAChC,iDAAiC,CAAA;IACjC,4CAA+B,CAAA;IAC/B,mCAA2B,CAAA;AAC7B,CAAC,EALW,YAAY,4BAAZ,YAAY,QAKvB;AAED;;;;;;;;;;GAUG;AACH,IAAY,SAQX;AARD,WAAY,SAAS;IACnB,kCAA6B,CAAA;IAC7B,4BAAwB,CAAA;IACxB,sCAA6B,CAAA;IAC7B,8CAAiC,CAAA;IACjC,wCAA8B,CAAA;IAC9B,sCAA6B,CAAA;IAC7B,sCAA6B,CAAA;AAC/B,CAAC,EARW,SAAS,yBAAT,SAAS,QAQpB;AAED,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,wBAAW,CAAA;IACX,wBAAW,CAAA;AACb,CAAC,EAHW,SAAS,yBAAT,SAAS,QAGpB;AAED;;;;;;;;;;GAUG;AACH,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,qCAAqB,CAAA;IACrB,iCAAmB,CAAA;IACnB,mCAAoB,CAAA;AACtB,CAAC,EAJW,YAAY,4BAAZ,YAAY,QAIvB;AAED,IAAY,iBAMX;AAND,WAAY,iBAAiB;IAC3B,wCAA8B,CAAA;IAC9B,8CAAiC,CAAA;IACjC,wDAAsC,CAAA;IACtC,8DAAyC,CAAA;IACzC,0CAA+B,CAAA;AACjC,CAAC,EANW,iBAAiB,iCAAjB,iBAAiB,QAM5B"}
|
package/build/interfaces.d.ts
CHANGED
|
@@ -38,12 +38,41 @@ export interface BaseEntity extends BaseDBItem {
|
|
|
38
38
|
* meaningless. The same user may therefore be ORG_MANAGER on one organization
|
|
39
39
|
* and ORG_EDITOR on another.
|
|
40
40
|
*
|
|
41
|
-
* `id
|
|
42
|
-
*
|
|
41
|
+
* The pair `{ id, role }` is ALL of authorization: `resolveOrgScope` and
|
|
42
|
+
* `reachableOrgIds` read nothing else, and it is verbatim what travels in the
|
|
43
|
+
* `grn` claim. It is NOT denormalized: `code`/`name` used to be carried here "to
|
|
44
|
+
* avoid a join on every authorization check", but no check has ever read them -
|
|
45
|
+
* they were display data on the authorization path, and the only effect of
|
|
46
|
+
* keeping them was a second copy of the organization registry to keep in sync.
|
|
47
|
+
* The UI resolves the name from `organization`, the source of truth.
|
|
43
48
|
*/
|
|
44
|
-
export interface OrgGrant
|
|
49
|
+
export interface OrgGrant {
|
|
50
|
+
id: string;
|
|
45
51
|
role: BaseRoles;
|
|
46
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A grant AS PERSISTED on `users.grants[]`.
|
|
55
|
+
*
|
|
56
|
+
* Splits provenance from authorization on purpose: `grantedBy`/`grantedAt` answer
|
|
57
|
+
* "who opened this door, and when" - an audit question - and are read only when
|
|
58
|
+
* the grant is displayed or reviewed. Keeping them off `OrgGrant` is what stops
|
|
59
|
+
* them from ending up in the access token, which is repeated on every request and
|
|
60
|
+
* whose contents cannot be revoked before expiry.
|
|
61
|
+
*
|
|
62
|
+
* Both are OPTIONAL, and not out of laziness: this is the type of `BaseUser.grants`,
|
|
63
|
+
* and on a resource service that user is rebuilt from the TOKEN, which carries
|
|
64
|
+
* `{ id, role }` and nothing else. Provenance is genuinely absent there, so a
|
|
65
|
+
* required field would be a promise the type could not keep. It is written in full
|
|
66
|
+
* by the single write path (`OrganizationCtrl.grantOrgRole`) and by nothing else.
|
|
67
|
+
*
|
|
68
|
+
* `grantedBy` is the granter's user ID and nothing else: the display name is
|
|
69
|
+
* resolved from `users` when shown, so no personal datum is copied into a
|
|
70
|
+
* different user's document.
|
|
71
|
+
*/
|
|
72
|
+
export interface OrgGrantRecord extends OrgGrant {
|
|
73
|
+
grantedBy?: string;
|
|
74
|
+
grantedAt?: Date;
|
|
75
|
+
}
|
|
47
76
|
/**
|
|
48
77
|
* Base shape for a versioned MongoDB entity whose `name`/`description` are
|
|
49
78
|
* internationalised (per-language map or plain string).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAc,SAAS,CAAC;AAEjF,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,IAAI,CAAA;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAI,MAAM,CAAC;IACjB,MAAM,EAAI,MAAM,CAAC;IACjB,MAAM,EAAI,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAI,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,OAAO,EAAM,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C,IAAI,CAAC,EAAK,MAAM,CAAC;IACjB,IAAI,CAAC,EAAK,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAc,SAAS,CAAC;AAEjF,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,IAAI,CAAA;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAI,MAAM,CAAC;IACjB,MAAM,EAAI,MAAM,CAAC;IACjB,MAAM,EAAI,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAI,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,OAAO,EAAM,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C,IAAI,CAAC,EAAK,MAAM,CAAC;IACjB,IAAI,CAAC,EAAK,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAI,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,IAAI,CAAC,EAAS,MAAM,CAAC;IACrB,IAAI,CAAC,EAAS,YAAY,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5C,WAAW,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5C,OAAO,CAAC,EAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAM,MAAM,CAAC;IACnB,IAAI,EAAQ,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAK,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAW,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAa,MAAM,CAAC;IACxB,MAAM,CAAC,EAAU,MAAM,CAAC;IACxB,MAAM,CAAC,EAAU,MAAM,CAAC;IACxB,MAAM,CAAC,EAAU,MAAM,CAAC;IACxB,OAAO,CAAC,EAAS,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAG,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAW,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAQ,GAAG,CAAC;IACpB,OAAO,CAAC,EAAQ,kBAAkB,GAAG,iBAAiB,CAAC;IACvD,SAAS,CAAC,EAAM,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BaseEntity,
|
|
1
|
+
import { BaseEntity, OrgGrantRecord } from '../../interfaces';
|
|
2
2
|
declare module '@nextage/nx-frame-be' {
|
|
3
3
|
interface BaseUser {
|
|
4
4
|
organizations?: BaseEntity[];
|
|
5
|
-
grants?:
|
|
5
|
+
grants?: OrgGrantRecord[];
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/base-user/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/base-user/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAU,QAAQ;QAKhB,aAAa,CAAC,EAAE,UAAU,EAAE,CAAC;QAS7B,MAAM,CAAC,EAAS,cAAc,EAAE,CAAC;KAClC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;+FAiB+F;AAE/F,OAAO,EAAE,QAAQ,EAAE,MAAuB,sBAAsB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;+FAiB+F;AAE/F,OAAO,EAAE,QAAQ,EAAE,MAAuB,sBAAsB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,UAAU,CAcnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,aAAa,CAS5E"}
|
|
@@ -4,29 +4,23 @@
|
|
|
4
4
|
* Kept separate from the logic in `claims.ts` (house-style: types live in a
|
|
5
5
|
* dedicated `*.interfaces.ts`, never mixed with the logic that consumes them).
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRoles } from '../enums';
|
|
8
7
|
import { BaseEntity, OrgGrant } from '../interfaces';
|
|
9
|
-
/**
|
|
10
|
-
* A grant as it travels INSIDE the token.
|
|
11
|
-
*
|
|
12
|
-
* Only `id` and `role` are carried: they are all authorization needs. The
|
|
13
|
-
* denormalized `code`/`name` exist for the UI and are re-read from the database
|
|
14
|
-
* when displayed, so keeping them out bounds the token size as grants grow.
|
|
15
|
-
*/
|
|
16
|
-
export interface GrantClaim {
|
|
17
|
-
id: string;
|
|
18
|
-
role: BaseRoles;
|
|
19
|
-
}
|
|
20
8
|
/**
|
|
21
9
|
* The identity portion of the access-token payload.
|
|
22
10
|
*
|
|
23
11
|
* Deliberately terse: these keys are repeated in every request. `rls` and `orgs`
|
|
24
12
|
* predate this ADR and keep their historic names and shapes.
|
|
13
|
+
*
|
|
14
|
+
* `grn` carries `OrgGrant` VERBATIM, not a token-specific shape: the pair
|
|
15
|
+
* `{ id, role }` is the whole of authorization, so a separate claim type could
|
|
16
|
+
* only ever drift from it. Provenance (`OrgGrantRecord.grantedBy`/`grantedAt`)
|
|
17
|
+
* stays out - it is audit data, and the token is neither the place to repeat it
|
|
18
|
+
* on every request nor a place anything can be revoked from before expiry.
|
|
25
19
|
*/
|
|
26
20
|
export interface AuthClaims {
|
|
27
21
|
rls: string[];
|
|
28
22
|
orgs: string[];
|
|
29
|
-
grn?:
|
|
23
|
+
grn?: OrgGrant[];
|
|
30
24
|
}
|
|
31
25
|
/**
|
|
32
26
|
* The identity rebuilt from a token by a resource service.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/claims.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"claims.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/claims.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAG,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAU,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,MAAM,EAAS,QAAQ,EAAE,CAAC;CAC3B"}
|
package/build/utils/claims.js
CHANGED
|
@@ -30,9 +30,12 @@ exports.identityFromClaims = identityFromClaims;
|
|
|
30
30
|
* @returns the identity claims to sign
|
|
31
31
|
*/
|
|
32
32
|
function buildAuthClaims(user) {
|
|
33
|
+
// rebuilt entry by entry, never spread: `user.grants` is an `OrgGrantRecord[]`
|
|
34
|
+
// read from the database, and spreading it would carry `grantedBy`/`grantedAt`
|
|
35
|
+
// into every request's token. The pair below is the whole of authorization.
|
|
33
36
|
const grants = (user.grants ?? [])
|
|
34
37
|
.filter(g => g.id && g.role)
|
|
35
|
-
.map(g => ({ id: g.id, role: g.role }));
|
|
38
|
+
.map(g => ({ id: String(g.id), role: g.role }));
|
|
36
39
|
return {
|
|
37
40
|
rls: user.roles ?? [],
|
|
38
41
|
orgs: (user.organizations ?? []).map(o => o.id).filter(Boolean),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;+FAiB+F;;AAgB/F,
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/utils/claims.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;+FAiB+F;;AAgB/F,0CAcC;AAYD,gDASC;AA5CD;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,IAAuB;IAErD,+EAA+E;IAC/E,+EAA+E;IAC/E,4EAA4E;IAC5E,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAElD,OAAO;QACL,GAAG,EAAG,IAAI,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAChE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,kBAAkB,CAAC,IAA0B;IAE3D,OAAO;QACL,KAAK,EAAU,IAAI,EAAE,GAAG,IAAI,EAAE;QAC9B,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,MAAM,EAAS,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAiB,EAAE,CAAC,CAAC;KACvD,CAAC;AACJ,CAAC"}
|
package/build/utils/tenant.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* *
|
|
10
10
|
*********************************************************************************************/
|
|
11
11
|
import { BaseUser, NxContext, NxObject } from '@nextage/nx-frame-be';
|
|
12
|
-
import { ScopedReadModel } from './tenant.interfaces';
|
|
12
|
+
import { ExistenceCheckModel, ScopedReadModel } from './tenant.interfaces';
|
|
13
13
|
/**
|
|
14
14
|
* Resolves the organization a request operates on - ALWAYS exactly one.
|
|
15
15
|
*
|
|
@@ -95,4 +95,73 @@ export declare function getTenant(user?: BaseUser): string;
|
|
|
95
95
|
* @throws `AppError('messages.notFound')` when the document is out of scope
|
|
96
96
|
*/
|
|
97
97
|
export declare function assertTenantScope(model: ScopedReadModel, id: string, context?: NxContext): Promise<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Answers whether the organization NAMED by a request actually exists (T49).
|
|
100
|
+
*
|
|
101
|
+
* {@link resolveTenant} answers one question - "may this user REACH the
|
|
102
|
+
* selection" - and for a cross-tenant role the answer is "any of them", so it
|
|
103
|
+
* hands the value back untouched. EXISTENCE was nobody's job: an `orgId` naming
|
|
104
|
+
* no organization, never created or since deleted, travelled through unchecked.
|
|
105
|
+
*
|
|
106
|
+
* On reads that costs nothing: the filter matches no document and the caller gets
|
|
107
|
+
* an empty list. It bites on WRITES, because {@link setTenant} also runs inside
|
|
108
|
+
* `beforeCreate`: the document is stored with a `tenantId` pointing nowhere,
|
|
109
|
+
* unreachable by every later query and invisible to any tenant-wide cascade,
|
|
110
|
+
* which keys off an `OrganizationRemoved` event that will never arrive for an
|
|
111
|
+
* organization that never existed.
|
|
112
|
+
*
|
|
113
|
+
* ### Where to call it
|
|
114
|
+
*
|
|
115
|
+
* In the REQUEST CONTEXT of the service - the one place `orgId` enters, crossed
|
|
116
|
+
* ONCE per request - and NOT inside `resolveTenant`. That helper is synchronous
|
|
117
|
+
* and `setTenant` calls it from `beforeCreate`/`beforeGet`/`updateFilters`, hooks
|
|
118
|
+
* that run on every single query: making it asynchronous would cost a database
|
|
119
|
+
* round trip per query, and a signature change rippling through every caller, to
|
|
120
|
+
* answer a question that only changes when the request does.
|
|
121
|
+
*
|
|
122
|
+
* ⚠️ A service whose `orgId` reaches it only through checked paths does NOT need
|
|
123
|
+
* this: auth-srv, for one, takes the organization from mutation ARGUMENTS and
|
|
124
|
+
* passes it to `assertOrgScope`, which re-reads the document and fails when it is
|
|
125
|
+
* missing - the check there would be dead code. Wire it where the selection
|
|
126
|
+
* enters raw, from a header or from connection params.
|
|
127
|
+
*
|
|
128
|
+
* ### Fail-closed, and mute
|
|
129
|
+
*
|
|
130
|
+
* A non-existent selection is refused with the very same `messages.forbidden` as
|
|
131
|
+
* an out-of-reach one: telling them apart would answer a question the caller is
|
|
132
|
+
* not entitled to ask - whether someone else's organization exists, or has been
|
|
133
|
+
* deleted.
|
|
134
|
+
*
|
|
135
|
+
* A malformed value is refused likewise and deliberately BEFORE the query: the
|
|
136
|
+
* selection is client-supplied, and handing an arbitrary string to mongoose would
|
|
137
|
+
* raise a CastError - a 500 where a denial belongs. Note that `toObjectId` does
|
|
138
|
+
* NOT throw on a bad value, it returns the string unchanged, so the guard cannot
|
|
139
|
+
* be left to it.
|
|
140
|
+
*
|
|
141
|
+
* NO selection means "the caller's own organization" (rule 1) and there is
|
|
142
|
+
* nothing to verify: the check is a no-op, so requests that simply do not select
|
|
143
|
+
* are unaffected.
|
|
144
|
+
*
|
|
145
|
+
* @param model the organization model, or any local projection standing in for it
|
|
146
|
+
* @param orgId the organization named by the request, when any
|
|
147
|
+
* @returns `true` when the selection is usable, `false` when it names nothing
|
|
148
|
+
*/
|
|
149
|
+
export declare function selectedOrgExists(model: ExistenceCheckModel, orgId?: string): Promise<boolean>;
|
|
150
|
+
/**
|
|
151
|
+
* {@link selectedOrgExists} in throwing form, for GraphQL context builders.
|
|
152
|
+
*
|
|
153
|
+
* A rejected context aborts the whole operation, which is exactly what a refusal
|
|
154
|
+
* means here.
|
|
155
|
+
*
|
|
156
|
+
* ⚠️ An Express middleware may NOT be able to use this shape: if the service
|
|
157
|
+
* registers its error handler BEFORE the routes, the error never reaches it, and
|
|
158
|
+
* `AppError` carries its code in `extensions.code` rather than in `.status`, so
|
|
159
|
+
* the handler would answer 500 on an already-written response. There, call
|
|
160
|
+
* {@link selectedOrgExists} and write the refusal directly.
|
|
161
|
+
*
|
|
162
|
+
* @param model the organization model, or any local projection standing in for it
|
|
163
|
+
* @param orgId the organization named by the request, when any
|
|
164
|
+
* @throws `AppError('messages.forbidden')` when the selection names no organization
|
|
165
|
+
*/
|
|
166
|
+
export declare function assertSelectedOrgExists(model: ExistenceCheckModel, orgId?: string): Promise<void>;
|
|
98
167
|
//# sourceMappingURL=tenant.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/utils/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;+FAS+F;AAE/F,OAAO,EAAY,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/utils/tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;+FAS+F;AAE/F,OAAO,EAAY,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAS,qBAAqB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAerE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,SAAc,GAAG,IAAI,CAGvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAMjD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMpH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvG"}
|
|
@@ -16,4 +16,19 @@ import { NxContext } from '@nextage/nx-frame-be';
|
|
|
16
16
|
export interface ScopedReadModel {
|
|
17
17
|
get(id: string, context: NxContext): Promise<unknown>;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* A model of which only "does a document with this `_id` exist" is needed.
|
|
21
|
+
*
|
|
22
|
+
* Structural for the same reason as {@link ScopedReadModel}, and deliberately
|
|
23
|
+
* built on `mgModel.exists` rather than on `get`: the question is whether the
|
|
24
|
+
* document EXISTS AT ALL, so it must NOT go through `beforeGet` and the tenant
|
|
25
|
+
* scoping it applies - a scoped read would answer "no" for an organization that
|
|
26
|
+
* exists but the caller cannot reach, which is a different question with a
|
|
27
|
+
* different answer.
|
|
28
|
+
*/
|
|
29
|
+
export interface ExistenceCheckModel {
|
|
30
|
+
mgModel: {
|
|
31
|
+
exists(filter: Record<string, unknown>): Promise<unknown>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
19
34
|
//# sourceMappingURL=tenant.interfaces.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/tenant.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD"}
|
|
1
|
+
{"version":3,"file":"tenant.interfaces.d.ts","sourceRoot":"","sources":["../../src/utils/tenant.interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE;QACP,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC3D,CAAC;CACH"}
|
package/build/utils/tenant.js
CHANGED
|
@@ -14,7 +14,10 @@ exports.resolveTenant = resolveTenant;
|
|
|
14
14
|
exports.setTenant = setTenant;
|
|
15
15
|
exports.getTenant = getTenant;
|
|
16
16
|
exports.assertTenantScope = assertTenantScope;
|
|
17
|
+
exports.selectedOrgExists = selectedOrgExists;
|
|
18
|
+
exports.assertSelectedOrgExists = assertSelectedOrgExists;
|
|
17
19
|
const nx_frame_be_1 = require("@nextage/nx-frame-be");
|
|
20
|
+
const mongo_utils_1 = require("@nextage/nx-frame-be/build/common/base/mongo-utils");
|
|
18
21
|
const scope_1 = require("./scope");
|
|
19
22
|
/**
|
|
20
23
|
* Resolves the organization a request operates on - ALWAYS exactly one.
|
|
@@ -121,4 +124,82 @@ async function assertTenantScope(model, id, context = {}) {
|
|
|
121
124
|
throw new nx_frame_be_1.AppError('messages.notFound');
|
|
122
125
|
return id;
|
|
123
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Answers whether the organization NAMED by a request actually exists (T49).
|
|
129
|
+
*
|
|
130
|
+
* {@link resolveTenant} answers one question - "may this user REACH the
|
|
131
|
+
* selection" - and for a cross-tenant role the answer is "any of them", so it
|
|
132
|
+
* hands the value back untouched. EXISTENCE was nobody's job: an `orgId` naming
|
|
133
|
+
* no organization, never created or since deleted, travelled through unchecked.
|
|
134
|
+
*
|
|
135
|
+
* On reads that costs nothing: the filter matches no document and the caller gets
|
|
136
|
+
* an empty list. It bites on WRITES, because {@link setTenant} also runs inside
|
|
137
|
+
* `beforeCreate`: the document is stored with a `tenantId` pointing nowhere,
|
|
138
|
+
* unreachable by every later query and invisible to any tenant-wide cascade,
|
|
139
|
+
* which keys off an `OrganizationRemoved` event that will never arrive for an
|
|
140
|
+
* organization that never existed.
|
|
141
|
+
*
|
|
142
|
+
* ### Where to call it
|
|
143
|
+
*
|
|
144
|
+
* In the REQUEST CONTEXT of the service - the one place `orgId` enters, crossed
|
|
145
|
+
* ONCE per request - and NOT inside `resolveTenant`. That helper is synchronous
|
|
146
|
+
* and `setTenant` calls it from `beforeCreate`/`beforeGet`/`updateFilters`, hooks
|
|
147
|
+
* that run on every single query: making it asynchronous would cost a database
|
|
148
|
+
* round trip per query, and a signature change rippling through every caller, to
|
|
149
|
+
* answer a question that only changes when the request does.
|
|
150
|
+
*
|
|
151
|
+
* ⚠️ A service whose `orgId` reaches it only through checked paths does NOT need
|
|
152
|
+
* this: auth-srv, for one, takes the organization from mutation ARGUMENTS and
|
|
153
|
+
* passes it to `assertOrgScope`, which re-reads the document and fails when it is
|
|
154
|
+
* missing - the check there would be dead code. Wire it where the selection
|
|
155
|
+
* enters raw, from a header or from connection params.
|
|
156
|
+
*
|
|
157
|
+
* ### Fail-closed, and mute
|
|
158
|
+
*
|
|
159
|
+
* A non-existent selection is refused with the very same `messages.forbidden` as
|
|
160
|
+
* an out-of-reach one: telling them apart would answer a question the caller is
|
|
161
|
+
* not entitled to ask - whether someone else's organization exists, or has been
|
|
162
|
+
* deleted.
|
|
163
|
+
*
|
|
164
|
+
* A malformed value is refused likewise and deliberately BEFORE the query: the
|
|
165
|
+
* selection is client-supplied, and handing an arbitrary string to mongoose would
|
|
166
|
+
* raise a CastError - a 500 where a denial belongs. Note that `toObjectId` does
|
|
167
|
+
* NOT throw on a bad value, it returns the string unchanged, so the guard cannot
|
|
168
|
+
* be left to it.
|
|
169
|
+
*
|
|
170
|
+
* NO selection means "the caller's own organization" (rule 1) and there is
|
|
171
|
+
* nothing to verify: the check is a no-op, so requests that simply do not select
|
|
172
|
+
* are unaffected.
|
|
173
|
+
*
|
|
174
|
+
* @param model the organization model, or any local projection standing in for it
|
|
175
|
+
* @param orgId the organization named by the request, when any
|
|
176
|
+
* @returns `true` when the selection is usable, `false` when it names nothing
|
|
177
|
+
*/
|
|
178
|
+
async function selectedOrgExists(model, orgId) {
|
|
179
|
+
if (!orgId)
|
|
180
|
+
return true;
|
|
181
|
+
if (!(0, mongo_utils_1.isValidObjectId)(orgId))
|
|
182
|
+
return false;
|
|
183
|
+
return !!await model.mgModel.exists({ _id: (0, mongo_utils_1.toObjectId)(orgId) });
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* {@link selectedOrgExists} in throwing form, for GraphQL context builders.
|
|
187
|
+
*
|
|
188
|
+
* A rejected context aborts the whole operation, which is exactly what a refusal
|
|
189
|
+
* means here.
|
|
190
|
+
*
|
|
191
|
+
* ⚠️ An Express middleware may NOT be able to use this shape: if the service
|
|
192
|
+
* registers its error handler BEFORE the routes, the error never reaches it, and
|
|
193
|
+
* `AppError` carries its code in `extensions.code` rather than in `.status`, so
|
|
194
|
+
* the handler would answer 500 on an already-written response. There, call
|
|
195
|
+
* {@link selectedOrgExists} and write the refusal directly.
|
|
196
|
+
*
|
|
197
|
+
* @param model the organization model, or any local projection standing in for it
|
|
198
|
+
* @param orgId the organization named by the request, when any
|
|
199
|
+
* @throws `AppError('messages.forbidden')` when the selection names no organization
|
|
200
|
+
*/
|
|
201
|
+
async function assertSelectedOrgExists(model, orgId) {
|
|
202
|
+
if (!await selectedOrgExists(model, orgId))
|
|
203
|
+
throw new nx_frame_be_1.AppError('messages.forbidden');
|
|
204
|
+
}
|
|
124
205
|
//# sourceMappingURL=tenant.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/utils/tenant.ts"],"names":[],"mappings":";AAAA;;;;;;;;;+FAS+F;;
|
|
1
|
+
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/utils/tenant.ts"],"names":[],"mappings":";AAAA;;;;;;;;;+FAS+F;;AA0C/F,sCAeC;AAcD,8BAGC;AAaD,8BAMC;AA0BD,8CAMC;AAqDD,8CASC;AAkBD,0DAIC;AA/MD,sDAA+E;AAC/E,oFAA6G;AAE7G,mCAAkE;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,SAAgB,aAAa,CAAC,IAAe,EAAE,KAAc;IAE3D,IAAI,CAAC,KAAK;QACR,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IAEzB,MAAM,SAAS,GAAG,IAAA,uBAAe,EAAC,IAAI,CAAC,CAAC;IAExC,sEAAsE;IACtE,IAAI,CAAC,SAAS;QACZ,OAAO,KAAK,CAAC;IAEf,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC5B,MAAM,IAAI,sBAAQ,CAAC,oBAAoB,CAAC,CAAC;IAE3C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,SAAS,CAAC,IAAc,EAAE,UAAqB,EAAE;IAE/D,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,SAAS,CAAC,IAAe;IAEvC,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM;QAC9B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAE1D,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACI,KAAK,UAAU,iBAAiB,CAAC,KAAsB,EAAE,EAAU,EAAE,UAAqB,EAAE;IAEjG,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC;QAC/B,MAAM,IAAI,sBAAQ,CAAC,mBAAmB,CAAC,CAAC;IAE1C,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACI,KAAK,UAAU,iBAAiB,CAAC,KAA0B,EAAE,KAAc;IAEhF,IAAI,CAAC,KAAK;QACR,OAAO,IAAI,CAAC;IAEd,IAAI,CAAC,IAAA,6BAAe,EAAC,KAAK,CAAC;QACzB,OAAO,KAAK,CAAC;IAEf,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,IAAA,wBAAU,EAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACI,KAAK,UAAU,uBAAuB,CAAC,KAA0B,EAAE,KAAc;IAEtF,IAAI,CAAC,MAAM,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;QACxC,MAAM,IAAI,sBAAQ,CAAC,oBAAoB,CAAC,CAAC;AAC7C,CAAC"}
|