@astram/identity 0.11.0 → 0.13.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 (96) hide show
  1. package/dist/cross-tenant-grant.object.d.ts.map +1 -1
  2. package/dist/cross-tenant-grant.object.js +3 -2
  3. package/dist/features/audit/directory-label-resolver.d.ts +4 -1
  4. package/dist/features/audit/directory-label-resolver.d.ts.map +1 -1
  5. package/dist/features/audit/directory-label-resolver.js +8 -5
  6. package/dist/features/audit/mariadb-audit-reader.d.ts +5 -1
  7. package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -1
  8. package/dist/features/audit/mariadb-audit-reader.js +10 -2
  9. package/dist/features/authz/access-bundle-reconcile.job.d.ts +4 -1
  10. package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -1
  11. package/dist/features/authz/access-bundle-reconcile.job.js +7 -4
  12. package/dist/features/authz/access-bundle-store.d.ts +4 -1
  13. package/dist/features/authz/access-bundle-store.d.ts.map +1 -1
  14. package/dist/features/authz/access-bundle-store.js +19 -16
  15. package/dist/features/authz/catalog-reconcile.job.d.ts +3 -1
  16. package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -1
  17. package/dist/features/authz/catalog-reconcile.job.js +3 -2
  18. package/dist/features/authz/db-permission-facts.adapter.d.ts +9 -3
  19. package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -1
  20. package/dist/features/authz/db-permission-facts.adapter.js +22 -16
  21. package/dist/features/authz/grant-admin.service.d.ts +4 -1
  22. package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
  23. package/dist/features/authz/grant-admin.service.js +32 -37
  24. package/dist/features/authz/grant-ceiling.d.ts +6 -1
  25. package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
  26. package/dist/features/authz/grant-ceiling.js +19 -16
  27. package/dist/features/authz/mariadb-catalog-store.d.ts +4 -1
  28. package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -1
  29. package/dist/features/authz/mariadb-catalog-store.js +10 -7
  30. package/dist/features/authz/permission-version.bumper.d.ts +6 -1
  31. package/dist/features/authz/permission-version.bumper.d.ts.map +1 -1
  32. package/dist/features/authz/permission-version.bumper.js +13 -11
  33. package/dist/features/authz/record-authorization.d.ts +16 -2
  34. package/dist/features/authz/record-authorization.d.ts.map +1 -1
  35. package/dist/features/authz/record-authorization.js +29 -5
  36. package/dist/features/authz/role-binding.d.ts +3 -2
  37. package/dist/features/authz/role-binding.d.ts.map +1 -1
  38. package/dist/features/authz/role-binding.js +7 -8
  39. package/dist/features/company/company-hierarchy.service.d.ts +6 -1
  40. package/dist/features/company/company-hierarchy.service.d.ts.map +1 -1
  41. package/dist/features/company/company-hierarchy.service.js +17 -16
  42. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +5 -1
  43. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -1
  44. package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +18 -14
  45. package/dist/features/framework-schemas.token.d.ts +8 -0
  46. package/dist/features/framework-schemas.token.d.ts.map +1 -0
  47. package/dist/features/framework-schemas.token.js +10 -0
  48. package/dist/features/identity-feature.module.d.ts +36 -2
  49. package/dist/features/identity-feature.module.d.ts.map +1 -1
  50. package/dist/features/identity-feature.module.js +142 -34
  51. package/dist/features/membership/membership-checker.d.ts +7 -1
  52. package/dist/features/membership/membership-checker.d.ts.map +1 -1
  53. package/dist/features/membership/membership-checker.js +9 -4
  54. package/dist/features/migration-ledger-check.d.ts +15 -0
  55. package/dist/features/migration-ledger-check.d.ts.map +1 -0
  56. package/dist/features/migration-ledger-check.js +26 -0
  57. package/dist/features/scope/scope-closure.service.d.ts +8 -1
  58. package/dist/features/scope/scope-closure.service.d.ts.map +1 -1
  59. package/dist/features/scope/scope-closure.service.js +22 -19
  60. package/dist/features/scope/scope-subtree.resolver.d.ts +7 -1
  61. package/dist/features/scope/scope-subtree.resolver.d.ts.map +1 -1
  62. package/dist/features/scope/scope-subtree.resolver.js +9 -4
  63. package/dist/features/service-account/lock-service-accounts.d.ts +3 -1
  64. package/dist/features/service-account/lock-service-accounts.d.ts.map +1 -1
  65. package/dist/features/service-account/lock-service-accounts.js +4 -4
  66. package/dist/features/service-account/no-expiry-key-report.d.ts +9 -3
  67. package/dist/features/service-account/no-expiry-key-report.d.ts.map +1 -1
  68. package/dist/features/service-account/no-expiry-key-report.js +17 -10
  69. package/dist/features/service-account/service-account.store.d.ts +8 -1
  70. package/dist/features/service-account/service-account.store.d.ts.map +1 -1
  71. package/dist/features/service-account/service-account.store.js +19 -14
  72. package/dist/features/user/user.service.d.ts +7 -3
  73. package/dist/features/user/user.service.d.ts.map +1 -1
  74. package/dist/features/user/user.service.js +12 -4
  75. package/dist/record-share.object.d.ts.map +1 -1
  76. package/dist/record-share.object.js +3 -2
  77. package/dist/schema.d.ts +15 -2
  78. package/dist/schema.d.ts.map +1 -1
  79. package/dist/schema.js +17 -2
  80. package/migrations/1710000000001-CreateIdentityOrgModel.cjs +14 -4
  81. package/migrations/1710000000002-CreateMembershipAndPostings.cjs +14 -4
  82. package/migrations/1710000000003-CreateIdentityCredential.cjs +14 -4
  83. package/migrations/1710000000004-CreateSessionTables.cjs +14 -4
  84. package/migrations/1710000000005-CreateMfaAndTokenSurface.cjs +14 -4
  85. package/migrations/1710000000010-CreateAuditLog.cjs +14 -4
  86. package/migrations/1710000000011-CreateCrossTenantGrant.cjs +15 -4
  87. package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +15 -4
  88. package/migrations/1710000000013-CreateSharedResource.cjs +14 -4
  89. package/migrations/1710000000018-CreateServiceAccount.cjs +13 -4
  90. package/migrations/1710000000019-AddSessionMustReenroll.cjs +14 -4
  91. package/migrations/1710000000020-CreateEmailChangeToken.cjs +15 -5
  92. package/migrations/1710000000021-AddUsersEmailUniqueIndex.cjs +24 -13
  93. package/migrations/1710000000022-CreateStoredFile.cjs +14 -4
  94. package/migrations/1710000000099-SeedDevIdentity.cjs +14 -6
  95. package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +14 -6
  96. package/package.json +4 -4
@@ -14,8 +14,14 @@
14
14
  */
15
15
  const { Company, User, Role, Department, Branch } = require('@astram/identity');
16
16
  const { columnForRole, reflectObjectType } = require('@astram/engine');
17
+ const { frameworkSchemasForMigration } = require('@astram/core');
18
+
19
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
20
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
21
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
22
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
23
+ const replacesChecksums = ['027818f28b12088392ba5f2194a842ef36815aa34b9df7669683c9cdf24c316f'];
17
24
 
18
- const SCHEMA = 'shield_app';
19
25
  const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
20
26
 
21
27
  // Reflect the @ObjectType/@Field metadata off each decorated class into its registration
@@ -30,7 +36,9 @@ const BranchReg = reflectObjectType(Branch);
30
36
  // Resolve a column by role from a reflected object registration (single source of truth).
31
37
  const col = (reg, role) => columnForRole(reg, role);
32
38
 
33
- async function up({ query }) {
39
+ async function up(ctx) {
40
+ const { query } = ctx;
41
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
34
42
  // companies (the tenant) — hierarchy + type + status.
35
43
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`${CompanyReg.table}\` (
36
44
  ${col(CompanyReg, 'primaryKey')} VARCHAR(36) NOT NULL,
@@ -89,7 +97,9 @@ async function up({ query }) {
89
97
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
90
98
  }
91
99
 
92
- async function down({ query }) {
100
+ async function down(ctx) {
101
+ const { query } = ctx;
102
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
93
103
  // Reverse dependency order — membership tables (1710000000002) drop first via their
94
104
  // own down(); here drop the catalogs + principal + tenant.
95
105
  for (const t of [BranchReg.table, DepartmentReg.table, RoleReg.table, UserReg.table, CompanyReg.table]) {
@@ -97,4 +107,4 @@ async function down({ query }) {
97
107
  }
98
108
  }
99
109
 
100
- module.exports = { up, down };
110
+ module.exports = { up, down, replacesChecksums };
@@ -15,8 +15,14 @@ const {
15
15
  CompanyMembership, MembershipPosting, CompanyDepartment, CompanyBranch,
16
16
  } = require('@astram/identity');
17
17
  const { columnForRole, reflectObjectType } = require('@astram/engine');
18
+ const { frameworkSchemasForMigration } = require('@astram/core');
19
+
20
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
21
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
22
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
23
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
24
+ const replacesChecksums = ['a214c2b29d1b21093851d3644581b17757a78e2341b50c67c7b9b3846c5dfc9e'];
18
25
 
19
- const SCHEMA = 'shield_app';
20
26
  const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
21
27
 
22
28
  // The decorated classes carry NO static `.table` — @ObjectType/@Field metadata is read
@@ -34,7 +40,9 @@ const CompanyDepartmentReg = reflectObjectType(CompanyDepartment);
34
40
  const CompanyBranchReg = reflectObjectType(CompanyBranch);
35
41
  const col = (reg, role) => columnForRole(reg, role);
36
42
 
37
- async function up({ query }) {
43
+ async function up(ctx) {
44
+ const { query } = ctx;
45
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
38
46
  // company_membership — first-class membership + active-tenant pointer.
39
47
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`${CompanyMembershipReg.table}\` (
40
48
  ${col(CompanyMembershipReg, 'primaryKey')} VARCHAR(36) NOT NULL,
@@ -102,10 +110,12 @@ async function up({ query }) {
102
110
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
103
111
  }
104
112
 
105
- async function down({ query }) {
113
+ async function down(ctx) {
114
+ const { query } = ctx;
115
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
106
116
  for (const t of [MembershipPostingReg.table, CompanyBranchReg.table, CompanyDepartmentReg.table, CompanyMembershipReg.table]) {
107
117
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`${t}\``);
108
118
  }
109
119
  }
110
120
 
111
- module.exports = { up, down };
121
+ module.exports = { up, down, replacesChecksums };
@@ -25,9 +25,17 @@
25
25
  * Idempotent (CREATE TABLE IF NOT EXISTS).
26
26
  */
27
27
 
28
- const SCHEMA = 'shield_identity';
28
+ const { frameworkSchemasForMigration } = require('@astram/core');
29
29
 
30
- async function up({ query }) {
30
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
31
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
32
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
33
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
34
+ const replacesChecksums = ['440923cce54f25764ca53fd68d3e18d7069207b22c3f2afb5551469a0279a1aa'];
35
+
36
+ async function up(ctx) {
37
+ const { query } = ctx;
38
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
31
39
  // identity_credential — one row per login principal. Resolved by
32
40
  // ic_identity_hint (username/email) at login. ic_password_hash is the
33
41
  // Argon2id encoded hash the engine verifies against. Whether the principal
@@ -51,8 +59,10 @@ async function up({ query }) {
51
59
  );
52
60
  }
53
61
 
54
- async function down({ query }) {
62
+ async function down(ctx) {
63
+ const { query } = ctx;
64
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
55
65
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`identity_credential\``);
56
66
  }
57
67
 
58
- module.exports = { up, down };
68
+ module.exports = { up, down, replacesChecksums };
@@ -23,9 +23,17 @@
23
23
  * Idempotent (CREATE TABLE IF NOT EXISTS).
24
24
  */
25
25
 
26
- const SCHEMA = 'shield_identity';
26
+ const { frameworkSchemasForMigration } = require('@astram/core');
27
27
 
28
- async function up({ query }) {
28
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
29
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
30
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
31
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
32
+ const replacesChecksums = ['882c6700cb00a977a1dc5038de0a1e107a279a28312a44359f5c8f8efa595c83'];
33
+
34
+ async function up(ctx) {
35
+ const { query } = ctx;
36
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
29
37
  // session — the authoritative session record (written at login; read/updated
30
38
  // at refresh + revocation). s_absolute_expires_at_ms anchors the ADR-0015
31
39
  // absolute cap (from login, never reset). Status ∈ active|rotated|revoked|expired.
@@ -70,10 +78,12 @@ async function up({ query }) {
70
78
  );
71
79
  }
72
80
 
73
- async function down({ query }) {
81
+ async function down(ctx) {
82
+ const { query } = ctx;
83
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
74
84
  // Child before parent — refresh_token FK-references session.
75
85
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`refresh_token\``);
76
86
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`session\``);
77
87
  }
78
88
 
79
- module.exports = { up, down };
89
+ module.exports = { up, down, replacesChecksums };
@@ -39,9 +39,17 @@
39
39
  * Idempotent (CREATE TABLE IF NOT EXISTS).
40
40
  */
41
41
 
42
- const SCHEMA = 'shield_identity';
42
+ const { frameworkSchemasForMigration } = require('@astram/core');
43
43
 
44
- async function up({ query }) {
44
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
45
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
46
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
47
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
48
+ const replacesChecksums = ['47015938e94c86373d0947828f6cb5019aff90d414391eccb8c52b40c81d7612'];
49
+
50
+ async function up(ctx) {
51
+ const { query } = ctx;
52
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
45
53
  await query(
46
54
  `CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`mfa_device\` (
47
55
  md_id VARCHAR(36) NOT NULL,
@@ -79,10 +87,12 @@ async function up({ query }) {
79
87
  );
80
88
  }
81
89
 
82
- async function down({ query }) {
90
+ async function down(ctx) {
91
+ const { query } = ctx;
92
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
83
93
  for (const t of ['mfa_recovery_code', 'password_reset_token', 'mfa_device']) {
84
94
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`${t}\``);
85
95
  }
86
96
  }
87
97
 
88
- module.exports = { up, down };
98
+ module.exports = { up, down, replacesChecksums };
@@ -13,9 +13,17 @@
13
13
  * server/migrations/1720000000002-CreateAuditLog.js. Idempotent.
14
14
  */
15
15
 
16
- const SCHEMA = 'shield_audit';
16
+ const { frameworkSchemasForMigration } = require('@astram/core');
17
17
 
18
- async function up({ query }) {
18
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
19
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
20
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
21
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
22
+ const replacesChecksums = ['da5fa4187db370cf9f70d97a94e27e54055bcb5bf9e8cfd6305a9bcfef748703'];
23
+
24
+ async function up(ctx) {
25
+ const { query } = ctx;
26
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).audit;
19
27
  await query(`CREATE SCHEMA IF NOT EXISTS \`${SCHEMA}\` DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
20
28
 
21
29
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`audit_log\` (
@@ -35,8 +43,10 @@ async function up({ query }) {
35
43
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
36
44
  }
37
45
 
38
- async function down({ query }) {
46
+ async function down(ctx) {
47
+ const { query } = ctx;
48
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).audit;
39
49
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`audit_log\``);
40
50
  }
41
51
 
42
- module.exports = { up, down };
52
+ module.exports = { up, down, replacesChecksums };
@@ -28,10 +28,19 @@
28
28
  * table. Timestamp 1710000000011 sorts after audit (...010) and before the dev seed (...099).
29
29
  */
30
30
 
31
- const SCHEMA = 'shield_authz';
31
+ const { frameworkSchemasForMigration } = require('@astram/core');
32
+
33
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
34
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
35
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
36
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
37
+ const replacesChecksums = ['3672b420280dc95cf15faab0a0a75cf7cdbf7d4b63cd3634bbe1508bf48d93a4'];
38
+
32
39
  const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
33
40
 
34
- async function up({ query }) {
41
+ async function up(ctx) {
42
+ const { query } = ctx;
43
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).authz;
35
44
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\` (
36
45
  ctg_uuid VARCHAR(36) NOT NULL, -- PK (grant uuid)
37
46
  ctg_owning_tenant_uuid VARCHAR(36) NOT NULL, -- tenant whose data is reached into / who consents
@@ -59,8 +68,10 @@ async function up({ query }) {
59
68
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
60
69
  }
61
70
 
62
- async function down({ query }) {
71
+ async function down(ctx) {
72
+ const { query } = ctx;
73
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).authz;
63
74
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\``);
64
75
  }
65
76
 
66
- module.exports = { up, down };
77
+ module.exports = { up, down, replacesChecksums };
@@ -35,7 +35,14 @@
35
35
  * optional at grant-write time; `role_permission` has no FK to `permission`).
36
36
  */
37
37
 
38
- const AUTHZ = 'shield_authz';
38
+ const { frameworkSchemasForMigration } = require('@astram/core');
39
+
40
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
41
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
42
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
43
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
44
+ const replacesChecksums = ['10cd7ad4323cc79408a0dab2334b76d0cae289c25a29838257515c776e0b04e6'];
45
+
39
46
  const MODULE = 'identity';
40
47
  const TYPE_KEY = 'cross_tenant_grant';
41
48
  const NEW_ACTIONS = ['share', 'accept', 'revoke'];
@@ -51,7 +58,9 @@ const OLD_ADMIN_KEYS = [
51
58
  'identity',
52
59
  ];
53
60
 
54
- async function up({ query }) {
61
+ async function up(ctx) {
62
+ const { query } = ctx;
63
+ const AUTHZ = frameworkSchemasForMigration(ctx, process.env).authz;
55
64
  // Every DISTINCT role currently holding ANY of the coarse identity-admin keys.
56
65
  const oldAdminKeyPlaceholders = OLD_ADMIN_KEYS.map(() => '?').join(', ');
57
66
  const adminRoles = await query(
@@ -75,7 +84,9 @@ async function up({ query }) {
75
84
  }
76
85
  }
77
86
 
78
- async function down({ query }) {
87
+ async function down(ctx) {
88
+ const { query } = ctx;
89
+ const AUTHZ = frameworkSchemasForMigration(ctx, process.env).authz;
79
90
  const keyPlaceholders = NEW_KEYS.map(() => '?').join(', ');
80
91
  await query(
81
92
  `DELETE FROM \`${AUTHZ}\`.\`role_permission\` WHERE rp_perm_key IN (${keyPlaceholders})`,
@@ -83,4 +94,4 @@ async function down({ query }) {
83
94
  );
84
95
  }
85
96
 
86
- module.exports = { up, down };
97
+ module.exports = { up, down, replacesChecksums };
@@ -11,9 +11,17 @@
11
11
  * only the table, not the schema. Idempotent (IF NOT EXISTS); down() drops the table.
12
12
  */
13
13
 
14
- const SCHEMA = 'shield_app';
14
+ const { frameworkSchemasForMigration } = require('@astram/core');
15
15
 
16
- async function up({ query }) {
16
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
17
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
18
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
19
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
20
+ const replacesChecksums = ['a7ae9649c125ecc1565363def7b0d0fc69a313230bec67bfc4025af80e898c04'];
21
+
22
+ async function up(ctx) {
23
+ const { query } = ctx;
24
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
17
25
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`shared_resource\` (
18
26
  sr_uuid VARCHAR(36) NOT NULL,
19
27
  sr_tenant_id VARCHAR(36) NOT NULL,
@@ -23,8 +31,10 @@ async function up({ query }) {
23
31
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
24
32
  }
25
33
 
26
- async function down({ query }) {
34
+ async function down(ctx) {
35
+ const { query } = ctx;
36
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
27
37
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`shared_resource\``);
28
38
  }
29
39
 
30
- module.exports = { up, down };
40
+ module.exports = { up, down, replacesChecksums };
@@ -20,13 +20,20 @@
20
20
  */
21
21
  const { ServiceAccount } = require('@astram/identity');
22
22
  const { columnForRole, reflectObjectType } = require('@astram/engine');
23
+ const { frameworkSchemasForMigration } = require('@astram/core');
23
24
 
24
- const SCHEMA = 'shield_app';
25
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
26
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
27
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
28
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
29
+ const replacesChecksums = ['848b0ff08ec31c851dec3c891e0c87920ba956c8ad2989ee03bc8509c2e28c20'];
25
30
 
26
31
  const SaReg = reflectObjectType(ServiceAccount);
27
32
  const col = (role) => columnForRole(SaReg, role);
28
33
 
29
- async function up({ query }) {
34
+ async function up(ctx) {
35
+ const { query } = ctx;
36
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
30
37
  // Only `primaryKey` + `status` are engine ROLES (single-sourced via col()); the other columns
31
38
  // carry no role, so they are named literally — matching their `@Field({ column })` on the object
32
39
  // (sa_tenant_id/sa_label/sa_created_at_ms) or are store-only (sa_scope_subtree/sa_role_ids/…).
@@ -46,8 +53,10 @@ async function up({ query }) {
46
53
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
47
54
  }
48
55
 
49
- async function down({ query }) {
56
+ async function down(ctx) {
57
+ const { query } = ctx;
58
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
50
59
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`${SaReg.table}\``);
51
60
  }
52
61
 
53
- module.exports = { up, down };
62
+ module.exports = { up, down, replacesChecksums };
@@ -19,17 +19,27 @@
19
19
  * change).
20
20
  */
21
21
 
22
- const SCHEMA = 'shield_identity';
22
+ const { frameworkSchemasForMigration } = require('@astram/core');
23
23
 
24
- async function up({ query }) {
24
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
25
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
26
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
27
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
28
+ const replacesChecksums = ['a734be029c84615378c41aca1b0dc0cfac00d4213c87f0ea11700902a8319c0d'];
29
+
30
+ async function up(ctx) {
31
+ const { query } = ctx;
32
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
25
33
  await query(
26
34
  `ALTER TABLE \`${SCHEMA}\`.\`session\`
27
35
  ADD COLUMN IF NOT EXISTS \`s_must_reenroll\` TINYINT(1) NOT NULL DEFAULT 0`,
28
36
  );
29
37
  }
30
38
 
31
- async function down({ query }) {
39
+ async function down(ctx) {
40
+ const { query } = ctx;
41
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
32
42
  await query(`ALTER TABLE \`${SCHEMA}\`.\`session\` DROP COLUMN IF EXISTS \`s_must_reenroll\``);
33
43
  }
34
44
 
35
- module.exports = { up, down };
45
+ module.exports = { up, down, replacesChecksums };
@@ -30,9 +30,17 @@
30
30
  * names. Idempotent (CREATE TABLE IF NOT EXISTS / DROP TABLE IF EXISTS).
31
31
  */
32
32
 
33
- const SCHEMA = 'shield_identity';
33
+ const { frameworkSchemasForMigration } = require('@astram/core');
34
34
 
35
- async function up({ query }) {
35
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
36
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
37
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
38
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
39
+ const replacesChecksums = ['978e80f32ad2103c6e298ed70fb8b73fab557448a376e80650f1d1c9f892aa39'];
40
+
41
+ async function up(ctx) {
42
+ const { query } = ctx;
43
+ const { identity: SCHEMA, app: APP } = frameworkSchemasForMigration(ctx, process.env);
36
44
  await query(
37
45
  `CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`email_change_token\` (
38
46
  ect_token_hash VARCHAR(64) NOT NULL,
@@ -49,14 +57,16 @@ async function up({ query }) {
49
57
  INDEX idx_ect_principal (ect_principal_id),
50
58
  INDEX idx_ect_expires (ect_expires_at_ms),
51
59
  CONSTRAINT FK_email_change_token_user
52
- FOREIGN KEY (ect_principal_id) REFERENCES \`shield_app\`.\`users\` (u_uuid)
60
+ FOREIGN KEY (ect_principal_id) REFERENCES \`${APP}\`.\`users\` (u_uuid)
53
61
  ON DELETE CASCADE
54
62
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,
55
63
  );
56
64
  }
57
65
 
58
- async function down({ query }) {
66
+ async function down(ctx) {
67
+ const { query } = ctx;
68
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
59
69
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`email_change_token\``);
60
70
  }
61
71
 
62
- module.exports = { up, down };
72
+ module.exports = { up, down, replacesChecksums };
@@ -38,7 +38,14 @@
38
38
  * (`...099`/`...100`, distinct addresses).
39
39
  */
40
40
 
41
- const SCHEMA = 'shield_app';
41
+ const { frameworkSchemasForMigration } = require('@astram/core');
42
+
43
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
44
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
45
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
46
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
47
+ const replacesChecksums = ['a08085e726ff0c2ec24c069cf099097a0bdefd06d0cab22c7d528280753de3f0'];
48
+
42
49
  const TABLE = 'users';
43
50
  const COLUMN = 'u_email';
44
51
  const INDEX = 'uq_users_email';
@@ -47,7 +54,7 @@ const MIGRATION = '1710000000021-AddUsersEmailUniqueIndex';
47
54
  const MAX_LISTED_GROUPS = 50;
48
55
  const ER_DUP_ENTRY = 1062;
49
56
 
50
- /** Same rule as `@astram/guard` `maskEmail` (kept local: a migration loads no runtime package). */
57
+ /** Same rule as `@astram/guard` `maskEmail` (kept local: the migration loads only `@astram/core`, which has no such helper). */
51
58
  function maskEmail(value) {
52
59
  const at = value.lastIndexOf('@');
53
60
  if (at <= 0 || at === value.length - 1) return '***';
@@ -55,7 +62,7 @@ function maskEmail(value) {
55
62
  }
56
63
 
57
64
  /** `null` = no index of that name; otherwise whether it is UNIQUE. */
58
- async function indexState(query) {
65
+ async function indexState(query, SCHEMA) {
59
66
  const rows = await query(
60
67
  `SELECT NON_UNIQUE AS non_unique
61
68
  FROM information_schema.STATISTICS
@@ -68,7 +75,7 @@ async function indexState(query) {
68
75
  }
69
76
 
70
77
  /** Duplicate groups by the column collation: `[{ masked, count }]`, largest first. */
71
- async function duplicateGroups(query) {
78
+ async function duplicateGroups(query, SCHEMA) {
72
79
  const rows = await query(
73
80
  `SELECT ${COLUMN} AS sample, COUNT(*) AS n
74
81
  FROM \`${SCHEMA}\`.\`${TABLE}\`
@@ -84,7 +91,7 @@ async function duplicateGroups(query) {
84
91
  }));
85
92
  }
86
93
 
87
- function duplicateError(groups, emptiesNormalised) {
94
+ function duplicateError(groups, emptiesNormalised, SCHEMA) {
88
95
  const listed = groups.slice(0, MAX_LISTED_GROUPS).map((g) => ` - ${g.masked}: ${g.count} users`);
89
96
  if (groups.length > MAX_LISTED_GROUPS) {
90
97
  listed.push(` - ... and ${groups.length - MAX_LISTED_GROUPS} more group(s)`);
@@ -104,8 +111,10 @@ function duplicateError(groups, emptiesNormalised) {
104
111
  );
105
112
  }
106
113
 
107
- async function up({ query }) {
108
- const state = await indexState(query);
114
+ async function up(ctx) {
115
+ const { query } = ctx;
116
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
117
+ const state = await indexState(query, SCHEMA);
109
118
  if (state !== null) {
110
119
  if (state.unique) return; // already applied (idempotent re-run)
111
120
  throw new Error(
@@ -120,8 +129,8 @@ async function up({ query }) {
120
129
  WHERE ${COLUMN} IS NOT NULL AND ${COLUMN} REGEXP '^[[:space:]]*$'`,
121
130
  );
122
131
 
123
- const groups = await duplicateGroups(query);
124
- if (groups.length > 0) throw duplicateError(groups, false);
132
+ const groups = await duplicateGroups(query, SCHEMA);
133
+ if (groups.length > 0) throw duplicateError(groups, false, SCHEMA);
125
134
 
126
135
  try {
127
136
  await query(
@@ -130,16 +139,18 @@ async function up({ query }) {
130
139
  } catch (err) {
131
140
  if (err === null || typeof err !== 'object' || err.errno !== ER_DUP_ENTRY) throw err;
132
141
  // A duplicate was written after the pre-check. Never surface the driver message (full address).
133
- const late = await duplicateGroups(query);
134
- if (late.length > 0) throw duplicateError(late, true);
142
+ const late = await duplicateGroups(query, SCHEMA);
143
+ if (late.length > 0) throw duplicateError(late, true, SCHEMA);
135
144
  throw new Error(
136
145
  `${MIGRATION}: a duplicate ${COLUMN} was written while the index was built; re-run astram-migrate.`,
137
146
  );
138
147
  }
139
148
  }
140
149
 
141
- async function down({ query }) {
150
+ async function down(ctx) {
151
+ const { query } = ctx;
152
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
142
153
  await query(`ALTER TABLE \`${SCHEMA}\`.\`${TABLE}\` DROP INDEX IF EXISTS \`${INDEX}\``);
143
154
  }
144
155
 
145
- module.exports = { up, down };
156
+ module.exports = { up, down, replacesChecksums };
@@ -16,9 +16,17 @@
16
16
  * No seed, no hard-coded ids.
17
17
  */
18
18
 
19
- const SCHEMA = 'shield_app';
19
+ const { frameworkSchemasForMigration } = require('@astram/core');
20
20
 
21
- async function up({ query }) {
21
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
22
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
23
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
24
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
25
+ const replacesChecksums = ['e2af5c4329d6b312ebbe1f8d61e8043ec578554e9ba59e18647ed7a8eb650d18'];
26
+
27
+ async function up(ctx) {
28
+ const { query } = ctx;
29
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
22
30
  await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`stored_file\` (
23
31
  sf_uuid VARCHAR(36) NOT NULL,
24
32
  sf_tenant_id VARCHAR(36) NOT NULL,
@@ -36,8 +44,10 @@ async function up({ query }) {
36
44
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
37
45
  }
38
46
 
39
- async function down({ query }) {
47
+ async function down(ctx) {
48
+ const { query } = ctx;
49
+ const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
40
50
  await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`stored_file\``);
41
51
  }
42
52
 
43
- module.exports = { up, down };
53
+ module.exports = { up, down, replacesChecksums };
@@ -20,9 +20,13 @@
20
20
  * by business key). down() removes the chain by business key.
21
21
  */
22
22
 
23
- const APP = 'shield_app';
24
- const IDENT = 'shield_identity';
25
- const AUTHZ = 'shield_authz';
23
+ const { frameworkSchemasForMigration } = require('@astram/core');
24
+
25
+ // Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
26
+ // @astram/migrate without it, the default names apply only while the namespace is unset.
27
+ // replacesChecksums: every checksum this file had in a published release (git release tags), so
28
+ // an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
29
+ const replacesChecksums = ['9b8a1ed3b967b95ae784396d9f14ee87dfc64253f3389ca4d413ca16f4f229d9'];
26
30
 
27
31
  const COMPANY = 'Astram Dev Co';
28
32
  const USERNAME = 'admin';
@@ -38,7 +42,9 @@ function seedEnabled() {
38
42
  return process.env.NODE_ENV === 'development' || process.env.ASTRAM_SEED_DEV === '1';
39
43
  }
40
44
 
41
- async function up({ query }) {
45
+ async function up(ctx) {
46
+ const { query } = ctx;
47
+ const { app: APP, identity: IDENT, authz: AUTHZ } = frameworkSchemasForMigration(ctx, process.env);
42
48
  if (!seedEnabled()) return; // production / non-dev → no-op
43
49
 
44
50
  // 1) company (tenant)
@@ -109,7 +115,9 @@ async function up({ query }) {
109
115
  );
110
116
  }
111
117
 
112
- async function down({ query }) {
118
+ async function down(ctx) {
119
+ const { query } = ctx;
120
+ const { app: APP, identity: IDENT, authz: AUTHZ } = frameworkSchemasForMigration(ctx, process.env);
113
121
  // Remove by business key (safe whether or not seed ran). Order: dependents first.
114
122
  const [role] = await query(`SELECT r_uuid FROM \`${APP}\`.\`roles\` WHERE r_name = ? LIMIT 1`, [ROLE]);
115
123
  if (role) {
@@ -127,4 +135,4 @@ async function down({ query }) {
127
135
  await query(`DELETE FROM \`${APP}\`.\`companies\` WHERE c_name = ?`, [COMPANY]);
128
136
  }
129
137
 
130
- module.exports = { up, down };
138
+ module.exports = { up, down, replacesChecksums };