@astram/identity 0.1.1 → 0.2.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.
Files changed (139) hide show
  1. package/dist/auth-facts-keys.d.ts +3 -0
  2. package/dist/auth-facts-keys.d.ts.map +1 -0
  3. package/dist/auth-facts-keys.js +24 -0
  4. package/dist/cross-tenant-grant.object.d.ts +28 -0
  5. package/dist/cross-tenant-grant.object.d.ts.map +1 -0
  6. package/dist/cross-tenant-grant.object.js +64 -0
  7. package/dist/features/access/access-admin.access.d.ts +3 -0
  8. package/dist/features/access/access-admin.access.d.ts.map +1 -0
  9. package/dist/features/access/access-admin.access.js +27 -0
  10. package/dist/features/access/company-admin.access.d.ts +3 -0
  11. package/dist/features/access/company-admin.access.d.ts.map +1 -0
  12. package/dist/features/access/company-admin.access.js +31 -0
  13. package/dist/features/access/index.d.ts +15 -0
  14. package/dist/features/access/index.d.ts.map +1 -0
  15. package/dist/features/access/index.js +26 -0
  16. package/dist/features/access/user-management.access.d.ts +3 -0
  17. package/dist/features/access/user-management.access.d.ts.map +1 -0
  18. package/dist/features/access/user-management.access.js +35 -0
  19. package/dist/features/audit/audit-read.service.d.ts +92 -0
  20. package/dist/features/audit/audit-read.service.d.ts.map +1 -0
  21. package/dist/features/audit/audit-read.service.js +97 -0
  22. package/dist/features/audit/audit.controller.d.ts +35 -0
  23. package/dist/features/audit/audit.controller.d.ts.map +1 -0
  24. package/dist/features/audit/audit.controller.js +73 -0
  25. package/dist/features/audit/directory-label-resolver.d.ts +14 -0
  26. package/dist/features/audit/directory-label-resolver.d.ts.map +1 -0
  27. package/dist/features/audit/directory-label-resolver.js +46 -0
  28. package/dist/features/audit/mariadb-audit-reader.d.ts +46 -0
  29. package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -0
  30. package/dist/features/audit/mariadb-audit-reader.js +116 -0
  31. package/dist/features/authz/access-bundle-reconcile.job.d.ts +35 -0
  32. package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -0
  33. package/dist/features/authz/access-bundle-reconcile.job.js +84 -0
  34. package/dist/features/authz/access-bundle-store.d.ts +65 -0
  35. package/dist/features/authz/access-bundle-store.d.ts.map +1 -0
  36. package/dist/features/authz/access-bundle-store.js +154 -0
  37. package/dist/features/authz/catalog-bootstrap.provider.d.ts +28 -0
  38. package/dist/features/authz/catalog-bootstrap.provider.d.ts.map +1 -0
  39. package/dist/features/authz/catalog-bootstrap.provider.js +85 -0
  40. package/dist/features/authz/catalog-reconcile.job.d.ts +23 -0
  41. package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -0
  42. package/dist/features/authz/catalog-reconcile.job.js +47 -0
  43. package/dist/features/authz/db-permission-facts.adapter.d.ts +60 -0
  44. package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -0
  45. package/dist/features/authz/db-permission-facts.adapter.js +116 -0
  46. package/dist/features/authz/grant-admin.controller.d.ts +96 -0
  47. package/dist/features/authz/grant-admin.controller.d.ts.map +1 -0
  48. package/dist/features/authz/grant-admin.controller.js +209 -0
  49. package/dist/features/authz/grant-admin.service.d.ts +107 -0
  50. package/dist/features/authz/grant-admin.service.d.ts.map +1 -0
  51. package/dist/features/authz/grant-admin.service.js +297 -0
  52. package/dist/features/authz/mariadb-catalog-store.d.ts +16 -0
  53. package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -0
  54. package/dist/features/authz/mariadb-catalog-store.js +44 -0
  55. package/dist/features/authz/permission-version.bumper.d.ts +47 -0
  56. package/dist/features/authz/permission-version.bumper.d.ts.map +1 -0
  57. package/dist/features/authz/permission-version.bumper.js +68 -0
  58. package/dist/features/authz/record-authorization.d.ts +31 -0
  59. package/dist/features/authz/record-authorization.d.ts.map +1 -0
  60. package/dist/features/authz/record-authorization.js +59 -0
  61. package/dist/features/authz/session-facts.reader.d.ts +41 -0
  62. package/dist/features/authz/session-facts.reader.d.ts.map +1 -0
  63. package/dist/features/authz/session-facts.reader.js +27 -0
  64. package/dist/features/company/company.controller.d.ts +37 -0
  65. package/dist/features/company/company.controller.d.ts.map +1 -0
  66. package/dist/features/company/company.controller.js +100 -0
  67. package/dist/features/company/company.service.d.ts +25 -0
  68. package/dist/features/company/company.service.d.ts.map +1 -0
  69. package/dist/features/company/company.service.js +53 -0
  70. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +126 -0
  71. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -0
  72. package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +276 -0
  73. package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts +65 -0
  74. package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts.map +1 -0
  75. package/dist/features/cross-tenant/cross-tenant-grant.controller.js +250 -0
  76. package/dist/features/cross-tenant/grant-authorize.d.ts +42 -0
  77. package/dist/features/cross-tenant/grant-authorize.d.ts.map +1 -0
  78. package/dist/features/cross-tenant/grant-authorize.js +17 -0
  79. package/dist/features/identity-feature.module.d.ts +26 -0
  80. package/dist/features/identity-feature.module.d.ts.map +1 -0
  81. package/dist/features/identity-feature.module.js +247 -0
  82. package/dist/features/membership/membership.controller.d.ts +77 -0
  83. package/dist/features/membership/membership.controller.d.ts.map +1 -0
  84. package/dist/features/membership/membership.controller.js +174 -0
  85. package/dist/features/membership/membership.service.d.ts +73 -0
  86. package/dist/features/membership/membership.service.d.ts.map +1 -0
  87. package/dist/features/membership/membership.service.js +173 -0
  88. package/dist/features/membership/posting-version-bump.d.ts +10 -0
  89. package/dist/features/membership/posting-version-bump.d.ts.map +1 -0
  90. package/dist/features/membership/posting-version-bump.js +4 -0
  91. package/dist/features/membership/posting.service.d.ts +59 -0
  92. package/dist/features/membership/posting.service.d.ts.map +1 -0
  93. package/dist/features/membership/posting.service.js +181 -0
  94. package/dist/features/org-structure/org-catalog.controller.d.ts +14 -0
  95. package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -0
  96. package/dist/features/org-structure/org-catalog.controller.js +62 -0
  97. package/dist/features/org-structure/org-catalog.service.d.ts +19 -0
  98. package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -0
  99. package/dist/features/org-structure/org-catalog.service.js +53 -0
  100. package/dist/features/role/role.controller.d.ts +37 -0
  101. package/dist/features/role/role.controller.d.ts.map +1 -0
  102. package/dist/features/role/role.controller.js +104 -0
  103. package/dist/features/role/role.service.d.ts +24 -0
  104. package/dist/features/role/role.service.d.ts.map +1 -0
  105. package/dist/features/role/role.service.js +53 -0
  106. package/dist/features/shared/http.d.ts +15 -0
  107. package/dist/features/shared/http.d.ts.map +1 -0
  108. package/dist/features/shared/http.js +25 -0
  109. package/dist/features/shared/list-query.d.ts +44 -0
  110. package/dist/features/shared/list-query.d.ts.map +1 -0
  111. package/dist/features/shared/list-query.js +53 -0
  112. package/dist/features/shared/named-option.d.ts +6 -0
  113. package/dist/features/shared/named-option.d.ts.map +1 -0
  114. package/dist/features/shared/named-option.js +2 -0
  115. package/dist/features/shared/record-resolver.d.ts +11 -0
  116. package/dist/features/shared/record-resolver.d.ts.map +1 -0
  117. package/dist/features/shared/record-resolver.js +32 -0
  118. package/dist/features/user/user.controller.d.ts +39 -0
  119. package/dist/features/user/user.controller.d.ts.map +1 -0
  120. package/dist/features/user/user.controller.js +104 -0
  121. package/dist/features/user/user.service.d.ts +26 -0
  122. package/dist/features/user/user.service.d.ts.map +1 -0
  123. package/dist/features/user/user.service.js +53 -0
  124. package/dist/index.d.ts +34 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +66 -33
  127. package/dist/objects.d.ts +13 -0
  128. package/dist/objects.d.ts.map +1 -0
  129. package/dist/objects.js +20 -0
  130. package/dist/shared-resource.object.d.ts +28 -0
  131. package/dist/shared-resource.object.d.ts.map +1 -0
  132. package/dist/shared-resource.object.js +63 -0
  133. package/migrations/1710000000010-CreateAuditLog.cjs +42 -0
  134. package/migrations/1710000000011-CreateCrossTenantGrant.cjs +66 -0
  135. package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +86 -0
  136. package/migrations/1710000000013-CreateSharedResource.cjs +30 -0
  137. package/migrations/1710000000099-SeedDevIdentity.cjs +130 -0
  138. package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +212 -0
  139. package/package.json +4 -3
package/dist/index.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  * the `identityObjects` array the engine registers. Physical names unchanged from the
5
5
  * app's originals (rename is a later sub-plan).
6
6
  */
7
- import type { ObjectTypeInput } from '@astram/engine';
8
7
  export { Company } from './company.object.js';
9
8
  export { User } from './user.object.js';
10
9
  export { Role } from './role.object.js';
@@ -15,6 +14,38 @@ export { MembershipPosting } from './membership-posting.object.js';
15
14
  export { CompanyDepartment } from './company-department.object.js';
16
15
  export { CompanyBranch } from './company-branch.object.js';
17
16
  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[];
17
+ /**
18
+ * All identity objects, registered with the engine at boot (same set + order as before).
19
+ * Defined in the leaf module `./objects.js` (imported by BOTH this barrel and
20
+ * `./auth-facts-keys.ts`) so the auth-facts key projection has no module-init cycle.
21
+ */
22
+ export { identityObjects } from './objects.js';
23
+ /**
24
+ * The framework identity permission-key universe, projected from `identityObjects` via
25
+ * `@astram/guard`'s registry bridge. Pass these into
26
+ * `ShieldModule.forRoot({ auth: true, identityActionKeys, identityControlPlaneKeys })`
27
+ * so guard's auth-facts adapter can expand `identity:*` grants + union root's
28
+ * control-plane keys WITHOUT guard importing this package.
29
+ */
30
+ export { identityAuthActionKeys, identityControlPlaneKeys } from './auth-facts-keys.js';
31
+ /**
32
+ * The framework-owned identity FEATURE module (company/user/role CRUD). The consumer imports
33
+ * `IdentityFeatureModule.forRoot()` in one line alongside `ShieldModule.forRoot(...)` (ADR-0026);
34
+ * `forRoot()` self-wires the engine (`EngineModule.forRoot` over the app's APP_DB_* env), registers
35
+ * `identityObjects`, and mounts the CRUD controllers — no separate engine setup needed.
36
+ */
37
+ export { IdentityFeatureModule } from './features/identity-feature.module.js';
38
+ /**
39
+ * The record-tier authorization wiring: the lean grant-read pool factory + the helper that
40
+ * assembles `RecordAuthorizationInputs` for `ShieldModule.forRoot({ recordAuthorization })`.
41
+ * Once wired, `decide()` is real (the framework's record-tier adapters run over the identity
42
+ * app schema) instead of the deny-everything stub.
43
+ */
44
+ export { createIdentityAuthzPool, buildIdentityRecordAuthorization, } from './features/authz/record-authorization.js';
45
+ /**
46
+ * The ADR-0011 per-request session-facts reader (ALS hot path), re-exported so a consumer
47
+ * that wants to construct it directly (rather than via `buildIdentityRecordAuthorization`)
48
+ * can do so.
49
+ */
50
+ export { AlsSessionFactsReader } from './features/authz/session-facts.reader.js';
20
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,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;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;GAMG;AACH,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAExF;;;;;GAKG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;GAKG;AACH,OAAO,EACL,uBAAuB,EACvB,gCAAgC,GACjC,MAAM,0CAA0C,CAAC;AAElD;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC"}
package/dist/index.js CHANGED
@@ -1,37 +1,70 @@
1
1
  "use strict";
2
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; } });
3
+ exports.AlsSessionFactsReader = exports.buildIdentityRecordAuthorization = exports.createIdentityAuthzPool = exports.IdentityFeatureModule = exports.identityControlPlaneKeys = exports.identityAuthActionKeys = 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
+ /**
5
+ * @astram/identity — the framework-owned identity + tenancy model.
6
+ * The 9 identity object classes (relocated from the consumer app in Sub-plan B) +
7
+ * the `identityObjects` array the engine registers. Physical names unchanged from the
8
+ * app's originals (rename is a later sub-plan).
9
+ */
10
+ var company_object_js_1 = require("./company.object.js");
11
+ Object.defineProperty(exports, "Company", { enumerable: true, get: function () { return company_object_js_1.Company; } });
12
+ var user_object_js_1 = require("./user.object.js");
13
+ Object.defineProperty(exports, "User", { enumerable: true, get: function () { return user_object_js_1.User; } });
14
+ var role_object_js_1 = require("./role.object.js");
15
+ Object.defineProperty(exports, "Role", { enumerable: true, get: function () { return role_object_js_1.Role; } });
16
+ var department_object_js_1 = require("./department.object.js");
17
+ Object.defineProperty(exports, "Department", { enumerable: true, get: function () { return department_object_js_1.Department; } });
18
+ var branch_object_js_1 = require("./branch.object.js");
19
+ Object.defineProperty(exports, "Branch", { enumerable: true, get: function () { return branch_object_js_1.Branch; } });
20
+ var company_membership_object_js_1 = require("./company-membership.object.js");
21
+ Object.defineProperty(exports, "CompanyMembership", { enumerable: true, get: function () { return company_membership_object_js_1.CompanyMembership; } });
22
+ var membership_posting_object_js_1 = require("./membership-posting.object.js");
23
+ Object.defineProperty(exports, "MembershipPosting", { enumerable: true, get: function () { return membership_posting_object_js_1.MembershipPosting; } });
24
+ var company_department_object_js_1 = require("./company-department.object.js");
25
+ Object.defineProperty(exports, "CompanyDepartment", { enumerable: true, get: function () { return company_department_object_js_1.CompanyDepartment; } });
26
+ var company_branch_object_js_1 = require("./company-branch.object.js");
27
+ Object.defineProperty(exports, "CompanyBranch", { enumerable: true, get: function () { return company_branch_object_js_1.CompanyBranch; } });
31
28
  var migrations_dir_js_1 = require("./migrations-dir.js");
32
29
  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
- ];
30
+ /**
31
+ * All identity objects, registered with the engine at boot (same set + order as before).
32
+ * Defined in the leaf module `./objects.js` (imported by BOTH this barrel and
33
+ * `./auth-facts-keys.ts`) so the auth-facts key projection has no module-init cycle.
34
+ */
35
+ var objects_js_1 = require("./objects.js");
36
+ Object.defineProperty(exports, "identityObjects", { enumerable: true, get: function () { return objects_js_1.identityObjects; } });
37
+ /**
38
+ * The framework identity permission-key universe, projected from `identityObjects` via
39
+ * `@astram/guard`'s registry bridge. Pass these into
40
+ * `ShieldModule.forRoot({ auth: true, identityActionKeys, identityControlPlaneKeys })`
41
+ * so guard's auth-facts adapter can expand `identity:*` grants + union root's
42
+ * control-plane keys WITHOUT guard importing this package.
43
+ */
44
+ var auth_facts_keys_js_1 = require("./auth-facts-keys.js");
45
+ Object.defineProperty(exports, "identityAuthActionKeys", { enumerable: true, get: function () { return auth_facts_keys_js_1.identityAuthActionKeys; } });
46
+ Object.defineProperty(exports, "identityControlPlaneKeys", { enumerable: true, get: function () { return auth_facts_keys_js_1.identityControlPlaneKeys; } });
47
+ /**
48
+ * The framework-owned identity FEATURE module (company/user/role CRUD). The consumer imports
49
+ * `IdentityFeatureModule.forRoot()` in one line alongside `ShieldModule.forRoot(...)` (ADR-0026);
50
+ * `forRoot()` self-wires the engine (`EngineModule.forRoot` over the app's APP_DB_* env), registers
51
+ * `identityObjects`, and mounts the CRUD controllers — no separate engine setup needed.
52
+ */
53
+ var identity_feature_module_js_1 = require("./features/identity-feature.module.js");
54
+ Object.defineProperty(exports, "IdentityFeatureModule", { enumerable: true, get: function () { return identity_feature_module_js_1.IdentityFeatureModule; } });
55
+ /**
56
+ * The record-tier authorization wiring: the lean grant-read pool factory + the helper that
57
+ * assembles `RecordAuthorizationInputs` for `ShieldModule.forRoot({ recordAuthorization })`.
58
+ * Once wired, `decide()` is real (the framework's record-tier adapters run over the identity
59
+ * app schema) instead of the deny-everything stub.
60
+ */
61
+ var record_authorization_js_1 = require("./features/authz/record-authorization.js");
62
+ Object.defineProperty(exports, "createIdentityAuthzPool", { enumerable: true, get: function () { return record_authorization_js_1.createIdentityAuthzPool; } });
63
+ Object.defineProperty(exports, "buildIdentityRecordAuthorization", { enumerable: true, get: function () { return record_authorization_js_1.buildIdentityRecordAuthorization; } });
64
+ /**
65
+ * The ADR-0011 per-request session-facts reader (ALS hot path), re-exported so a consumer
66
+ * that wants to construct it directly (rather than via `buildIdentityRecordAuthorization`)
67
+ * can do so.
68
+ */
69
+ var session_facts_reader_js_1 = require("./features/authz/session-facts.reader.js");
70
+ Object.defineProperty(exports, "AlsSessionFactsReader", { enumerable: true, get: function () { return session_facts_reader_js_1.AlsSessionFactsReader; } });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The framework identity object-type set — extracted here (rather than defined in
3
+ * `./index.ts`) so it can be imported WITHOUT a module-init cycle. `./auth-facts-keys.ts`
4
+ * builds the permission-key registry at module-eval time from this array; had it imported
5
+ * `identityObjects` from `./index.ts` (which re-exports `auth-facts-keys.ts`), the array
6
+ * could still be `undefined` when the bridge runs → a throw. Both `./index.ts` and
7
+ * `./auth-facts-keys.ts` import the array from THIS leaf module, which depends only on the
8
+ * object classes — so it is fully initialized before either consumer evaluates.
9
+ */
10
+ import type { ObjectTypeInput } from '@astram/engine';
11
+ /** All identity objects, registered with the engine at boot (same set + order as before). */
12
+ export declare const identityObjects: readonly ObjectTypeInput[];
13
+ //# sourceMappingURL=objects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAatD,6FAA6F;AAC7F,eAAO,MAAM,eAAe,EAAE,SAAS,eAAe,EAIrD,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.identityObjects = 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
+ const cross_tenant_grant_object_js_1 = require("./cross-tenant-grant.object.js");
14
+ const shared_resource_object_js_1 = require("./shared-resource.object.js");
15
+ /** All identity objects, registered with the engine at boot (same set + order as before). */
16
+ exports.identityObjects = [
17
+ 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,
18
+ company_membership_object_js_1.CompanyMembership, membership_posting_object_js_1.MembershipPosting, company_department_object_js_1.CompanyDepartment, company_branch_object_js_1.CompanyBranch,
19
+ cross_tenant_grant_object_js_1.CrossTenantGrant, shared_resource_object_js_1.SharedResource,
20
+ ];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * shared_resource — a MINIMAL, framework-owned `tenantScoped` demo object type.
3
+ *
4
+ * WHY IT EXISTS: it is the first (and currently only) `tenantScoped`/offerable type in the
5
+ * framework. All nine identity types are `system` (non-offerable, grant-governed), so the
6
+ * cross-tenant grant flow would have NOTHING to offer without an offerable resource type.
7
+ * `shared_resource` fills that gap so a cross-tenant grant has a real, shareable target.
8
+ *
9
+ * ACCESS: `{ tenantScoped: true, plane: 'data' }` — tenant business content, not
10
+ * administration. `tenantScoped` (mutually exclusive with `system`) means the engine
11
+ * enforces per-tenant isolation via the server-stamped `sr_tenant_id` column below; the
12
+ * engine's registry REQUIRES exactly one field with `trusted: 'tenantId'` for a
13
+ * tenantScoped type (else boot fails), which `tenantId` satisfies. `plane: 'data'` denies
14
+ * the capped root by construction — correct for a shareable business resource, since the
15
+ * cross-tenant flow reaches it through per-tenant admins, never root.
16
+ *
17
+ * TENANT ISOLATION IS SERVER-STAMPED: `sr_tenant_id` is `trusted: 'tenantId'` — the engine
18
+ * OVERWRITES it from the session's tenant on every write; a client can never forge it.
19
+ *
20
+ * Keep this tiny — its only job is to be an offerable type. Not a real business feature.
21
+ */
22
+ export declare class SharedResource {
23
+ uuid: string;
24
+ /** Server-stamped tenant boundary (engine overwrites from the session; never client). */
25
+ tenantId: string;
26
+ label: string;
27
+ }
28
+ //# sourceMappingURL=shared-resource.object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-resource.object.d.ts","sourceRoot":"","sources":["../src/shared-resource.object.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAOa,cAAc;IAEzB,IAAI,EAAG,MAAM,CAAC;IAEd,yFAAyF;IAEzF,QAAQ,EAAG,MAAM,CAAC;IAGlB,KAAK,EAAG,MAAM,CAAC;CAChB"}
@@ -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.SharedResource = void 0;
13
+ const engine_1 = require("@astram/engine");
14
+ const schema_1 = require("./schema");
15
+ /**
16
+ * shared_resource — a MINIMAL, framework-owned `tenantScoped` demo object type.
17
+ *
18
+ * WHY IT EXISTS: it is the first (and currently only) `tenantScoped`/offerable type in the
19
+ * framework. All nine identity types are `system` (non-offerable, grant-governed), so the
20
+ * cross-tenant grant flow would have NOTHING to offer without an offerable resource type.
21
+ * `shared_resource` fills that gap so a cross-tenant grant has a real, shareable target.
22
+ *
23
+ * ACCESS: `{ tenantScoped: true, plane: 'data' }` — tenant business content, not
24
+ * administration. `tenantScoped` (mutually exclusive with `system`) means the engine
25
+ * enforces per-tenant isolation via the server-stamped `sr_tenant_id` column below; the
26
+ * engine's registry REQUIRES exactly one field with `trusted: 'tenantId'` for a
27
+ * tenantScoped type (else boot fails), which `tenantId` satisfies. `plane: 'data'` denies
28
+ * the capped root by construction — correct for a shareable business resource, since the
29
+ * cross-tenant flow reaches it through per-tenant admins, never root.
30
+ *
31
+ * TENANT ISOLATION IS SERVER-STAMPED: `sr_tenant_id` is `trusted: 'tenantId'` — the engine
32
+ * OVERWRITES it from the session's tenant on every write; a client can never forge it.
33
+ *
34
+ * Keep this tiny — its only job is to be an offerable type. Not a real business feature.
35
+ */
36
+ let SharedResource = class SharedResource {
37
+ uuid;
38
+ /** Server-stamped tenant boundary (engine overwrites from the session; never client). */
39
+ tenantId;
40
+ label;
41
+ };
42
+ exports.SharedResource = SharedResource;
43
+ __decorate([
44
+ (0, engine_1.Field)({ column: 'sr_uuid', type: 'uuid', role: 'primaryKey' }),
45
+ __metadata("design:type", String)
46
+ ], SharedResource.prototype, "uuid", void 0);
47
+ __decorate([
48
+ (0, engine_1.Field)({ column: 'sr_tenant_id', type: 'uuid', trusted: 'tenantId' }),
49
+ __metadata("design:type", String)
50
+ ], SharedResource.prototype, "tenantId", void 0);
51
+ __decorate([
52
+ (0, engine_1.Field)({ column: 'sr_label', type: 'string', required: true }),
53
+ __metadata("design:type", String)
54
+ ], SharedResource.prototype, "label", void 0);
55
+ exports.SharedResource = SharedResource = __decorate([
56
+ (0, engine_1.ObjectType)({
57
+ typeKey: 'shared_resource',
58
+ table: 'shared_resource',
59
+ schema: schema_1.SCHEMA,
60
+ access: { tenantScoped: true, plane: 'data' },
61
+ }),
62
+ (0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'sr_uuid' })
63
+ ], SharedResource);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Framework audit migration — the `audit_log` table (ADR-0014's `shield_audit`
3
+ * security schema), the append-only sink the @astram/engine pipe's mandatory
4
+ * audit stage writes to (via MariaDbAuditSink). No existing framework migration
5
+ * creates `shield_audit` (guard's 1710000000006 creates `shield_authz`, a
6
+ * DIFFERENT schema) — this migration owns both the schema and the table.
7
+ *
8
+ * Columns mirror the MariaDbAuditSink INSERT exactly (al_* prefix): al_uuid,
9
+ * al_type, al_occurred_at_ms, al_actor_id, al_tenant_id, al_action_key,
10
+ * al_resource_json, al_decision_json, al_attributes_json. Append-only by
11
+ * convention — the engine only ever INSERTs; there is no update/delete path
12
+ * in the adapter. Ported from astram3's frozen
13
+ * server/migrations/1720000000002-CreateAuditLog.js. Idempotent.
14
+ */
15
+
16
+ const SCHEMA = 'shield_audit';
17
+
18
+ async function up({ query }) {
19
+ await query(`CREATE SCHEMA IF NOT EXISTS \`${SCHEMA}\` DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
20
+
21
+ await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`audit_log\` (
22
+ al_uuid VARCHAR(36) NOT NULL,
23
+ al_type VARCHAR(64) NOT NULL,
24
+ al_occurred_at_ms BIGINT NOT NULL,
25
+ al_actor_id VARCHAR(64) NOT NULL,
26
+ al_tenant_id VARCHAR(64) NOT NULL,
27
+ al_action_key VARCHAR(64) NULL,
28
+ al_resource_json JSON NULL,
29
+ al_decision_json JSON NULL,
30
+ al_attributes_json JSON NULL,
31
+ PRIMARY KEY (al_uuid),
32
+ INDEX idx_audit_actor (al_actor_id),
33
+ INDEX idx_audit_tenant (al_tenant_id),
34
+ INDEX idx_audit_time (al_occurred_at_ms)
35
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
36
+ }
37
+
38
+ async function down({ query }) {
39
+ await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`audit_log\``);
40
+ }
41
+
42
+ module.exports = { up, down };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Framework authz migration — the `shield_authz.cross_tenant_grant` table (ADR-0009). One row
3
+ * = one explicit, consented, scoped grant that lets a GRANTEE tenant's principal reach into
4
+ * the OWNING tenant's object type (or a single record) for a bounded set of actions, until an
5
+ * expiry. Guard's `CrossTenantGrantWriter` (offer/accept/revoke) and
6
+ * `MariaDbCrossTenantGrantAdapter` (findActiveGrant) READ + WRITE this table via
7
+ * `SHIELD_CROSS_TENANT_GRANT_WRITER` / `SHIELD_CROSS_TENANT_GRANT_PORT` — no existing framework
8
+ * migration created it (a framework-ownership gap, like `audit_log` before it). The schema
9
+ * `shield_authz` already exists (guard's 1710000000006-CreateAuthzSchema.cjs creates it) — this
10
+ * migration does NOT recreate the schema, only the table.
11
+ *
12
+ * • Lifecycle bound (who may offer/accept/revoke) lives in code (CrossTenantGrantWriter); this
13
+ * table is pure storage.
14
+ * • A grant is ACTIVE only when `ctg_status = 'accepted'` AND `ctg_revoked_at IS NULL` AND
15
+ * `ctg_expires_at_ms > <now-epoch-ms>` — exactly what the adapter's findActiveGrant filters on.
16
+ * • ⚠️ `ctg_expires_at_ms` is a BIGINT epoch-ms (NOT a TIMESTAMP): compared to a BOUND integer
17
+ * clock value, so expiry is timezone-immune (mirrors the reset-token store's
18
+ * `prt_expires_at_ms` precedent).
19
+ * • `ctg_allowed_actions` is a JSON array of action-key strings — the hard, non-wildcard,
20
+ * non-empty action ceiling enforced at offer time.
21
+ * • `ctg_offered_by` records the owning-tenant admin who offered (who-may-grant audit trail).
22
+ *
23
+ * Columns + `idx_ctg_lookup` ported from astram3's frozen
24
+ * server/migrations/1720000000040-CreateCrossTenantGrant.js; `idx_ctg_grantee` folded in from
25
+ * astram3's frozen server/migrations/1720000000070-AddCtgGranteeIndex.js (astram3 split the
26
+ * grantee-side index across a later migration — the framework creates the table + both indexes
27
+ * in one). Idempotent (`CREATE TABLE IF NOT EXISTS`). No hard-coded UUIDs. `down()` drops the
28
+ * table. Timestamp 1710000000011 sorts after audit (...010) and before the dev seed (...099).
29
+ */
30
+
31
+ const SCHEMA = 'shield_authz';
32
+ const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
33
+
34
+ async function up({ query }) {
35
+ await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\` (
36
+ ctg_uuid VARCHAR(36) NOT NULL, -- PK (grant uuid)
37
+ ctg_owning_tenant_uuid VARCHAR(36) NOT NULL, -- tenant whose data is reached into / who consents
38
+ ctg_grantee_tenant_uuid VARCHAR(36) NOT NULL, -- tenant reaching in
39
+ ctg_grantee_kind VARCHAR(16) NOT NULL, -- 'user' | 'role' (in the grantee tenant)
40
+ ctg_grantee_uuid VARCHAR(36) NOT NULL, -- the specific user/role uuid
41
+ ctg_type_key VARCHAR(64) NOT NULL, -- object type the grant covers
42
+ ctg_scope VARCHAR(16) NOT NULL, -- 'object_type' | 'record'
43
+ ctg_record_uuid VARCHAR(36) NULL, -- set iff scope='record'
44
+ ctg_allowed_actions JSON NOT NULL, -- hard ceiling: JSON array of action keys (non-wildcard, non-empty)
45
+ ctg_status VARCHAR(16) NOT NULL DEFAULT 'offered', -- 'offered' | 'accepted' | 'revoked' (activates only on 'accepted')
46
+ ctg_offered_by VARCHAR(36) NOT NULL, -- the owning-tenant admin who offered (who-may-grant audit)
47
+ ctg_expires_at_ms BIGINT NOT NULL, -- mandatory FUTURE expiry, epoch-ms (BIGINT — timezone-immune)
48
+ ctg_created_at ${TS},
49
+ ctg_accepted_at TIMESTAMP(0) NULL, -- stamped on accept (audit ts; not security-compared)
50
+ ctg_revoked_at TIMESTAMP(0) NULL, -- stamped on revoke, NULL sentinel = not revoked (either side)
51
+ PRIMARY KEY (ctg_uuid),
52
+ -- The findActiveGrant lookup path: (owning tenant, type, grantee, status) narrows to a
53
+ -- grantee's active grants for an object type fast.
54
+ INDEX idx_ctg_lookup (ctg_owning_tenant_uuid, ctg_type_key, ctg_grantee_uuid, ctg_status),
55
+ -- The grantee-side lookup path: "the offers sitting in MY tenant's inbox" —
56
+ -- WHERE ctg_grantee_tenant_uuid = ? AND ctg_status = 'offered' AND ctg_expires_at_ms > ?.
57
+ -- idx_ctg_lookup is owning-tenant-first and does not serve a grantee-tenant-first scan.
58
+ INDEX idx_ctg_grantee (ctg_grantee_tenant_uuid, ctg_status, ctg_expires_at_ms)
59
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
60
+ }
61
+
62
+ async function down({ query }) {
63
+ await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\``);
64
+ }
65
+
66
+ module.exports = { up, down };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Framework authz migration — grant-authority seed for cross-tenant grants. `cross_tenant_grant`'s
3
+ * three permission keys (`identity:cross_tenant_grant:{share,accept,revoke}` — module key is
4
+ * 'identity', see `@astram/guard`'s `identity/identity-actions.ts`'s `MODULE_KEY`) are the
5
+ * data-driven authority for the offer/accept/revoke lifecycle — `decide()` gates them exactly like
6
+ * every other object-type action, now that `cross_tenant_grant` is a registered object type
7
+ * (`packages/identity/src/cross-tenant-grant.object.ts` + `identityObjects`).
8
+ *
9
+ * WHAT THIS MIGRATION DOES vs WHAT IT DOESN'T:
10
+ * - It DOES NOT insert the 3 rows into `shield_authz.permission` (the catalog). The boot
11
+ * catalog reconciler auto-inserts an ACTIVE catalog row for every action the registry
12
+ * declares — and `cross_tenant_grant` is now registered, so the 3 keys appear in the catalog
13
+ * automatically the first time the app boots after this ships. No manual catalog seed needed.
14
+ * - It DOES grant the 3 new keys to every role that CURRENTLY holds coarse identity-admin
15
+ * authority, so an existing admin role doesn't lose the ability to offer/accept/revoke the
16
+ * moment this ships: a role counts as "identity admin" iff it holds any of
17
+ * `identity:company:update` | `identity:company:delete` | `identity:company:*` |
18
+ * `identity:*` | `identity` (mirrors astram3's deleted `DbGrantAuthorityResolver`'s
19
+ * `COMPANY_ADMIN_KEYS` set, matched by EXACT string — the same wildcards `expandGrantKeys`
20
+ * treats as coarse admin grants). This also ensures the framework's dev seed
21
+ * (`1710000000099-SeedDevIdentity.cjs`, which grants its role `identity:*`) gets the 3 keys
22
+ * automatically, so the seeded admin can offer/accept/revoke out of the box. Resolved by
23
+ * `rp_perm_key` STRING MATCH against `role_permission` — no hardcoded uuids, no assumption
24
+ * about which named roles exist on a given tenant.
25
+ *
26
+ * Idempotent: SELECT-before-INSERT via NOT EXISTS on the natural key (rp_role_uuid, rp_perm_key)
27
+ * — re-running inserts ZERO new rows. `down()` deletes only the 3 keys' grants (by key), leaving
28
+ * every other role_permission row untouched.
29
+ *
30
+ * Ported from astram3's frozen `server/migrations/1720000000080-SeedCrossTenantGrantPerms.js`.
31
+ * Timestamp 1710000000012 sorts strictly after the ctg DDL (...011) and before the dev seed
32
+ * (...099) — no dependency ordering concern (this migration only reads/writes `role_permission`,
33
+ * a table created by guard's 1710000000006-CreateAuthzSchema.cjs, and does not depend on the
34
+ * `cross_tenant_grant` object type's runtime registration — the catalog row for these keys is
35
+ * optional at grant-write time; `role_permission` has no FK to `permission`).
36
+ */
37
+
38
+ const AUTHZ = 'shield_authz';
39
+ const MODULE = 'identity';
40
+ const TYPE_KEY = 'cross_tenant_grant';
41
+ const NEW_ACTIONS = ['share', 'accept', 'revoke'];
42
+ const NEW_KEYS = NEW_ACTIONS.map((a) => `${MODULE}:${TYPE_KEY}:${a}`);
43
+
44
+ /** Coarse identity-admin keys — matched by exact string, mirroring astram3's deleted
45
+ * `DbGrantAuthorityResolver`'s `COMPANY_ADMIN_KEYS`. */
46
+ const OLD_ADMIN_KEYS = [
47
+ 'identity:company:update',
48
+ 'identity:company:delete',
49
+ 'identity:company:*',
50
+ 'identity:*',
51
+ 'identity',
52
+ ];
53
+
54
+ async function up({ query }) {
55
+ // Every DISTINCT role currently holding ANY of the coarse identity-admin keys.
56
+ const oldAdminKeyPlaceholders = OLD_ADMIN_KEYS.map(() => '?').join(', ');
57
+ const adminRoles = await query(
58
+ `SELECT DISTINCT rp_role_uuid AS role_uuid
59
+ FROM \`${AUTHZ}\`.\`role_permission\`
60
+ WHERE rp_perm_key IN (${oldAdminKeyPlaceholders})`,
61
+ [...OLD_ADMIN_KEYS],
62
+ );
63
+
64
+ for (const { role_uuid: roleUuid } of adminRoles) {
65
+ for (const key of NEW_KEYS) {
66
+ await query(
67
+ `INSERT INTO \`${AUTHZ}\`.\`role_permission\` (rp_uuid, rp_role_uuid, rp_perm_key)
68
+ SELECT UUID(), ?, ?
69
+ WHERE NOT EXISTS (
70
+ SELECT 1 FROM \`${AUTHZ}\`.\`role_permission\`
71
+ WHERE rp_role_uuid = ? AND rp_perm_key = ?)`,
72
+ [roleUuid, key, roleUuid, key],
73
+ );
74
+ }
75
+ }
76
+ }
77
+
78
+ async function down({ query }) {
79
+ const keyPlaceholders = NEW_KEYS.map(() => '?').join(', ');
80
+ await query(
81
+ `DELETE FROM \`${AUTHZ}\`.\`role_permission\` WHERE rp_perm_key IN (${keyPlaceholders})`,
82
+ [...NEW_KEYS],
83
+ );
84
+ }
85
+
86
+ module.exports = { up, down };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Framework migration — the `shield_app`.`shared_resource` table.
3
+ *
4
+ * `shared_resource` is the framework's minimal `tenantScoped`/offerable demo object type
5
+ * (see `src/shared-resource.object.ts`): the first offerable resource so cross-tenant
6
+ * grants have something to offer (all nine identity types are `system`/non-offerable).
7
+ *
8
+ * Columns EXACTLY mirror the @ObjectType's declared @Field columns (entity↔migration sync
9
+ * is absolute): sr_uuid (PK), sr_tenant_id (trusted:'tenantId', server-stamped), sr_label.
10
+ * The `shield_app` schema already exists (created by 1710000000001) — this migration owns
11
+ * only the table, not the schema. Idempotent (IF NOT EXISTS); down() drops the table.
12
+ */
13
+
14
+ const SCHEMA = 'shield_app';
15
+
16
+ async function up({ query }) {
17
+ await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`shared_resource\` (
18
+ sr_uuid VARCHAR(36) NOT NULL,
19
+ sr_tenant_id VARCHAR(36) NOT NULL,
20
+ sr_label VARCHAR(255) NOT NULL,
21
+ PRIMARY KEY (sr_uuid),
22
+ INDEX idx_sr_tenant (sr_tenant_id)
23
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
24
+ }
25
+
26
+ async function down({ query }) {
27
+ await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`shared_resource\``);
28
+ }
29
+
30
+ module.exports = { up, down };