@axiom-lattice/pg-stores 5.0.3 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +102 -0
- package/dist/index.d.mts +178 -2
- package/dist/index.d.ts +178 -2
- package/dist/index.js +1243 -197
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1167 -124
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/PostgreSQLInviteLinkStore.test.ts +63 -0
- package/src/__tests__/PostgreSQLOidcIdentityClaimStore.test.ts +123 -0
- package/src/__tests__/PostgreSQLOidcProviderConfigStore.test.ts +233 -0
- package/src/__tests__/PostgreSQLPlatformInvitationStore.test.ts +222 -0
- package/src/__tests__/PostgreSQLPlatformMemberStore.test.ts +146 -0
- package/src/__tests__/PostgreSQLPlatformSettingsStore.test.ts +115 -0
- package/src/__tests__/PostgreSQLUserExternalIdentityStore.test.ts +125 -0
- package/src/__tests__/PostgreSQLUserStore.test.ts +75 -0
- package/src/createPgStoreConfig.ts +34 -0
- package/src/index.ts +16 -0
- package/src/migrations/invite_link_migration.ts +35 -0
- package/src/migrations/oidc_identity_claim_invite_migration.ts +29 -0
- package/src/migrations/oidc_identity_claim_migration.ts +39 -0
- package/src/migrations/oidc_provider_allowed_hosts_migration.ts +19 -0
- package/src/migrations/oidc_provider_config_migration.ts +27 -0
- package/src/migrations/platform_invitation_migration.ts +22 -0
- package/src/migrations/platform_member_migration.ts +21 -0
- package/src/migrations/platform_settings_migration.ts +21 -0
- package/src/migrations/user_email_lower_unique_migration.ts +22 -0
- package/src/migrations/user_external_identity_migration.ts +27 -0
- package/src/stores/PostgreSQLInviteLinkStore.ts +134 -0
- package/src/stores/PostgreSQLOidcIdentityClaimStore.ts +251 -0
- package/src/stores/PostgreSQLOidcProviderConfigStore.ts +222 -0
- package/src/stores/PostgreSQLPlatformInvitationStore.ts +133 -0
- package/src/stores/PostgreSQLPlatformMemberStore.ts +105 -0
- package/src/stores/PostgreSQLPlatformSettingsStore.ts +98 -0
- package/src/stores/PostgreSQLUserExternalIdentityStore.ts +134 -0
- package/src/stores/PostgreSQLUserStore.ts +3 -2
package/dist/index.mjs
CHANGED
|
@@ -2950,9 +2950,10 @@ var PostgreSQLUserStore = class {
|
|
|
2950
2950
|
`
|
|
2951
2951
|
SELECT id, email, name, status, metadata, created_at, updated_at
|
|
2952
2952
|
FROM lattice_users
|
|
2953
|
-
WHERE email = $1
|
|
2953
|
+
WHERE lower(email) = lower($1)
|
|
2954
|
+
LIMIT 1
|
|
2954
2955
|
`,
|
|
2955
|
-
[email]
|
|
2956
|
+
[email.trim()]
|
|
2956
2957
|
);
|
|
2957
2958
|
if (result.rows.length === 0) return null;
|
|
2958
2959
|
return this.mapRowToUser(result.rows[0]);
|
|
@@ -3493,6 +3494,693 @@ var PostgreSQLUserTenantLinkStore = class {
|
|
|
3493
3494
|
}
|
|
3494
3495
|
};
|
|
3495
3496
|
|
|
3497
|
+
// src/stores/PostgreSQLUserExternalIdentityStore.ts
|
|
3498
|
+
import {
|
|
3499
|
+
ExternalIdentityConflictError,
|
|
3500
|
+
ExternalIdentityPersistError
|
|
3501
|
+
} from "@axiom-lattice/protocols";
|
|
3502
|
+
var InvalidUserExternalIdentityRowError = class extends Error {
|
|
3503
|
+
constructor() {
|
|
3504
|
+
super("Invalid user external identity row");
|
|
3505
|
+
this.code = "INVALID_USER_EXTERNAL_IDENTITY_ROW";
|
|
3506
|
+
this.name = "InvalidUserExternalIdentityRowError";
|
|
3507
|
+
}
|
|
3508
|
+
};
|
|
3509
|
+
var columns = "user_id, provider_id, issuer, subject, email, linked_at";
|
|
3510
|
+
function mapRow(row) {
|
|
3511
|
+
if (typeof row.user_id !== "string" || typeof row.provider_id !== "string" || typeof row.issuer !== "string" || typeof row.subject !== "string" || !(row.linked_at instanceof Date) || Number.isNaN(row.linked_at.getTime())) {
|
|
3512
|
+
throw new InvalidUserExternalIdentityRowError();
|
|
3513
|
+
}
|
|
3514
|
+
return {
|
|
3515
|
+
userId: row.user_id,
|
|
3516
|
+
providerId: row.provider_id,
|
|
3517
|
+
issuer: row.issuer,
|
|
3518
|
+
subject: row.subject,
|
|
3519
|
+
...typeof row.email === "string" ? { email: row.email } : {},
|
|
3520
|
+
linkedAt: row.linked_at
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
var PostgreSQLUserExternalIdentityStore = class {
|
|
3524
|
+
constructor(options) {
|
|
3525
|
+
this.pool = options.pool;
|
|
3526
|
+
}
|
|
3527
|
+
async getByProviderSubject(providerId, issuer, subject) {
|
|
3528
|
+
const result = await this.pool.query(
|
|
3529
|
+
`SELECT ${columns} FROM lattice_user_identities
|
|
3530
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
|
|
3531
|
+
[providerId, issuer, subject]
|
|
3532
|
+
);
|
|
3533
|
+
return result.rows[0] ? mapRow(result.rows[0]) : null;
|
|
3534
|
+
}
|
|
3535
|
+
async listByUser(userId) {
|
|
3536
|
+
const result = await this.pool.query(
|
|
3537
|
+
`SELECT ${columns} FROM lattice_user_identities WHERE user_id = $1 ORDER BY linked_at ASC`,
|
|
3538
|
+
[userId]
|
|
3539
|
+
);
|
|
3540
|
+
return result.rows.map(mapRow);
|
|
3541
|
+
}
|
|
3542
|
+
async link(identity) {
|
|
3543
|
+
const inserted = await this.pool.query(
|
|
3544
|
+
`INSERT INTO lattice_user_identities (${columns})
|
|
3545
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3546
|
+
ON CONFLICT (provider_id, issuer, subject) DO NOTHING
|
|
3547
|
+
RETURNING ${columns}`,
|
|
3548
|
+
[
|
|
3549
|
+
identity.userId,
|
|
3550
|
+
identity.providerId,
|
|
3551
|
+
identity.issuer,
|
|
3552
|
+
identity.subject,
|
|
3553
|
+
identity.email ?? null,
|
|
3554
|
+
identity.linkedAt
|
|
3555
|
+
]
|
|
3556
|
+
);
|
|
3557
|
+
if (inserted.rows[0]) return;
|
|
3558
|
+
const existing = await this.getByProviderSubject(
|
|
3559
|
+
identity.providerId,
|
|
3560
|
+
identity.issuer,
|
|
3561
|
+
identity.subject
|
|
3562
|
+
);
|
|
3563
|
+
if (!existing) {
|
|
3564
|
+
throw new ExternalIdentityPersistError(
|
|
3565
|
+
identity.providerId,
|
|
3566
|
+
identity.issuer,
|
|
3567
|
+
identity.subject
|
|
3568
|
+
);
|
|
3569
|
+
}
|
|
3570
|
+
if (existing.userId !== identity.userId) {
|
|
3571
|
+
throw new ExternalIdentityConflictError(
|
|
3572
|
+
identity.providerId,
|
|
3573
|
+
identity.issuer,
|
|
3574
|
+
identity.subject
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
if (identity.email !== void 0 && identity.email !== existing.email) {
|
|
3578
|
+
await this.pool.query(
|
|
3579
|
+
`UPDATE lattice_user_identities SET email = $1
|
|
3580
|
+
WHERE provider_id = $2 AND issuer = $3 AND subject = $4`,
|
|
3581
|
+
[identity.email, identity.providerId, identity.issuer, identity.subject]
|
|
3582
|
+
);
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
async unlink(providerId, issuer, subject) {
|
|
3586
|
+
await this.pool.query(
|
|
3587
|
+
`DELETE FROM lattice_user_identities
|
|
3588
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
|
|
3589
|
+
[providerId, issuer, subject]
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
};
|
|
3593
|
+
|
|
3594
|
+
// src/stores/PostgreSQLOidcProviderConfigStore.ts
|
|
3595
|
+
import { safeDecrypt as safeDecrypt4, encrypt as encrypt4 } from "@axiom-lattice/core";
|
|
3596
|
+
import {
|
|
3597
|
+
OidcProviderConfigConflictError
|
|
3598
|
+
} from "@axiom-lattice/protocols";
|
|
3599
|
+
var TABLE2 = "lattice_oidc_provider_configs";
|
|
3600
|
+
function parseAllowedPrivateHosts(value) {
|
|
3601
|
+
if (!Array.isArray(value)) return void 0;
|
|
3602
|
+
return value.filter((entry) => typeof entry === "string");
|
|
3603
|
+
}
|
|
3604
|
+
function serializeAllowedPrivateHosts(value) {
|
|
3605
|
+
return value === void 0 ? null : JSON.stringify(value);
|
|
3606
|
+
}
|
|
3607
|
+
function normalizeTenantId(value) {
|
|
3608
|
+
if (typeof value !== "string") return void 0;
|
|
3609
|
+
const trimmed = value.trim();
|
|
3610
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3611
|
+
}
|
|
3612
|
+
function mapRow2(row) {
|
|
3613
|
+
if (typeof row.id !== "string" || typeof row.display_name !== "string" || typeof row.issuer !== "string" || typeof row.client_id !== "string" || typeof row.client_secret !== "string" || typeof row.scopes !== "string" || typeof row.enabled !== "boolean" || typeof row.jit_provisioning !== "boolean" || typeof row.auto_link_email !== "boolean" || !(row.created_at instanceof Date) || !(row.updated_at instanceof Date)) {
|
|
3614
|
+
throw new Error("Invalid OIDC provider config row");
|
|
3615
|
+
}
|
|
3616
|
+
const allowedPrivateHosts = parseAllowedPrivateHosts(
|
|
3617
|
+
row.allowed_private_hosts
|
|
3618
|
+
);
|
|
3619
|
+
return {
|
|
3620
|
+
id: row.id,
|
|
3621
|
+
displayName: row.display_name,
|
|
3622
|
+
issuer: row.issuer,
|
|
3623
|
+
clientId: row.client_id,
|
|
3624
|
+
clientSecret: safeDecrypt4(
|
|
3625
|
+
row.client_secret,
|
|
3626
|
+
`OIDC provider config "${row.id}" client secret`
|
|
3627
|
+
),
|
|
3628
|
+
scopes: row.scopes,
|
|
3629
|
+
enabled: row.enabled,
|
|
3630
|
+
jitProvisioning: row.jit_provisioning,
|
|
3631
|
+
autoLinkEmail: row.auto_link_email,
|
|
3632
|
+
...typeof row.default_tenant_id === "string" ? { defaultTenantId: row.default_tenant_id } : {},
|
|
3633
|
+
...allowedPrivateHosts !== void 0 ? { allowedPrivateHosts } : {},
|
|
3634
|
+
createdAt: row.created_at,
|
|
3635
|
+
updatedAt: row.updated_at
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
function isUniqueViolation(error) {
|
|
3639
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
3640
|
+
}
|
|
3641
|
+
var PostgreSQLOidcProviderConfigStore = class {
|
|
3642
|
+
constructor(options) {
|
|
3643
|
+
this.pool = options.pool;
|
|
3644
|
+
}
|
|
3645
|
+
async list() {
|
|
3646
|
+
const result = await this.pool.query(
|
|
3647
|
+
`SELECT * FROM ${TABLE2} ORDER BY created_at ASC, id ASC`
|
|
3648
|
+
);
|
|
3649
|
+
return result.rows.map(mapRow2);
|
|
3650
|
+
}
|
|
3651
|
+
async get(id) {
|
|
3652
|
+
const result = await this.pool.query(
|
|
3653
|
+
`SELECT * FROM ${TABLE2} WHERE id = $1`,
|
|
3654
|
+
[id]
|
|
3655
|
+
);
|
|
3656
|
+
return result.rows[0] ? mapRow2(result.rows[0]) : null;
|
|
3657
|
+
}
|
|
3658
|
+
async create(id, input) {
|
|
3659
|
+
const tenantId = normalizeTenantId(input.defaultTenantId);
|
|
3660
|
+
try {
|
|
3661
|
+
await this.pool.query(
|
|
3662
|
+
`INSERT INTO ${TABLE2}
|
|
3663
|
+
(id, display_name, issuer, client_id, client_secret, scopes,
|
|
3664
|
+
enabled, jit_provisioning, auto_link_email, default_tenant_id,
|
|
3665
|
+
allowed_private_hosts)
|
|
3666
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
|
3667
|
+
[
|
|
3668
|
+
id,
|
|
3669
|
+
input.displayName,
|
|
3670
|
+
input.issuer,
|
|
3671
|
+
input.clientId,
|
|
3672
|
+
encrypt4(input.clientSecret),
|
|
3673
|
+
input.scopes,
|
|
3674
|
+
input.enabled,
|
|
3675
|
+
input.jitProvisioning,
|
|
3676
|
+
input.autoLinkEmail,
|
|
3677
|
+
tenantId ?? null,
|
|
3678
|
+
serializeAllowedPrivateHosts(input.allowedPrivateHosts)
|
|
3679
|
+
]
|
|
3680
|
+
);
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
if (isUniqueViolation(error)) {
|
|
3683
|
+
throw new OidcProviderConfigConflictError(id);
|
|
3684
|
+
}
|
|
3685
|
+
throw error;
|
|
3686
|
+
}
|
|
3687
|
+
const created = await this.get(id);
|
|
3688
|
+
if (!created) {
|
|
3689
|
+
throw new Error(`OIDC provider config "${id}" was not persisted`);
|
|
3690
|
+
}
|
|
3691
|
+
return created;
|
|
3692
|
+
}
|
|
3693
|
+
async update(id, updates) {
|
|
3694
|
+
const existing = await this.get(id);
|
|
3695
|
+
if (!existing) return null;
|
|
3696
|
+
const setClauses = [];
|
|
3697
|
+
const values = [];
|
|
3698
|
+
let paramIndex = 1;
|
|
3699
|
+
const set = (column, value) => {
|
|
3700
|
+
setClauses.push(`${column} = $${paramIndex++}`);
|
|
3701
|
+
values.push(value);
|
|
3702
|
+
};
|
|
3703
|
+
if (updates.displayName !== void 0) set("display_name", updates.displayName);
|
|
3704
|
+
if (updates.issuer !== void 0) set("issuer", updates.issuer);
|
|
3705
|
+
if (updates.clientId !== void 0) set("client_id", updates.clientId);
|
|
3706
|
+
if (updates.clientSecret !== void 0 && updates.clientSecret !== "") {
|
|
3707
|
+
set("client_secret", encrypt4(updates.clientSecret));
|
|
3708
|
+
}
|
|
3709
|
+
if (updates.scopes !== void 0) set("scopes", updates.scopes);
|
|
3710
|
+
if (updates.enabled !== void 0) set("enabled", updates.enabled);
|
|
3711
|
+
if (updates.jitProvisioning !== void 0) {
|
|
3712
|
+
set("jit_provisioning", updates.jitProvisioning);
|
|
3713
|
+
}
|
|
3714
|
+
if (updates.autoLinkEmail !== void 0) {
|
|
3715
|
+
set("auto_link_email", updates.autoLinkEmail);
|
|
3716
|
+
}
|
|
3717
|
+
if (updates.defaultTenantId !== void 0) {
|
|
3718
|
+
set("default_tenant_id", normalizeTenantId(updates.defaultTenantId) ?? null);
|
|
3719
|
+
}
|
|
3720
|
+
if (updates.allowedPrivateHosts !== void 0) {
|
|
3721
|
+
set(
|
|
3722
|
+
"allowed_private_hosts",
|
|
3723
|
+
serializeAllowedPrivateHosts(updates.allowedPrivateHosts)
|
|
3724
|
+
);
|
|
3725
|
+
}
|
|
3726
|
+
if (setClauses.length === 0) return existing;
|
|
3727
|
+
setClauses.push("updated_at = NOW()");
|
|
3728
|
+
values.push(id);
|
|
3729
|
+
await this.pool.query(
|
|
3730
|
+
`UPDATE ${TABLE2} SET ${setClauses.join(", ")} WHERE id = $${paramIndex}`,
|
|
3731
|
+
values
|
|
3732
|
+
);
|
|
3733
|
+
return this.get(id);
|
|
3734
|
+
}
|
|
3735
|
+
async delete(id) {
|
|
3736
|
+
const result = await this.pool.query(`DELETE FROM ${TABLE2} WHERE id = $1`, [id]);
|
|
3737
|
+
return (result.rowCount ?? 0) > 0;
|
|
3738
|
+
}
|
|
3739
|
+
};
|
|
3740
|
+
|
|
3741
|
+
// src/stores/PostgreSQLPlatformMemberStore.ts
|
|
3742
|
+
var TABLE3 = "lattice_platform_members";
|
|
3743
|
+
var ROLES = ["owner", "admin", "auditor"];
|
|
3744
|
+
function isPlatformRole(value) {
|
|
3745
|
+
return ROLES.includes(value);
|
|
3746
|
+
}
|
|
3747
|
+
function mapRow3(row) {
|
|
3748
|
+
if (typeof row.user_id !== "string" || typeof row.role !== "string" || !isPlatformRole(row.role) || !(row.granted_at instanceof Date) || !(row.updated_at instanceof Date)) {
|
|
3749
|
+
throw new Error("Invalid platform member row");
|
|
3750
|
+
}
|
|
3751
|
+
return {
|
|
3752
|
+
userId: row.user_id,
|
|
3753
|
+
role: row.role,
|
|
3754
|
+
...typeof row.granted_by === "string" ? { grantedBy: row.granted_by } : {},
|
|
3755
|
+
grantedAt: row.granted_at,
|
|
3756
|
+
updatedAt: row.updated_at
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
var PostgreSQLPlatformMemberStore = class {
|
|
3760
|
+
constructor(options) {
|
|
3761
|
+
this.pool = options.pool;
|
|
3762
|
+
}
|
|
3763
|
+
/** Lists every platform member ordered by user id. */
|
|
3764
|
+
async list() {
|
|
3765
|
+
const result = await this.pool.query(
|
|
3766
|
+
`SELECT * FROM ${TABLE3} ORDER BY user_id ASC`
|
|
3767
|
+
);
|
|
3768
|
+
return result.rows.map(mapRow3);
|
|
3769
|
+
}
|
|
3770
|
+
/** Returns the platform member for a user, or null. */
|
|
3771
|
+
async get(userId) {
|
|
3772
|
+
const result = await this.pool.query(
|
|
3773
|
+
`SELECT * FROM ${TABLE3} WHERE user_id = $1`,
|
|
3774
|
+
[userId]
|
|
3775
|
+
);
|
|
3776
|
+
return result.rows[0] ? mapRow3(result.rows[0]) : null;
|
|
3777
|
+
}
|
|
3778
|
+
/**
|
|
3779
|
+
* Inserts or replaces a member keyed by user id.
|
|
3780
|
+
*
|
|
3781
|
+
* The supplied grant replaces any existing row in full (last write wins),
|
|
3782
|
+
* including `grantedAt` and `updatedAt`.
|
|
3783
|
+
*/
|
|
3784
|
+
async upsert(member) {
|
|
3785
|
+
const result = await this.pool.query(
|
|
3786
|
+
`INSERT INTO ${TABLE3} (user_id, role, granted_by, granted_at, updated_at)
|
|
3787
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3788
|
+
ON CONFLICT (user_id) DO UPDATE SET
|
|
3789
|
+
role = EXCLUDED.role,
|
|
3790
|
+
granted_by = EXCLUDED.granted_by,
|
|
3791
|
+
granted_at = EXCLUDED.granted_at,
|
|
3792
|
+
updated_at = EXCLUDED.updated_at
|
|
3793
|
+
RETURNING *`,
|
|
3794
|
+
[
|
|
3795
|
+
member.userId,
|
|
3796
|
+
member.role,
|
|
3797
|
+
member.grantedBy ?? null,
|
|
3798
|
+
member.grantedAt,
|
|
3799
|
+
member.updatedAt
|
|
3800
|
+
]
|
|
3801
|
+
);
|
|
3802
|
+
return mapRow3(result.rows[0]);
|
|
3803
|
+
}
|
|
3804
|
+
/** Deletes a member. Returns whether a row was removed. */
|
|
3805
|
+
async delete(userId) {
|
|
3806
|
+
const result = await this.pool.query(`DELETE FROM ${TABLE3} WHERE user_id = $1`, [
|
|
3807
|
+
userId
|
|
3808
|
+
]);
|
|
3809
|
+
return (result.rowCount ?? 0) > 0;
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
|
|
3813
|
+
// src/stores/PostgreSQLPlatformInvitationStore.ts
|
|
3814
|
+
import {
|
|
3815
|
+
normalizePlatformInvitationEmail
|
|
3816
|
+
} from "@axiom-lattice/protocols";
|
|
3817
|
+
var TABLE4 = "lattice_platform_invitations";
|
|
3818
|
+
var ROLES2 = ["owner", "admin", "auditor"];
|
|
3819
|
+
function isPlatformRole2(value) {
|
|
3820
|
+
return ROLES2.includes(value);
|
|
3821
|
+
}
|
|
3822
|
+
function mapRow4(row) {
|
|
3823
|
+
if (typeof row.email !== "string" || typeof row.role !== "string" || !isPlatformRole2(row.role) || !(row.created_at instanceof Date)) {
|
|
3824
|
+
throw new Error("Invalid platform invitation row");
|
|
3825
|
+
}
|
|
3826
|
+
return {
|
|
3827
|
+
email: row.email,
|
|
3828
|
+
role: row.role,
|
|
3829
|
+
...typeof row.invited_by === "string" ? { invitedBy: row.invited_by } : {},
|
|
3830
|
+
createdAt: row.created_at,
|
|
3831
|
+
...row.expires_at instanceof Date ? { expiresAt: row.expires_at } : {},
|
|
3832
|
+
...row.consumed_at instanceof Date ? { consumedAt: row.consumed_at } : {}
|
|
3833
|
+
};
|
|
3834
|
+
}
|
|
3835
|
+
var PostgreSQLPlatformInvitationStore = class {
|
|
3836
|
+
constructor(options) {
|
|
3837
|
+
this.pool = options.pool;
|
|
3838
|
+
}
|
|
3839
|
+
/** Lists every invitation ordered by normalized email. */
|
|
3840
|
+
async list() {
|
|
3841
|
+
const result = await this.pool.query(
|
|
3842
|
+
`SELECT * FROM ${TABLE4} ORDER BY email ASC`
|
|
3843
|
+
);
|
|
3844
|
+
return result.rows.map(mapRow4);
|
|
3845
|
+
}
|
|
3846
|
+
/** Returns the invitation for an email, or null. */
|
|
3847
|
+
async get(email) {
|
|
3848
|
+
const result = await this.pool.query(
|
|
3849
|
+
`SELECT * FROM ${TABLE4} WHERE email = $1`,
|
|
3850
|
+
[normalizePlatformInvitationEmail(email)]
|
|
3851
|
+
);
|
|
3852
|
+
return result.rows[0] ? mapRow4(result.rows[0]) : null;
|
|
3853
|
+
}
|
|
3854
|
+
/**
|
|
3855
|
+
* Inserts or replaces an invitation keyed by normalized email.
|
|
3856
|
+
*
|
|
3857
|
+
* The supplied invitation replaces any existing row in full (last write wins),
|
|
3858
|
+
* including `expiresAt` and `consumedAt`.
|
|
3859
|
+
*/
|
|
3860
|
+
async upsert(invitation) {
|
|
3861
|
+
const result = await this.pool.query(
|
|
3862
|
+
`INSERT INTO ${TABLE4}
|
|
3863
|
+
(email, role, invited_by, created_at, expires_at, consumed_at)
|
|
3864
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3865
|
+
ON CONFLICT (email) DO UPDATE SET
|
|
3866
|
+
role = EXCLUDED.role,
|
|
3867
|
+
invited_by = EXCLUDED.invited_by,
|
|
3868
|
+
created_at = EXCLUDED.created_at,
|
|
3869
|
+
expires_at = EXCLUDED.expires_at,
|
|
3870
|
+
consumed_at = EXCLUDED.consumed_at
|
|
3871
|
+
RETURNING *`,
|
|
3872
|
+
[
|
|
3873
|
+
normalizePlatformInvitationEmail(invitation.email),
|
|
3874
|
+
invitation.role,
|
|
3875
|
+
invitation.invitedBy ?? null,
|
|
3876
|
+
invitation.createdAt,
|
|
3877
|
+
invitation.expiresAt ?? null,
|
|
3878
|
+
invitation.consumedAt ?? null
|
|
3879
|
+
]
|
|
3880
|
+
);
|
|
3881
|
+
return mapRow4(result.rows[0]);
|
|
3882
|
+
}
|
|
3883
|
+
/** Deletes an invitation. Returns whether a row was removed. */
|
|
3884
|
+
async delete(email) {
|
|
3885
|
+
const result = await this.pool.query(
|
|
3886
|
+
`DELETE FROM ${TABLE4} WHERE email = $1`,
|
|
3887
|
+
[normalizePlatformInvitationEmail(email)]
|
|
3888
|
+
);
|
|
3889
|
+
return (result.rowCount ?? 0) > 0;
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Atomically consumes a pending, unexpired invitation.
|
|
3893
|
+
*
|
|
3894
|
+
* Returns the consumed invitation, or null when the row is missing, already
|
|
3895
|
+
* consumed, or expired.
|
|
3896
|
+
*/
|
|
3897
|
+
async consume(email, at) {
|
|
3898
|
+
const result = await this.pool.query(
|
|
3899
|
+
`UPDATE ${TABLE4}
|
|
3900
|
+
SET consumed_at = $1
|
|
3901
|
+
WHERE email = $2
|
|
3902
|
+
AND consumed_at IS NULL
|
|
3903
|
+
AND (expires_at IS NULL OR expires_at > $1)
|
|
3904
|
+
RETURNING *`,
|
|
3905
|
+
[at, normalizePlatformInvitationEmail(email)]
|
|
3906
|
+
);
|
|
3907
|
+
return result.rows[0] ? mapRow4(result.rows[0]) : null;
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
|
|
3911
|
+
// src/stores/PostgreSQLOidcIdentityClaimStore.ts
|
|
3912
|
+
import {
|
|
3913
|
+
normalizeOidcIdentityEmail,
|
|
3914
|
+
OidcIdentityClaimConflictError
|
|
3915
|
+
} from "@axiom-lattice/protocols";
|
|
3916
|
+
var TABLE5 = "lattice_oidc_identity_claims";
|
|
3917
|
+
function mapRow5(row) {
|
|
3918
|
+
return {
|
|
3919
|
+
id: row.id,
|
|
3920
|
+
providerId: row.provider_id,
|
|
3921
|
+
issuer: row.issuer,
|
|
3922
|
+
subject: row.subject,
|
|
3923
|
+
email: row.email,
|
|
3924
|
+
...row.name !== null ? { name: row.name } : {},
|
|
3925
|
+
...row.picture !== null ? { picture: row.picture } : {},
|
|
3926
|
+
status: row.status,
|
|
3927
|
+
createdAt: row.created_at,
|
|
3928
|
+
updatedAt: row.updated_at,
|
|
3929
|
+
expiresAt: row.expires_at,
|
|
3930
|
+
...row.reviewed_by !== null ? { reviewedBy: row.reviewed_by } : {},
|
|
3931
|
+
...row.reviewed_at !== null ? { reviewedAt: row.reviewed_at } : {},
|
|
3932
|
+
...row.linked_user_id !== null ? { linkedUserId: row.linked_user_id } : {},
|
|
3933
|
+
...row.source !== null ? { source: row.source } : {},
|
|
3934
|
+
...row.invite_link_id !== null ? { inviteLinkId: row.invite_link_id } : {},
|
|
3935
|
+
...row.requested_user_id !== null ? { requestedUserId: row.requested_user_id } : {},
|
|
3936
|
+
...row.requested_platform_role !== null ? { requestedPlatformRole: row.requested_platform_role } : {},
|
|
3937
|
+
...row.requested_tenant_id !== null ? { requestedTenantId: row.requested_tenant_id } : {},
|
|
3938
|
+
...row.requested_tenant_role !== null ? { requestedTenantRole: row.requested_tenant_role } : {}
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
function isUniqueViolation2(error) {
|
|
3942
|
+
return typeof error === "object" && error !== null && error.code === "23505";
|
|
3943
|
+
}
|
|
3944
|
+
var PostgreSQLOidcIdentityClaimStore = class {
|
|
3945
|
+
constructor(options) {
|
|
3946
|
+
this.pool = options.pool;
|
|
3947
|
+
}
|
|
3948
|
+
async list(status) {
|
|
3949
|
+
const result = status ? await this.pool.query(
|
|
3950
|
+
`SELECT * FROM ${TABLE5} WHERE status = $1
|
|
3951
|
+
ORDER BY created_at ASC, id ASC`,
|
|
3952
|
+
[status]
|
|
3953
|
+
) : await this.pool.query(
|
|
3954
|
+
`SELECT * FROM ${TABLE5} ORDER BY created_at ASC, id ASC`
|
|
3955
|
+
);
|
|
3956
|
+
return result.rows.map(mapRow5);
|
|
3957
|
+
}
|
|
3958
|
+
async get(id) {
|
|
3959
|
+
const result = await this.pool.query(
|
|
3960
|
+
`SELECT * FROM ${TABLE5} WHERE id = $1`,
|
|
3961
|
+
[id]
|
|
3962
|
+
);
|
|
3963
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
3964
|
+
}
|
|
3965
|
+
async getPendingBySubject(providerId, issuer, subject, now) {
|
|
3966
|
+
const result = await this.pool.query(
|
|
3967
|
+
`SELECT * FROM ${TABLE5}
|
|
3968
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3
|
|
3969
|
+
AND status = 'pending' AND expires_at > $4`,
|
|
3970
|
+
[providerId, issuer, subject, now]
|
|
3971
|
+
);
|
|
3972
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
3973
|
+
}
|
|
3974
|
+
async create(input) {
|
|
3975
|
+
const now = /* @__PURE__ */ new Date();
|
|
3976
|
+
await this.pool.query(
|
|
3977
|
+
`UPDATE ${TABLE5}
|
|
3978
|
+
SET status = 'expired', updated_at = $1
|
|
3979
|
+
WHERE provider_id = $2 AND issuer = $3 AND subject = $4
|
|
3980
|
+
AND status = 'pending' AND expires_at <= $1`,
|
|
3981
|
+
[now, input.providerId, input.issuer, input.subject]
|
|
3982
|
+
);
|
|
3983
|
+
try {
|
|
3984
|
+
const result = await this.pool.query(
|
|
3985
|
+
`INSERT INTO ${TABLE5}
|
|
3986
|
+
(id, provider_id, issuer, subject, email, name, picture, status,
|
|
3987
|
+
created_at, updated_at, expires_at, source, invite_link_id,
|
|
3988
|
+
requested_user_id, requested_platform_role, requested_tenant_id,
|
|
3989
|
+
requested_tenant_role)
|
|
3990
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8, $8, $9,
|
|
3991
|
+
$10, $11, $12, $13, $14, $15)
|
|
3992
|
+
RETURNING *`,
|
|
3993
|
+
[
|
|
3994
|
+
input.id,
|
|
3995
|
+
input.providerId,
|
|
3996
|
+
input.issuer,
|
|
3997
|
+
input.subject,
|
|
3998
|
+
normalizeOidcIdentityEmail(input.email),
|
|
3999
|
+
input.name ?? null,
|
|
4000
|
+
input.picture ?? null,
|
|
4001
|
+
now,
|
|
4002
|
+
input.expiresAt,
|
|
4003
|
+
input.source ?? null,
|
|
4004
|
+
input.inviteLinkId ?? null,
|
|
4005
|
+
input.requestedUserId ?? null,
|
|
4006
|
+
input.requestedPlatformRole ?? null,
|
|
4007
|
+
input.requestedTenantId ?? null,
|
|
4008
|
+
input.requestedTenantRole ?? null
|
|
4009
|
+
]
|
|
4010
|
+
);
|
|
4011
|
+
return mapRow5(result.rows[0]);
|
|
4012
|
+
} catch (error) {
|
|
4013
|
+
if (isUniqueViolation2(error)) {
|
|
4014
|
+
throw new OidcIdentityClaimConflictError(
|
|
4015
|
+
input.providerId,
|
|
4016
|
+
input.issuer,
|
|
4017
|
+
input.subject
|
|
4018
|
+
);
|
|
4019
|
+
}
|
|
4020
|
+
throw error;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
async updatePending(id, updates, now) {
|
|
4024
|
+
const sets = [];
|
|
4025
|
+
const params = [];
|
|
4026
|
+
if (updates.email !== void 0) {
|
|
4027
|
+
params.push(normalizeOidcIdentityEmail(updates.email));
|
|
4028
|
+
sets.push(`email = $${params.length}`);
|
|
4029
|
+
}
|
|
4030
|
+
if (updates.name !== void 0) {
|
|
4031
|
+
params.push(updates.name);
|
|
4032
|
+
sets.push(`name = $${params.length}`);
|
|
4033
|
+
}
|
|
4034
|
+
if (updates.picture !== void 0) {
|
|
4035
|
+
params.push(updates.picture);
|
|
4036
|
+
sets.push(`picture = $${params.length}`);
|
|
4037
|
+
}
|
|
4038
|
+
if (sets.length === 0) return this.get(id);
|
|
4039
|
+
params.push(now);
|
|
4040
|
+
sets.push(`updated_at = $${params.length}`);
|
|
4041
|
+
params.push(id);
|
|
4042
|
+
const result = await this.pool.query(
|
|
4043
|
+
`UPDATE ${TABLE5} SET ${sets.join(", ")}
|
|
4044
|
+
WHERE id = $${params.length} AND status = 'pending'
|
|
4045
|
+
RETURNING *`,
|
|
4046
|
+
params
|
|
4047
|
+
);
|
|
4048
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4049
|
+
}
|
|
4050
|
+
async approve(id, approval, now) {
|
|
4051
|
+
const result = await this.pool.query(
|
|
4052
|
+
`UPDATE ${TABLE5}
|
|
4053
|
+
SET status = 'approved', reviewed_by = $1, reviewed_at = $2,
|
|
4054
|
+
linked_user_id = $3, updated_at = $2
|
|
4055
|
+
WHERE id = $4 AND status = 'pending'
|
|
4056
|
+
RETURNING *`,
|
|
4057
|
+
[approval.reviewedBy, now, approval.linkedUserId, id]
|
|
4058
|
+
);
|
|
4059
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4060
|
+
}
|
|
4061
|
+
async reject(id, reviewedBy, now) {
|
|
4062
|
+
const result = await this.pool.query(
|
|
4063
|
+
`UPDATE ${TABLE5}
|
|
4064
|
+
SET status = 'rejected', reviewed_by = $1, reviewed_at = $2,
|
|
4065
|
+
updated_at = $2
|
|
4066
|
+
WHERE id = $3 AND status = 'pending'
|
|
4067
|
+
RETURNING *`,
|
|
4068
|
+
[reviewedBy, now, id]
|
|
4069
|
+
);
|
|
4070
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4071
|
+
}
|
|
4072
|
+
async delete(id) {
|
|
4073
|
+
const result = await this.pool.query(
|
|
4074
|
+
`DELETE FROM ${TABLE5} WHERE id = $1`,
|
|
4075
|
+
[id]
|
|
4076
|
+
);
|
|
4077
|
+
return (result.rowCount ?? 0) > 0;
|
|
4078
|
+
}
|
|
4079
|
+
};
|
|
4080
|
+
|
|
4081
|
+
// src/stores/PostgreSQLInviteLinkStore.ts
|
|
4082
|
+
import { encrypt as encrypt5, safeDecrypt as safeDecrypt5 } from "@axiom-lattice/core";
|
|
4083
|
+
import {
|
|
4084
|
+
normalizeInviteLinkEmail
|
|
4085
|
+
} from "@axiom-lattice/protocols";
|
|
4086
|
+
var TABLE6 = "lattice_invite_links";
|
|
4087
|
+
function mapRow6(row) {
|
|
4088
|
+
return {
|
|
4089
|
+
id: row.id,
|
|
4090
|
+
scope: row.scope,
|
|
4091
|
+
token: safeDecrypt5(row.token_encrypted, `Invite link "${row.id}" token`),
|
|
4092
|
+
tokenHash: row.token_hash,
|
|
4093
|
+
...row.email !== null ? { email: row.email } : {},
|
|
4094
|
+
...row.platform_role !== null ? { platformRole: row.platform_role } : {},
|
|
4095
|
+
...row.tenant_id !== null ? { tenantId: row.tenant_id } : {},
|
|
4096
|
+
...row.tenant_role !== null ? { tenantRole: row.tenant_role } : {},
|
|
4097
|
+
...row.created_by !== null ? { createdBy: row.created_by } : {},
|
|
4098
|
+
createdAt: row.created_at,
|
|
4099
|
+
updatedAt: row.updated_at,
|
|
4100
|
+
expiresAt: row.expires_at,
|
|
4101
|
+
maxUses: Number(row.max_uses),
|
|
4102
|
+
usedCount: Number(row.used_count),
|
|
4103
|
+
...row.revoked_at !== null ? { revokedAt: row.revoked_at } : {}
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
var PostgreSQLInviteLinkStore = class {
|
|
4107
|
+
constructor(options) {
|
|
4108
|
+
this.options = options;
|
|
4109
|
+
}
|
|
4110
|
+
get pool() {
|
|
4111
|
+
return this.options.pool;
|
|
4112
|
+
}
|
|
4113
|
+
async list() {
|
|
4114
|
+
const result = await this.pool.query(
|
|
4115
|
+
`SELECT * FROM ${TABLE6} ORDER BY created_at DESC, id DESC`
|
|
4116
|
+
);
|
|
4117
|
+
return result.rows.map(mapRow6);
|
|
4118
|
+
}
|
|
4119
|
+
async get(id) {
|
|
4120
|
+
const result = await this.pool.query(
|
|
4121
|
+
`SELECT * FROM ${TABLE6} WHERE id = $1`,
|
|
4122
|
+
[id]
|
|
4123
|
+
);
|
|
4124
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4125
|
+
}
|
|
4126
|
+
async getByTokenHash(tokenHash) {
|
|
4127
|
+
const result = await this.pool.query(
|
|
4128
|
+
`SELECT * FROM ${TABLE6} WHERE token_hash = $1`,
|
|
4129
|
+
[tokenHash]
|
|
4130
|
+
);
|
|
4131
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4132
|
+
}
|
|
4133
|
+
async create(input) {
|
|
4134
|
+
const now = /* @__PURE__ */ new Date();
|
|
4135
|
+
const result = await this.pool.query(
|
|
4136
|
+
`INSERT INTO ${TABLE6}
|
|
4137
|
+
(id, scope, token_hash, token_encrypted, email, platform_role,
|
|
4138
|
+
tenant_id, tenant_role, created_by, created_at, updated_at,
|
|
4139
|
+
expires_at, max_uses, used_count)
|
|
4140
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $10, $11, $12, 0)
|
|
4141
|
+
RETURNING *`,
|
|
4142
|
+
[
|
|
4143
|
+
input.id,
|
|
4144
|
+
input.scope,
|
|
4145
|
+
input.tokenHash,
|
|
4146
|
+
encrypt5(input.token),
|
|
4147
|
+
input.email ? normalizeInviteLinkEmail(input.email) : null,
|
|
4148
|
+
input.platformRole ?? null,
|
|
4149
|
+
input.tenantId ?? null,
|
|
4150
|
+
input.tenantRole ?? null,
|
|
4151
|
+
input.createdBy ?? null,
|
|
4152
|
+
now,
|
|
4153
|
+
input.expiresAt,
|
|
4154
|
+
input.maxUses
|
|
4155
|
+
]
|
|
4156
|
+
);
|
|
4157
|
+
return mapRow6(result.rows[0]);
|
|
4158
|
+
}
|
|
4159
|
+
async consume(tokenHash, now) {
|
|
4160
|
+
const result = await this.pool.query(
|
|
4161
|
+
`UPDATE ${TABLE6}
|
|
4162
|
+
SET used_count = used_count + 1, updated_at = $1
|
|
4163
|
+
WHERE token_hash = $2
|
|
4164
|
+
AND revoked_at IS NULL
|
|
4165
|
+
AND expires_at > $1
|
|
4166
|
+
AND used_count < max_uses
|
|
4167
|
+
RETURNING *`,
|
|
4168
|
+
[now, tokenHash]
|
|
4169
|
+
);
|
|
4170
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4171
|
+
}
|
|
4172
|
+
async revoke(id, now) {
|
|
4173
|
+
const result = await this.pool.query(
|
|
4174
|
+
`UPDATE ${TABLE6}
|
|
4175
|
+
SET revoked_at = COALESCE(revoked_at, $1), updated_at = $1
|
|
4176
|
+
WHERE id = $2
|
|
4177
|
+
RETURNING *`,
|
|
4178
|
+
[now, id]
|
|
4179
|
+
);
|
|
4180
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4181
|
+
}
|
|
4182
|
+
};
|
|
4183
|
+
|
|
3496
4184
|
// src/stores/PostgreSQLWorkflowTrackingStore.ts
|
|
3497
4185
|
import { randomUUID } from "crypto";
|
|
3498
4186
|
import { Pool as Pool11 } from "pg";
|
|
@@ -5978,7 +6666,7 @@ var alterChannelInstallationsTable = {
|
|
|
5978
6666
|
};
|
|
5979
6667
|
|
|
5980
6668
|
// src/stores/PostgreSQLChannelInstallationStore.ts
|
|
5981
|
-
import { safeDecrypt as
|
|
6669
|
+
import { safeDecrypt as safeDecrypt6, encrypt as encrypt6 } from "@axiom-lattice/core";
|
|
5982
6670
|
var PostgreSQLChannelInstallationStore = class {
|
|
5983
6671
|
constructor(options) {
|
|
5984
6672
|
this.initialized = false;
|
|
@@ -6200,17 +6888,17 @@ var PostgreSQLChannelInstallationStore = class {
|
|
|
6200
6888
|
encryptSecrets(config) {
|
|
6201
6889
|
return {
|
|
6202
6890
|
...config,
|
|
6203
|
-
...typeof config.appSecret === "string" ? { appSecret:
|
|
6204
|
-
...typeof config.verificationToken === "string" ? { verificationToken:
|
|
6205
|
-
...typeof config.encryptKey === "string" ? { encryptKey:
|
|
6891
|
+
...typeof config.appSecret === "string" ? { appSecret: encrypt6(config.appSecret) } : {},
|
|
6892
|
+
...typeof config.verificationToken === "string" ? { verificationToken: encrypt6(config.verificationToken) } : {},
|
|
6893
|
+
...typeof config.encryptKey === "string" ? { encryptKey: encrypt6(config.encryptKey) } : {}
|
|
6206
6894
|
};
|
|
6207
6895
|
}
|
|
6208
6896
|
decryptSecrets(config) {
|
|
6209
6897
|
return {
|
|
6210
6898
|
...config,
|
|
6211
|
-
...typeof config.appSecret === "string" ? { appSecret:
|
|
6212
|
-
...typeof config.verificationToken === "string" ? { verificationToken:
|
|
6213
|
-
...typeof config.encryptKey === "string" ? { encryptKey:
|
|
6899
|
+
...typeof config.appSecret === "string" ? { appSecret: safeDecrypt6(config.appSecret) } : {},
|
|
6900
|
+
...typeof config.verificationToken === "string" ? { verificationToken: safeDecrypt6(config.verificationToken) } : {},
|
|
6901
|
+
...typeof config.encryptKey === "string" ? { encryptKey: safeDecrypt6(config.encryptKey) } : {}
|
|
6214
6902
|
};
|
|
6215
6903
|
}
|
|
6216
6904
|
};
|
|
@@ -6288,7 +6976,7 @@ var addApiKeyGrants = {
|
|
|
6288
6976
|
};
|
|
6289
6977
|
|
|
6290
6978
|
// src/stores/PostgreSQLA2AApiKeyStore.ts
|
|
6291
|
-
import { encrypt as
|
|
6979
|
+
import { encrypt as encrypt7, decrypt, safeDecrypt as safeDecrypt7 } from "@axiom-lattice/core";
|
|
6292
6980
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6293
6981
|
function generateApiKey() {
|
|
6294
6982
|
return `a2a_${randomUUID2().replace(/-/g, "")}`;
|
|
@@ -6296,7 +6984,7 @@ function generateApiKey() {
|
|
|
6296
6984
|
function mapRowToRecord(row) {
|
|
6297
6985
|
return {
|
|
6298
6986
|
id: row.id,
|
|
6299
|
-
key:
|
|
6987
|
+
key: safeDecrypt7(row.key_value),
|
|
6300
6988
|
tenantId: row.tenant_id,
|
|
6301
6989
|
projectId: row.project_id,
|
|
6302
6990
|
assistantIds: row.assistant_ids ?? void 0,
|
|
@@ -6404,7 +7092,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6404
7092
|
`INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, assistant_ids, grants, label)
|
|
6405
7093
|
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
|
|
6406
7094
|
[
|
|
6407
|
-
|
|
7095
|
+
encrypt7(key),
|
|
6408
7096
|
input.tenantId,
|
|
6409
7097
|
input.projectId,
|
|
6410
7098
|
input.assistantIds ? JSON.stringify(input.assistantIds) : null,
|
|
@@ -6473,7 +7161,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6473
7161
|
const key = generateApiKey();
|
|
6474
7162
|
const result = await this.pool.query(
|
|
6475
7163
|
`UPDATE lattice_a2a_api_keys SET key_value = $1, updated_at = NOW() WHERE id = $2 RETURNING *`,
|
|
6476
|
-
[
|
|
7164
|
+
[encrypt7(key), id]
|
|
6477
7165
|
);
|
|
6478
7166
|
if (result.rows.length === 0) throw new Error(`A2A API key not found: ${id}`);
|
|
6479
7167
|
const record = mapRowToRecord(result.rows[0]);
|
|
@@ -6742,7 +7430,7 @@ function isIdentity(value) {
|
|
|
6742
7430
|
function isStatus(value) {
|
|
6743
7431
|
return value === "draft" || value === "active" || value === "disabled";
|
|
6744
7432
|
}
|
|
6745
|
-
function
|
|
7433
|
+
function mapRow7(row) {
|
|
6746
7434
|
if (!isStatus(row.status) || !isIntegration(row.integration) || !isScope(row.scope) || !isFeatures(row.features) || !isAppearance(row.appearance) || !isIdentity(row.identity)) {
|
|
6747
7435
|
throw new Error(`Invalid agent web app row: ${row.id}`);
|
|
6748
7436
|
}
|
|
@@ -6814,7 +7502,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6814
7502
|
ORDER BY created_at DESC, id DESC`,
|
|
6815
7503
|
[tenantId, assistantId]
|
|
6816
7504
|
);
|
|
6817
|
-
return result.rows.map(
|
|
7505
|
+
return result.rows.map(mapRow7);
|
|
6818
7506
|
}
|
|
6819
7507
|
async getById(tenantId, webAppId) {
|
|
6820
7508
|
await this.ensureInitialized();
|
|
@@ -6823,7 +7511,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6823
7511
|
WHERE tenant_id = $1 AND id = $2`,
|
|
6824
7512
|
[tenantId, webAppId]
|
|
6825
7513
|
);
|
|
6826
|
-
return result.rows[0] ?
|
|
7514
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
6827
7515
|
}
|
|
6828
7516
|
async findById(webAppId) {
|
|
6829
7517
|
await this.ensureInitialized();
|
|
@@ -6832,7 +7520,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6832
7520
|
WHERE id = $1`,
|
|
6833
7521
|
[webAppId]
|
|
6834
7522
|
);
|
|
6835
|
-
return result.rows[0] ?
|
|
7523
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
6836
7524
|
}
|
|
6837
7525
|
async create(tenantId, input) {
|
|
6838
7526
|
await this.ensureInitialized();
|
|
@@ -6856,7 +7544,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6856
7544
|
input.identity ? JSON.stringify(input.identity) : null
|
|
6857
7545
|
]
|
|
6858
7546
|
);
|
|
6859
|
-
return
|
|
7547
|
+
return mapRow7(result.rows[0]);
|
|
6860
7548
|
}
|
|
6861
7549
|
async update(tenantId, webAppId, patch, options) {
|
|
6862
7550
|
await this.ensureInitialized();
|
|
@@ -6898,7 +7586,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6898
7586
|
RETURNING *`,
|
|
6899
7587
|
values
|
|
6900
7588
|
);
|
|
6901
|
-
return result.rows[0] ?
|
|
7589
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
6902
7590
|
}
|
|
6903
7591
|
async delete(tenantId, webAppId) {
|
|
6904
7592
|
await this.ensureInitialized();
|
|
@@ -9991,7 +10679,7 @@ var createChannelIdentityMappingTables = {
|
|
|
9991
10679
|
};
|
|
9992
10680
|
|
|
9993
10681
|
// src/migrations/connection_config_migrations.ts
|
|
9994
|
-
import { encrypt as
|
|
10682
|
+
import { encrypt as encrypt8, isEncryptedValue } from "@axiom-lattice/core";
|
|
9995
10683
|
var createConnectionConfigsTable = {
|
|
9996
10684
|
version: 160,
|
|
9997
10685
|
name: "create_connection_configs_table",
|
|
@@ -10063,7 +10751,7 @@ var reencryptLegacyConnectionSecrets = {
|
|
|
10063
10751
|
for (const field of secretFields) {
|
|
10064
10752
|
const value = config[field];
|
|
10065
10753
|
if (typeof value === "string" && value.length > 0 && !isEncryptedValue(value)) {
|
|
10066
|
-
config[field] =
|
|
10754
|
+
config[field] = encrypt8(value);
|
|
10067
10755
|
changed = true;
|
|
10068
10756
|
}
|
|
10069
10757
|
}
|
|
@@ -10079,6 +10767,254 @@ var reencryptLegacyConnectionSecrets = {
|
|
|
10079
10767
|
}
|
|
10080
10768
|
};
|
|
10081
10769
|
|
|
10770
|
+
// src/migrations/user_external_identity_migration.ts
|
|
10771
|
+
var createUserExternalIdentityTable = {
|
|
10772
|
+
version: 209,
|
|
10773
|
+
name: "create_user_external_identity",
|
|
10774
|
+
up: async (client) => {
|
|
10775
|
+
await client.query(`
|
|
10776
|
+
CREATE TABLE IF NOT EXISTS lattice_user_identities (
|
|
10777
|
+
user_id VARCHAR(255) NOT NULL,
|
|
10778
|
+
provider_id VARCHAR(255) NOT NULL,
|
|
10779
|
+
issuer TEXT NOT NULL,
|
|
10780
|
+
subject TEXT NOT NULL,
|
|
10781
|
+
email TEXT,
|
|
10782
|
+
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10783
|
+
PRIMARY KEY (provider_id, issuer, subject)
|
|
10784
|
+
)
|
|
10785
|
+
`);
|
|
10786
|
+
await client.query(`
|
|
10787
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_user_identities_user
|
|
10788
|
+
ON lattice_user_identities (user_id)
|
|
10789
|
+
`);
|
|
10790
|
+
},
|
|
10791
|
+
down: async (client) => {
|
|
10792
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_user_identities_user");
|
|
10793
|
+
await client.query("DROP TABLE IF EXISTS lattice_user_identities");
|
|
10794
|
+
}
|
|
10795
|
+
};
|
|
10796
|
+
|
|
10797
|
+
// src/migrations/user_email_lower_unique_migration.ts
|
|
10798
|
+
var createUserEmailLowerUniqueIndex = {
|
|
10799
|
+
version: 210,
|
|
10800
|
+
name: "create_user_email_lower_unique_index",
|
|
10801
|
+
up: async (client) => {
|
|
10802
|
+
await client.query(`
|
|
10803
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_lattice_users_email_lower
|
|
10804
|
+
ON lattice_users (lower(email))
|
|
10805
|
+
`);
|
|
10806
|
+
},
|
|
10807
|
+
down: async (client) => {
|
|
10808
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_users_email_lower");
|
|
10809
|
+
}
|
|
10810
|
+
};
|
|
10811
|
+
|
|
10812
|
+
// src/migrations/oidc_provider_config_migration.ts
|
|
10813
|
+
var createOidcProviderConfigsTable = {
|
|
10814
|
+
version: 211,
|
|
10815
|
+
name: "create_oidc_provider_configs",
|
|
10816
|
+
up: async (client) => {
|
|
10817
|
+
await client.query(`
|
|
10818
|
+
CREATE TABLE IF NOT EXISTS lattice_oidc_provider_configs (
|
|
10819
|
+
id VARCHAR(64) PRIMARY KEY,
|
|
10820
|
+
display_name TEXT NOT NULL,
|
|
10821
|
+
issuer TEXT NOT NULL,
|
|
10822
|
+
client_id TEXT NOT NULL,
|
|
10823
|
+
client_secret TEXT NOT NULL,
|
|
10824
|
+
scopes TEXT NOT NULL,
|
|
10825
|
+
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
10826
|
+
jit_provisioning BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10827
|
+
auto_link_email BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10828
|
+
default_tenant_id TEXT,
|
|
10829
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10830
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
10831
|
+
)
|
|
10832
|
+
`);
|
|
10833
|
+
},
|
|
10834
|
+
down: async (client) => {
|
|
10835
|
+
await client.query("DROP TABLE IF EXISTS lattice_oidc_provider_configs");
|
|
10836
|
+
}
|
|
10837
|
+
};
|
|
10838
|
+
|
|
10839
|
+
// src/migrations/platform_member_migration.ts
|
|
10840
|
+
var createPlatformMembersTable = {
|
|
10841
|
+
version: 212,
|
|
10842
|
+
name: "create_platform_members",
|
|
10843
|
+
up: async (client) => {
|
|
10844
|
+
await client.query(`
|
|
10845
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_members (
|
|
10846
|
+
user_id VARCHAR(255) PRIMARY KEY,
|
|
10847
|
+
role VARCHAR(16) NOT NULL,
|
|
10848
|
+
granted_by VARCHAR(255),
|
|
10849
|
+
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10850
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
10851
|
+
)
|
|
10852
|
+
`);
|
|
10853
|
+
},
|
|
10854
|
+
down: async (client) => {
|
|
10855
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_members");
|
|
10856
|
+
}
|
|
10857
|
+
};
|
|
10858
|
+
|
|
10859
|
+
// src/migrations/platform_invitation_migration.ts
|
|
10860
|
+
var createPlatformInvitationsTable = {
|
|
10861
|
+
version: 213,
|
|
10862
|
+
name: "create_platform_invitations",
|
|
10863
|
+
up: async (client) => {
|
|
10864
|
+
await client.query(`
|
|
10865
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_invitations (
|
|
10866
|
+
email VARCHAR(320) PRIMARY KEY,
|
|
10867
|
+
role VARCHAR(16) NOT NULL,
|
|
10868
|
+
invited_by VARCHAR(255),
|
|
10869
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10870
|
+
expires_at TIMESTAMPTZ,
|
|
10871
|
+
consumed_at TIMESTAMPTZ
|
|
10872
|
+
)
|
|
10873
|
+
`);
|
|
10874
|
+
},
|
|
10875
|
+
down: async (client) => {
|
|
10876
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_invitations");
|
|
10877
|
+
}
|
|
10878
|
+
};
|
|
10879
|
+
|
|
10880
|
+
// src/migrations/oidc_identity_claim_migration.ts
|
|
10881
|
+
var createOidcIdentityClaimsTable = {
|
|
10882
|
+
version: 214,
|
|
10883
|
+
name: "create_oidc_identity_claims",
|
|
10884
|
+
up: async (client) => {
|
|
10885
|
+
await client.query(`
|
|
10886
|
+
CREATE TABLE IF NOT EXISTS lattice_oidc_identity_claims (
|
|
10887
|
+
id UUID PRIMARY KEY,
|
|
10888
|
+
provider_id VARCHAR(64) NOT NULL,
|
|
10889
|
+
issuer TEXT NOT NULL,
|
|
10890
|
+
subject TEXT NOT NULL,
|
|
10891
|
+
email VARCHAR(320) NOT NULL,
|
|
10892
|
+
name TEXT,
|
|
10893
|
+
picture TEXT,
|
|
10894
|
+
status VARCHAR(16) NOT NULL,
|
|
10895
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10896
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10897
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
10898
|
+
reviewed_by VARCHAR(255),
|
|
10899
|
+
reviewed_at TIMESTAMPTZ,
|
|
10900
|
+
linked_user_id VARCHAR(255)
|
|
10901
|
+
)
|
|
10902
|
+
`);
|
|
10903
|
+
await client.query(`
|
|
10904
|
+
CREATE UNIQUE INDEX IF NOT EXISTS lattice_oidc_identity_claims_pending_subject
|
|
10905
|
+
ON lattice_oidc_identity_claims (provider_id, issuer, subject)
|
|
10906
|
+
WHERE status = 'pending'
|
|
10907
|
+
`);
|
|
10908
|
+
await client.query(`
|
|
10909
|
+
CREATE INDEX IF NOT EXISTS lattice_oidc_identity_claims_status_created
|
|
10910
|
+
ON lattice_oidc_identity_claims (status, created_at)
|
|
10911
|
+
`);
|
|
10912
|
+
},
|
|
10913
|
+
down: async (client) => {
|
|
10914
|
+
await client.query("DROP TABLE IF EXISTS lattice_oidc_identity_claims");
|
|
10915
|
+
}
|
|
10916
|
+
};
|
|
10917
|
+
|
|
10918
|
+
// src/migrations/invite_link_migration.ts
|
|
10919
|
+
var createInviteLinksTable = {
|
|
10920
|
+
version: 215,
|
|
10921
|
+
name: "create_invite_links",
|
|
10922
|
+
up: async (client) => {
|
|
10923
|
+
await client.query(`
|
|
10924
|
+
CREATE TABLE IF NOT EXISTS lattice_invite_links (
|
|
10925
|
+
id UUID PRIMARY KEY,
|
|
10926
|
+
scope VARCHAR(16) NOT NULL,
|
|
10927
|
+
token_hash VARCHAR(64) NOT NULL UNIQUE,
|
|
10928
|
+
token_encrypted TEXT NOT NULL,
|
|
10929
|
+
email VARCHAR(320),
|
|
10930
|
+
platform_role VARCHAR(16),
|
|
10931
|
+
tenant_id VARCHAR(255),
|
|
10932
|
+
tenant_role VARCHAR(16),
|
|
10933
|
+
created_by VARCHAR(255),
|
|
10934
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10935
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10936
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
10937
|
+
max_uses INTEGER NOT NULL,
|
|
10938
|
+
used_count INTEGER NOT NULL DEFAULT 0,
|
|
10939
|
+
revoked_at TIMESTAMPTZ
|
|
10940
|
+
)
|
|
10941
|
+
`);
|
|
10942
|
+
await client.query(`
|
|
10943
|
+
CREATE INDEX IF NOT EXISTS lattice_invite_links_created
|
|
10944
|
+
ON lattice_invite_links (created_at DESC)
|
|
10945
|
+
`);
|
|
10946
|
+
},
|
|
10947
|
+
down: async (client) => {
|
|
10948
|
+
await client.query("DROP TABLE IF EXISTS lattice_invite_links");
|
|
10949
|
+
}
|
|
10950
|
+
};
|
|
10951
|
+
|
|
10952
|
+
// src/migrations/oidc_identity_claim_invite_migration.ts
|
|
10953
|
+
var addOidcIdentityClaimInviteFields = {
|
|
10954
|
+
version: 216,
|
|
10955
|
+
name: "add_oidc_identity_claim_invite_fields",
|
|
10956
|
+
up: async (client) => {
|
|
10957
|
+
await client.query(`
|
|
10958
|
+
ALTER TABLE lattice_oidc_identity_claims
|
|
10959
|
+
ADD COLUMN IF NOT EXISTS source VARCHAR(32),
|
|
10960
|
+
ADD COLUMN IF NOT EXISTS invite_link_id UUID,
|
|
10961
|
+
ADD COLUMN IF NOT EXISTS requested_user_id VARCHAR(255),
|
|
10962
|
+
ADD COLUMN IF NOT EXISTS requested_platform_role VARCHAR(16),
|
|
10963
|
+
ADD COLUMN IF NOT EXISTS requested_tenant_id VARCHAR(255),
|
|
10964
|
+
ADD COLUMN IF NOT EXISTS requested_tenant_role VARCHAR(16)
|
|
10965
|
+
`);
|
|
10966
|
+
},
|
|
10967
|
+
down: async (client) => {
|
|
10968
|
+
await client.query(`
|
|
10969
|
+
ALTER TABLE lattice_oidc_identity_claims
|
|
10970
|
+
DROP COLUMN IF EXISTS source,
|
|
10971
|
+
DROP COLUMN IF EXISTS invite_link_id,
|
|
10972
|
+
DROP COLUMN IF EXISTS requested_user_id,
|
|
10973
|
+
DROP COLUMN IF EXISTS requested_platform_role,
|
|
10974
|
+
DROP COLUMN IF EXISTS requested_tenant_id,
|
|
10975
|
+
DROP COLUMN IF EXISTS requested_tenant_role
|
|
10976
|
+
`);
|
|
10977
|
+
}
|
|
10978
|
+
};
|
|
10979
|
+
|
|
10980
|
+
// src/migrations/platform_settings_migration.ts
|
|
10981
|
+
var createPlatformSettingsTable = {
|
|
10982
|
+
version: 217,
|
|
10983
|
+
name: "create_platform_settings",
|
|
10984
|
+
up: async (client) => {
|
|
10985
|
+
await client.query(`
|
|
10986
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_settings (
|
|
10987
|
+
id VARCHAR(32) PRIMARY KEY,
|
|
10988
|
+
document JSONB NOT NULL,
|
|
10989
|
+
version INTEGER NOT NULL,
|
|
10990
|
+
updated_by VARCHAR(255),
|
|
10991
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
10992
|
+
)
|
|
10993
|
+
`);
|
|
10994
|
+
},
|
|
10995
|
+
down: async (client) => {
|
|
10996
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_settings");
|
|
10997
|
+
}
|
|
10998
|
+
};
|
|
10999
|
+
|
|
11000
|
+
// src/migrations/oidc_provider_allowed_hosts_migration.ts
|
|
11001
|
+
var addOidcProviderAllowedHosts = {
|
|
11002
|
+
version: 218,
|
|
11003
|
+
name: "add_oidc_provider_allowed_hosts",
|
|
11004
|
+
up: async (client) => {
|
|
11005
|
+
await client.query(`
|
|
11006
|
+
ALTER TABLE lattice_oidc_provider_configs
|
|
11007
|
+
ADD COLUMN IF NOT EXISTS allowed_private_hosts JSONB
|
|
11008
|
+
`);
|
|
11009
|
+
},
|
|
11010
|
+
down: async (client) => {
|
|
11011
|
+
await client.query(`
|
|
11012
|
+
ALTER TABLE lattice_oidc_provider_configs
|
|
11013
|
+
DROP COLUMN IF EXISTS allowed_private_hosts
|
|
11014
|
+
`);
|
|
11015
|
+
}
|
|
11016
|
+
};
|
|
11017
|
+
|
|
10082
11018
|
// src/migrations/collection_migrations.ts
|
|
10083
11019
|
var createCollectionsTable = {
|
|
10084
11020
|
version: 137,
|
|
@@ -11144,7 +12080,7 @@ var PostgreSQLCapabilityBundleStore = class {
|
|
|
11144
12080
|
function isValidDate(value) {
|
|
11145
12081
|
return value instanceof Date && !Number.isNaN(value.getTime());
|
|
11146
12082
|
}
|
|
11147
|
-
function
|
|
12083
|
+
function mapRow8(row) {
|
|
11148
12084
|
if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || row.type !== "main" || typeof row.name !== "string" || !isValidDate(row.created_at) || !isValidDate(row.updated_at)) {
|
|
11149
12085
|
throw new Error("Invalid project room row");
|
|
11150
12086
|
}
|
|
@@ -11175,7 +12111,7 @@ var PostgreSQLProjectRoomStore = class {
|
|
|
11175
12111
|
RETURNING id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at`,
|
|
11176
12112
|
[input.id, input.tenantId, input.workspaceId, input.projectId, "main", input.name]
|
|
11177
12113
|
);
|
|
11178
|
-
return
|
|
12114
|
+
return mapRow8(result.rows[0]);
|
|
11179
12115
|
}
|
|
11180
12116
|
/** Finds a tenant-scoped project's main room, or returns null. */
|
|
11181
12117
|
async getMainRoom(tenantId, projectId) {
|
|
@@ -11183,7 +12119,7 @@ var PostgreSQLProjectRoomStore = class {
|
|
|
11183
12119
|
"SELECT id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at FROM lattice_project_rooms WHERE tenant_id = $1 AND project_id = $2 AND type = 'main'",
|
|
11184
12120
|
[tenantId, projectId]
|
|
11185
12121
|
);
|
|
11186
|
-
return result.rows[0] ?
|
|
12122
|
+
return result.rows[0] ? mapRow8(result.rows[0]) : null;
|
|
11187
12123
|
}
|
|
11188
12124
|
};
|
|
11189
12125
|
|
|
@@ -11209,7 +12145,7 @@ function isRole(value) {
|
|
|
11209
12145
|
function isStatus2(value) {
|
|
11210
12146
|
return value === "active" || value === "removed";
|
|
11211
12147
|
}
|
|
11212
|
-
function
|
|
12148
|
+
function mapRow9(row) {
|
|
11213
12149
|
if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.project_id !== "string" || typeof row.user_id !== "string" || !isRole(row.role) || !isStatus2(row.status) || !isDate(row.joined_at) || !isDate(row.updated_at)) throw new Error("Invalid project membership row");
|
|
11214
12150
|
return {
|
|
11215
12151
|
id: row.id,
|
|
@@ -11230,7 +12166,7 @@ function mapDuplicate(error) {
|
|
|
11230
12166
|
if (isDuplicate2(error)) throw new ProjectMembershipIdConflictError();
|
|
11231
12167
|
throw error;
|
|
11232
12168
|
}
|
|
11233
|
-
var
|
|
12169
|
+
var columns2 = "id, tenant_id, project_id, user_id, role, status, joined_at, updated_at";
|
|
11234
12170
|
var PostgreSQLProjectMembershipStore = class {
|
|
11235
12171
|
/** Creates a store using an externally managed pool; the pool is not migrated or closed. */
|
|
11236
12172
|
constructor(options) {
|
|
@@ -11238,13 +12174,13 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11238
12174
|
}
|
|
11239
12175
|
/** Lists memberships in stable joined-time and ID order. */
|
|
11240
12176
|
async list(tenantId, projectId) {
|
|
11241
|
-
const result = await this.pool.query(`SELECT ${
|
|
11242
|
-
return result.rows.map(
|
|
12177
|
+
const result = await this.pool.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`, [tenantId, projectId]);
|
|
12178
|
+
return result.rows.map(mapRow9);
|
|
11243
12179
|
}
|
|
11244
12180
|
/** Finds a membership by exact tenant, project, and user identity. */
|
|
11245
12181
|
async findByUser(tenantId, projectId, userId) {
|
|
11246
|
-
const result = await this.pool.query(`SELECT ${
|
|
11247
|
-
return result.rows[0] ?
|
|
12182
|
+
const result = await this.pool.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND user_id = $3`, [tenantId, projectId, userId]);
|
|
12183
|
+
return result.rows[0] ? mapRow9(result.rows[0]) : null;
|
|
11248
12184
|
}
|
|
11249
12185
|
/** Inserts a membership and maps database uniqueness errors to stable typed errors. */
|
|
11250
12186
|
async create(input) {
|
|
@@ -11252,8 +12188,8 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11252
12188
|
try {
|
|
11253
12189
|
await client.query("BEGIN");
|
|
11254
12190
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11255
|
-
const result = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${
|
|
11256
|
-
const membership =
|
|
12191
|
+
const result = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns2}`, [input.id, input.tenantId, input.projectId, input.userId, input.role, input.status]);
|
|
12192
|
+
const membership = mapRow9(result.rows[0]);
|
|
11257
12193
|
await client.query("COMMIT");
|
|
11258
12194
|
return membership;
|
|
11259
12195
|
} catch (error) {
|
|
@@ -11272,16 +12208,16 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11272
12208
|
try {
|
|
11273
12209
|
await client.query("BEGIN");
|
|
11274
12210
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11275
|
-
const existingResult = await client.query(`SELECT ${
|
|
12211
|
+
const existingResult = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 FOR UPDATE`, [input.tenantId, input.projectId]);
|
|
11276
12212
|
if (existingResult.rows.length > 0) {
|
|
11277
|
-
const existing = existingResult.rows.map(
|
|
12213
|
+
const existing = existingResult.rows.map(mapRow9).find((item) => item.userId === input.userId && item.role === "owner" && item.status === "active");
|
|
11278
12214
|
await client.query("COMMIT");
|
|
11279
12215
|
return existing ? { kind: "existing", membership: existing } : { kind: "already_initialized" };
|
|
11280
12216
|
}
|
|
11281
12217
|
const idResult = await client.query("SELECT id FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2", [input.tenantId, input.id]);
|
|
11282
12218
|
if (idResult.rows.length > 0) throw new ProjectMembershipIdConflictError();
|
|
11283
|
-
const inserted = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, 'owner', 'active', date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${
|
|
11284
|
-
const membership =
|
|
12219
|
+
const inserted = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, 'owner', 'active', date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns2}`, [input.id, input.tenantId, input.projectId, input.userId]);
|
|
12220
|
+
const membership = mapRow9(inserted.rows[0]);
|
|
11285
12221
|
await client.query("COMMIT");
|
|
11286
12222
|
return { kind: "created", membership };
|
|
11287
12223
|
} catch (error) {
|
|
@@ -11313,16 +12249,16 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11313
12249
|
return { kind: "not_found" };
|
|
11314
12250
|
}
|
|
11315
12251
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${tenantId}:${identity.rows[0].project_id}`]);
|
|
11316
|
-
const activeRows = await client.query(`SELECT ${
|
|
11317
|
-
const lockedMemberships = activeRows.rows.map(
|
|
12252
|
+
const activeRows = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND status = 'active' FOR UPDATE`, [tenantId, identity.rows[0].project_id]);
|
|
12253
|
+
const lockedMemberships = activeRows.rows.map(mapRow9);
|
|
11318
12254
|
let current = lockedMemberships.find((membership) => membership.id === id);
|
|
11319
12255
|
if (!current) {
|
|
11320
|
-
const target = await client.query(`SELECT ${
|
|
12256
|
+
const target = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`, [tenantId, id]);
|
|
11321
12257
|
if (!target.rows[0]) {
|
|
11322
12258
|
await client.query("COMMIT");
|
|
11323
12259
|
return { kind: "not_found" };
|
|
11324
12260
|
}
|
|
11325
|
-
current =
|
|
12261
|
+
current = mapRow9(target.rows[0]);
|
|
11326
12262
|
}
|
|
11327
12263
|
if (current.updatedAt.getTime() !== expected.getTime()) {
|
|
11328
12264
|
await client.query("COMMIT");
|
|
@@ -11335,13 +12271,13 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11335
12271
|
return { kind: "last_owner" };
|
|
11336
12272
|
}
|
|
11337
12273
|
}
|
|
11338
|
-
const result = await client.query(remove ? `UPDATE lattice_project_memberships SET status = 'removed', updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${
|
|
12274
|
+
const result = await client.query(remove ? `UPDATE lattice_project_memberships SET status = 'removed', updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns2}` : `UPDATE lattice_project_memberships SET role = $4, updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns2}`, remove ? [tenantId, id, expected] : [tenantId, id, expected, role]);
|
|
11339
12275
|
if (!result.rows[0]) {
|
|
11340
12276
|
await client.query("COMMIT");
|
|
11341
12277
|
return { kind: "conflict" };
|
|
11342
12278
|
}
|
|
11343
12279
|
await client.query("COMMIT");
|
|
11344
|
-
return { kind: remove ? "removed" : "updated", membership:
|
|
12280
|
+
return { kind: remove ? "removed" : "updated", membership: mapRow9(result.rows[0]) };
|
|
11345
12281
|
} catch (error) {
|
|
11346
12282
|
try {
|
|
11347
12283
|
await client.query("ROLLBACK");
|
|
@@ -11355,7 +12291,7 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11355
12291
|
};
|
|
11356
12292
|
|
|
11357
12293
|
// src/stores/PostgreSQLProjectBotMembershipStore.ts
|
|
11358
|
-
var
|
|
12294
|
+
var columns3 = "id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at";
|
|
11359
12295
|
var ProjectBotMembershipIdConflictError = class extends Error {
|
|
11360
12296
|
/** Creates an accurate tenant-scoped membership ID collision error. */
|
|
11361
12297
|
constructor(tenantId, id) {
|
|
@@ -11372,7 +12308,7 @@ function isRole2(value) {
|
|
|
11372
12308
|
function isStatus3(value) {
|
|
11373
12309
|
return value === "active" || value === "paused" || value === "removed";
|
|
11374
12310
|
}
|
|
11375
|
-
function
|
|
12311
|
+
function mapRow10(row) {
|
|
11376
12312
|
if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || typeof row.room_id !== "string" || typeof row.assistant_id !== "string" || !isRole2(row.role) || typeof row.title !== "string" || row.responsibility !== null && typeof row.responsibility !== "string" || typeof row.mention_name !== "string" || !isStatus3(row.status) || typeof row.room_thread_id !== "string" || !isDate2(row.joined_at) || !isDate2(row.updated_at)) {
|
|
11377
12313
|
throw new Error("Invalid project bot membership row");
|
|
11378
12314
|
}
|
|
@@ -11393,11 +12329,11 @@ function mapRow4(row) {
|
|
|
11393
12329
|
updatedAt: row.updated_at
|
|
11394
12330
|
};
|
|
11395
12331
|
}
|
|
11396
|
-
function
|
|
12332
|
+
function isUniqueViolation3(error) {
|
|
11397
12333
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
11398
12334
|
}
|
|
11399
12335
|
function constraintConflict(error) {
|
|
11400
|
-
if (!
|
|
12336
|
+
if (!isUniqueViolation3(error) || typeof error.constraint !== "string") return void 0;
|
|
11401
12337
|
if (error.constraint === "uq_lattice_project_bot_memberships_coordinator") return "coordinator_conflict";
|
|
11402
12338
|
if (error.constraint === "uq_lattice_project_bot_memberships_mention") return "mention_conflict";
|
|
11403
12339
|
return void 0;
|
|
@@ -11416,26 +12352,26 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11416
12352
|
/** Lists retained memberships in stable join order. */
|
|
11417
12353
|
async list(tenantId, projectId) {
|
|
11418
12354
|
const result = await this.pool.query(
|
|
11419
|
-
`SELECT ${
|
|
12355
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`,
|
|
11420
12356
|
[tenantId, projectId]
|
|
11421
12357
|
);
|
|
11422
|
-
return result.rows.map(
|
|
12358
|
+
return result.rows.map(mapRow10);
|
|
11423
12359
|
}
|
|
11424
12360
|
/** Finds a membership by tenant-scoped ID. */
|
|
11425
12361
|
async findById(tenantId, id) {
|
|
11426
12362
|
const result = await this.pool.query(
|
|
11427
|
-
`SELECT ${
|
|
12363
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2`,
|
|
11428
12364
|
[tenantId, id]
|
|
11429
12365
|
);
|
|
11430
|
-
return result.rows[0] ?
|
|
12366
|
+
return result.rows[0] ? mapRow10(result.rows[0]) : null;
|
|
11431
12367
|
}
|
|
11432
12368
|
/** Finds an assistant's durable membership in a tenant-scoped project. */
|
|
11433
12369
|
async findByAssistant(tenantId, projectId, assistantId) {
|
|
11434
12370
|
const result = await this.pool.query(
|
|
11435
|
-
`SELECT ${
|
|
12371
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3`,
|
|
11436
12372
|
[tenantId, projectId, assistantId]
|
|
11437
12373
|
);
|
|
11438
|
-
return result.rows[0] ?
|
|
12374
|
+
return result.rows[0] ? mapRow10(result.rows[0]) : null;
|
|
11439
12375
|
}
|
|
11440
12376
|
/** Inserts a new membership or reactivates the assistant's durable membership atomically. */
|
|
11441
12377
|
async save(input) {
|
|
@@ -11444,17 +12380,17 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11444
12380
|
await client.query("BEGIN");
|
|
11445
12381
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11446
12382
|
const existingResult = await client.query(
|
|
11447
|
-
`SELECT ${
|
|
12383
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3 FOR UPDATE`,
|
|
11448
12384
|
[input.tenantId, input.projectId, input.assistantId]
|
|
11449
12385
|
);
|
|
11450
|
-
const existing = existingResult.rows[0] ?
|
|
12386
|
+
const existing = existingResult.rows[0] ? mapRow10(existingResult.rows[0]) : void 0;
|
|
11451
12387
|
let result;
|
|
11452
12388
|
if (existing) {
|
|
11453
12389
|
result = await client.query(
|
|
11454
12390
|
`UPDATE lattice_project_bot_memberships
|
|
11455
12391
|
SET role = $3, title = $4, responsibility = $5, mention_name = $6, status = 'active',
|
|
11456
12392
|
updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
|
|
11457
|
-
WHERE tenant_id = $1 AND id = $2 RETURNING ${
|
|
12393
|
+
WHERE tenant_id = $1 AND id = $2 RETURNING ${columns3}`,
|
|
11458
12394
|
[input.tenantId, existing.id, input.role, input.title, input.responsibility ?? null, input.mentionName]
|
|
11459
12395
|
);
|
|
11460
12396
|
} else {
|
|
@@ -11467,7 +12403,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11467
12403
|
`INSERT INTO lattice_project_bot_memberships
|
|
11468
12404
|
(id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at)
|
|
11469
12405
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'active', $11, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp()))
|
|
11470
|
-
RETURNING ${
|
|
12406
|
+
RETURNING ${columns3}`,
|
|
11471
12407
|
[
|
|
11472
12408
|
input.id,
|
|
11473
12409
|
input.tenantId,
|
|
@@ -11483,14 +12419,14 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11483
12419
|
]
|
|
11484
12420
|
);
|
|
11485
12421
|
}
|
|
11486
|
-
const membership =
|
|
12422
|
+
const membership = mapRow10(result.rows[0]);
|
|
11487
12423
|
await client.query("COMMIT");
|
|
11488
12424
|
return { kind: existing === void 0 ? "created" : existing.status === "removed" ? "reactivated" : "updated", membership };
|
|
11489
12425
|
} catch (error) {
|
|
11490
12426
|
await rollback(client);
|
|
11491
12427
|
const conflict = constraintConflict(error);
|
|
11492
12428
|
if (conflict) return { kind: conflict };
|
|
11493
|
-
if (
|
|
12429
|
+
if (isUniqueViolation3(error) && error.constraint === "lattice_project_bot_memberships_pkey") {
|
|
11494
12430
|
throw new ProjectBotMembershipIdConflictError(input.tenantId, input.id);
|
|
11495
12431
|
}
|
|
11496
12432
|
throw error;
|
|
@@ -11514,14 +12450,14 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11514
12450
|
if (typeof identity.rows[0].project_id !== "string") throw new Error("Invalid project bot membership row");
|
|
11515
12451
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${identity.rows[0].project_id}`]);
|
|
11516
12452
|
const locked = await client.query(
|
|
11517
|
-
`SELECT ${
|
|
12453
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`,
|
|
11518
12454
|
[input.tenantId, input.id]
|
|
11519
12455
|
);
|
|
11520
12456
|
if (!locked.rows[0]) {
|
|
11521
12457
|
await client.query("COMMIT");
|
|
11522
12458
|
return { kind: "not_found" };
|
|
11523
12459
|
}
|
|
11524
|
-
const existing =
|
|
12460
|
+
const existing = mapRow10(locked.rows[0]);
|
|
11525
12461
|
if (existing.updatedAt.getTime() !== input.expectedUpdatedAt.getTime()) {
|
|
11526
12462
|
await client.query("COMMIT");
|
|
11527
12463
|
return { kind: "conflict" };
|
|
@@ -11532,7 +12468,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11532
12468
|
SET role = $4, title = $5, responsibility = $6, mention_name = $7, status = $8,
|
|
11533
12469
|
updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
|
|
11534
12470
|
WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3
|
|
11535
|
-
RETURNING ${
|
|
12471
|
+
RETURNING ${columns3}`,
|
|
11536
12472
|
[
|
|
11537
12473
|
input.tenantId,
|
|
11538
12474
|
input.id,
|
|
@@ -11548,7 +12484,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11548
12484
|
await client.query("COMMIT");
|
|
11549
12485
|
return { kind: "conflict" };
|
|
11550
12486
|
}
|
|
11551
|
-
const membership =
|
|
12487
|
+
const membership = mapRow10(updated.rows[0]);
|
|
11552
12488
|
await client.query("COMMIT");
|
|
11553
12489
|
return { kind: "updated", membership };
|
|
11554
12490
|
} catch (error) {
|
|
@@ -11563,7 +12499,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11563
12499
|
};
|
|
11564
12500
|
|
|
11565
12501
|
// src/stores/PostgreSQLProjectRoomMessageStore.ts
|
|
11566
|
-
var
|
|
12502
|
+
var columns4 = "id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at";
|
|
11567
12503
|
var ProjectRoomMessageIdConflictError = class extends Error {
|
|
11568
12504
|
/** Creates an accurate tenant-scoped message ID collision error. */
|
|
11569
12505
|
constructor(tenantId, id) {
|
|
@@ -11604,7 +12540,7 @@ function mapMention(value) {
|
|
|
11604
12540
|
function isSource(value) {
|
|
11605
12541
|
return value === "user" || value === "agent" || value === "task" || value === "routine" || value === "system";
|
|
11606
12542
|
}
|
|
11607
|
-
function
|
|
12543
|
+
function mapRow11(row) {
|
|
11608
12544
|
const author = mapAuthor(row.author);
|
|
11609
12545
|
const content = isRecord4(row.content) && row.content.type === "text" && typeof row.content.text === "string" && hasOnlyKeys(row.content, ["type", "text"]) ? { type: "text", text: row.content.text } : void 0;
|
|
11610
12546
|
const mentions = Array.isArray(row.mentions) ? row.mentions.map(mapMention) : void 0;
|
|
@@ -11631,7 +12567,7 @@ function mapRowsTolerant(rows, scope) {
|
|
|
11631
12567
|
const messages = [];
|
|
11632
12568
|
for (const row of rows) {
|
|
11633
12569
|
try {
|
|
11634
|
-
messages.push(
|
|
12570
|
+
messages.push(mapRow11(row));
|
|
11635
12571
|
} catch (error) {
|
|
11636
12572
|
console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
|
|
11637
12573
|
tenantId: scope.tenantId,
|
|
@@ -11643,7 +12579,7 @@ function mapRowsTolerant(rows, scope) {
|
|
|
11643
12579
|
}
|
|
11644
12580
|
return messages;
|
|
11645
12581
|
}
|
|
11646
|
-
function
|
|
12582
|
+
function isUniqueViolation4(error) {
|
|
11647
12583
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
11648
12584
|
}
|
|
11649
12585
|
var PostgreSQLProjectRoomMessageStore = class {
|
|
@@ -11658,15 +12594,15 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11658
12594
|
`INSERT INTO lattice_project_room_messages
|
|
11659
12595
|
(id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at)
|
|
11660
12596
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
|
|
11661
|
-
RETURNING ${
|
|
12597
|
+
RETURNING ${columns4}`,
|
|
11662
12598
|
this.parameters(input)
|
|
11663
12599
|
);
|
|
11664
|
-
return
|
|
12600
|
+
return mapRow11(result.rows[0]);
|
|
11665
12601
|
} catch (error) {
|
|
11666
|
-
if (
|
|
12602
|
+
if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
|
|
11667
12603
|
throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
|
|
11668
12604
|
}
|
|
11669
|
-
if (
|
|
12605
|
+
if (isUniqueViolation4(error) && error.constraint === "uq_lattice_project_room_messages_idempotency") {
|
|
11670
12606
|
throw new DuplicateProjectRoomMessageIdempotencyKeyError(input.tenantId, input.roomId, input.idempotencyKey ?? "");
|
|
11671
12607
|
}
|
|
11672
12608
|
throw error;
|
|
@@ -11681,12 +12617,12 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11681
12617
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
|
|
11682
12618
|
ON CONFLICT (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL
|
|
11683
12619
|
DO UPDATE SET idempotency_key = lattice_project_room_messages.idempotency_key
|
|
11684
|
-
RETURNING ${
|
|
12620
|
+
RETURNING ${columns4}`,
|
|
11685
12621
|
this.parameters(input)
|
|
11686
12622
|
);
|
|
11687
|
-
return
|
|
12623
|
+
return mapRow11(result.rows[0]);
|
|
11688
12624
|
} catch (error) {
|
|
11689
|
-
if (
|
|
12625
|
+
if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
|
|
11690
12626
|
throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
|
|
11691
12627
|
}
|
|
11692
12628
|
throw error;
|
|
@@ -11699,12 +12635,12 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11699
12635
|
}
|
|
11700
12636
|
const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
|
|
11701
12637
|
const result = input.before ? await this.pool.query(
|
|
11702
|
-
`SELECT ${
|
|
12638
|
+
`SELECT ${columns4} FROM lattice_project_room_messages
|
|
11703
12639
|
WHERE tenant_id = $1 AND room_id = $2 AND (created_at < $3 OR (created_at = $3 AND id < $4))
|
|
11704
12640
|
ORDER BY created_at DESC, id DESC LIMIT $5`,
|
|
11705
12641
|
[input.tenantId, input.roomId, input.before.createdAt, input.before.id, limit]
|
|
11706
12642
|
) : await this.pool.query(
|
|
11707
|
-
`SELECT ${
|
|
12643
|
+
`SELECT ${columns4} FROM lattice_project_room_messages
|
|
11708
12644
|
WHERE tenant_id = $1 AND room_id = $2 ORDER BY created_at DESC, id DESC LIMIT $3`,
|
|
11709
12645
|
[input.tenantId, input.roomId, limit]
|
|
11710
12646
|
);
|
|
@@ -11713,10 +12649,10 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11713
12649
|
/** Finds a message by tenant-scoped ID. */
|
|
11714
12650
|
async findById(tenantId, id) {
|
|
11715
12651
|
const result = await this.pool.query(
|
|
11716
|
-
`SELECT ${
|
|
12652
|
+
`SELECT ${columns4} FROM lattice_project_room_messages WHERE tenant_id = $1 AND id = $2`,
|
|
11717
12653
|
[tenantId, id]
|
|
11718
12654
|
);
|
|
11719
|
-
return result.rows[0] ?
|
|
12655
|
+
return result.rows[0] ? mapRow11(result.rows[0]) : null;
|
|
11720
12656
|
}
|
|
11721
12657
|
/** Counts messages created strictly after a horizon, optionally excluding one human author. */
|
|
11722
12658
|
async countAfter(input) {
|
|
@@ -11774,7 +12710,7 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11774
12710
|
};
|
|
11775
12711
|
|
|
11776
12712
|
// src/stores/PostgreSQLProjectRoomReadStateStore.ts
|
|
11777
|
-
function
|
|
12713
|
+
function mapRow12(row) {
|
|
11778
12714
|
if (typeof row.tenant_id !== "string" || typeof row.room_id !== "string" || typeof row.user_id !== "string" || !(row.last_read_at instanceof Date) || Number.isNaN(row.last_read_at.getTime()) || !(row.updated_at instanceof Date) || Number.isNaN(row.updated_at.getTime())) {
|
|
11779
12715
|
throw new Error("Invalid project room read state row");
|
|
11780
12716
|
}
|
|
@@ -11792,7 +12728,7 @@ var PostgreSQLProjectRoomReadStateStore = class {
|
|
|
11792
12728
|
FROM lattice_project_room_read_states WHERE tenant_id = $1 AND room_id = $2 AND user_id = $3`,
|
|
11793
12729
|
[tenantId, roomId, userId]
|
|
11794
12730
|
);
|
|
11795
|
-
return result.rows[0] ?
|
|
12731
|
+
return result.rows[0] ? mapRow12(result.rows[0]) : null;
|
|
11796
12732
|
}
|
|
11797
12733
|
/** Upserts the marker monotonically using GREATEST so older reports never regress it. */
|
|
11798
12734
|
async markRead(input) {
|
|
@@ -11808,14 +12744,14 @@ var PostgreSQLProjectRoomReadStateStore = class {
|
|
|
11808
12744
|
RETURNING tenant_id, room_id, user_id, last_read_at, updated_at`,
|
|
11809
12745
|
[input.tenantId, input.roomId, input.userId, input.lastReadAt]
|
|
11810
12746
|
);
|
|
11811
|
-
return
|
|
12747
|
+
return mapRow12(result.rows[0]);
|
|
11812
12748
|
}
|
|
11813
12749
|
};
|
|
11814
12750
|
|
|
11815
12751
|
// src/stores/PostgreSQLUserPushSubscriptionStore.ts
|
|
11816
12752
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
11817
|
-
var
|
|
11818
|
-
function
|
|
12753
|
+
var columns5 = "id, tenant_id, user_id, endpoint, p256dh, auth, user_agent, created_at";
|
|
12754
|
+
function mapRow13(row) {
|
|
11819
12755
|
if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.user_id !== "string" || typeof row.endpoint !== "string" || typeof row.p256dh !== "string" || typeof row.auth !== "string" || !(row.created_at instanceof Date) || Number.isNaN(row.created_at.getTime())) {
|
|
11820
12756
|
throw new Error("Invalid user push subscription row");
|
|
11821
12757
|
}
|
|
@@ -11837,22 +12773,22 @@ var PostgreSQLUserPushSubscriptionStore = class {
|
|
|
11837
12773
|
/** Lists every subscription for a user. */
|
|
11838
12774
|
async list(tenantId, userId) {
|
|
11839
12775
|
const result = await this.pool.query(
|
|
11840
|
-
`SELECT ${
|
|
12776
|
+
`SELECT ${columns5} FROM lattice_user_push_subscriptions WHERE tenant_id = $1 AND user_id = $2`,
|
|
11841
12777
|
[tenantId, userId]
|
|
11842
12778
|
);
|
|
11843
|
-
return result.rows.map(
|
|
12779
|
+
return result.rows.map(mapRow13);
|
|
11844
12780
|
}
|
|
11845
12781
|
/** Upserts a subscription keyed by tenant + user + endpoint. */
|
|
11846
12782
|
async save(input) {
|
|
11847
12783
|
const result = await this.pool.query(
|
|
11848
|
-
`INSERT INTO lattice_user_push_subscriptions (${
|
|
12784
|
+
`INSERT INTO lattice_user_push_subscriptions (${columns5})
|
|
11849
12785
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
|
|
11850
12786
|
ON CONFLICT (tenant_id, user_id, endpoint) DO UPDATE
|
|
11851
12787
|
SET p256dh = EXCLUDED.p256dh, auth = EXCLUDED.auth, user_agent = EXCLUDED.user_agent
|
|
11852
|
-
RETURNING ${
|
|
12788
|
+
RETURNING ${columns5}`,
|
|
11853
12789
|
[randomUUID5(), input.tenantId, input.userId, input.endpoint, input.keys.p256dh, input.keys.auth, input.userAgent ?? null]
|
|
11854
12790
|
);
|
|
11855
|
-
return
|
|
12791
|
+
return mapRow13(result.rows[0]);
|
|
11856
12792
|
}
|
|
11857
12793
|
/** Deletes one subscription by endpoint. */
|
|
11858
12794
|
async delete(tenantId, userId, endpoint) {
|
|
@@ -11866,7 +12802,7 @@ var PostgreSQLUserPushSubscriptionStore = class {
|
|
|
11866
12802
|
|
|
11867
12803
|
// src/stores/PostgreSQLModelProviderStore.ts
|
|
11868
12804
|
import { Pool as Pool27 } from "pg";
|
|
11869
|
-
import { encrypt as
|
|
12805
|
+
import { encrypt as encrypt9, safeDecrypt as safeDecrypt8 } from "@axiom-lattice/core";
|
|
11870
12806
|
function extraOrUndefined(value) {
|
|
11871
12807
|
return value && Object.keys(value).length > 0 ? value : void 0;
|
|
11872
12808
|
}
|
|
@@ -11990,7 +12926,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
11990
12926
|
await this.ensureInitialized();
|
|
11991
12927
|
const now = /* @__PURE__ */ new Date();
|
|
11992
12928
|
const nowString = now.toISOString();
|
|
11993
|
-
const apiKeyEnc = data.apiKey ?
|
|
12929
|
+
const apiKeyEnc = data.apiKey ? encrypt9(data.apiKey) : null;
|
|
11994
12930
|
const apiKeyHint = data.apiKey ? data.apiKey.slice(-4) : null;
|
|
11995
12931
|
await this.pool.query(
|
|
11996
12932
|
`
|
|
@@ -12066,7 +13002,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
12066
13002
|
updateData.api_key_enc = null;
|
|
12067
13003
|
updateData.api_key_hint = null;
|
|
12068
13004
|
} else if (updates.apiKey) {
|
|
12069
|
-
updateData.api_key_enc =
|
|
13005
|
+
updateData.api_key_enc = encrypt9(updates.apiKey);
|
|
12070
13006
|
updateData.api_key_hint = updates.apiKey.slice(-4);
|
|
12071
13007
|
}
|
|
12072
13008
|
if (updates.enabled !== void 0) {
|
|
@@ -12386,7 +13322,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
12386
13322
|
mapRowToEntry(row) {
|
|
12387
13323
|
let apiKey;
|
|
12388
13324
|
if (row.api_key_enc) {
|
|
12389
|
-
apiKey =
|
|
13325
|
+
apiKey = safeDecrypt8(row.api_key_enc);
|
|
12390
13326
|
}
|
|
12391
13327
|
return {
|
|
12392
13328
|
id: row.id,
|
|
@@ -12639,7 +13575,7 @@ var PostgreSQLOutboxStore = class {
|
|
|
12639
13575
|
};
|
|
12640
13576
|
|
|
12641
13577
|
// src/stores/PostgreSQLPluginDeploymentStore.ts
|
|
12642
|
-
var
|
|
13578
|
+
var columns6 = "id, tenant_id, plugin_type, plugin_version, mode, status, provider, sandbox_name, port, endpoint, image_ref, idle_timeout_ms, last_health_at, last_error, created_at, updated_at";
|
|
12643
13579
|
var statuses = [
|
|
12644
13580
|
"requested",
|
|
12645
13581
|
"provisioning",
|
|
@@ -12652,7 +13588,7 @@ var statuses = [
|
|
|
12652
13588
|
function isStatus4(value) {
|
|
12653
13589
|
return typeof value === "string" && statuses.includes(value);
|
|
12654
13590
|
}
|
|
12655
|
-
function
|
|
13591
|
+
function mapRow14(row) {
|
|
12656
13592
|
if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.plugin_type !== "string" || typeof row.plugin_version !== "string" || row.mode !== "onDemand" && row.mode !== "alwaysOn" || !isStatus4(row.status) || typeof row.provider !== "string" || typeof row.sandbox_name !== "string" || typeof row.port !== "number" || typeof row.idle_timeout_ms !== "number" || !(row.created_at instanceof Date) || Number.isNaN(row.created_at.getTime()) || !(row.updated_at instanceof Date) || Number.isNaN(row.updated_at.getTime())) {
|
|
12657
13593
|
throw new Error("Invalid plugin deployment row");
|
|
12658
13594
|
}
|
|
@@ -12683,23 +13619,23 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12683
13619
|
/** Lists every deployment for a tenant, ordered by plugin type. */
|
|
12684
13620
|
async listByTenant(tenantId) {
|
|
12685
13621
|
const result = await this.pool.query(
|
|
12686
|
-
`SELECT ${
|
|
13622
|
+
`SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 ORDER BY plugin_type`,
|
|
12687
13623
|
[tenantId]
|
|
12688
13624
|
);
|
|
12689
|
-
return result.rows.map(
|
|
13625
|
+
return result.rows.map(mapRow14);
|
|
12690
13626
|
}
|
|
12691
13627
|
/** Returns the deployment for a tenant + plugin type, or undefined. */
|
|
12692
13628
|
async get(tenantId, pluginType) {
|
|
12693
13629
|
const result = await this.pool.query(
|
|
12694
|
-
`SELECT ${
|
|
13630
|
+
`SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 AND plugin_type = $2`,
|
|
12695
13631
|
[tenantId, pluginType]
|
|
12696
13632
|
);
|
|
12697
|
-
return result.rows[0] ?
|
|
13633
|
+
return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
|
|
12698
13634
|
}
|
|
12699
13635
|
/** Upserts a deployment keyed by tenant + plugin type, preserving the original id and createdAt. */
|
|
12700
13636
|
async upsert(deployment) {
|
|
12701
13637
|
const result = await this.pool.query(
|
|
12702
|
-
`INSERT INTO lattice_plugin_deployments (${
|
|
13638
|
+
`INSERT INTO lattice_plugin_deployments (${columns6})
|
|
12703
13639
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
|
12704
13640
|
ON CONFLICT (tenant_id, plugin_type) DO UPDATE SET
|
|
12705
13641
|
plugin_version = EXCLUDED.plugin_version,
|
|
@@ -12714,7 +13650,7 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12714
13650
|
last_health_at = EXCLUDED.last_health_at,
|
|
12715
13651
|
last_error = EXCLUDED.last_error,
|
|
12716
13652
|
updated_at = NOW()
|
|
12717
|
-
RETURNING ${
|
|
13653
|
+
RETURNING ${columns6}`,
|
|
12718
13654
|
[
|
|
12719
13655
|
deployment.id,
|
|
12720
13656
|
deployment.tenantId,
|
|
@@ -12734,7 +13670,7 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12734
13670
|
new Date(deployment.updatedAt)
|
|
12735
13671
|
]
|
|
12736
13672
|
);
|
|
12737
|
-
return
|
|
13673
|
+
return mapRow14(result.rows[0]);
|
|
12738
13674
|
}
|
|
12739
13675
|
/** Applies a partial update to mutable columns; returns undefined when no row exists. */
|
|
12740
13676
|
async updateStatus(tenantId, pluginType, patch) {
|
|
@@ -12762,10 +13698,10 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12762
13698
|
const result = await this.pool.query(
|
|
12763
13699
|
`UPDATE lattice_plugin_deployments SET ${assignments.join(", ")}
|
|
12764
13700
|
WHERE tenant_id = $${tenantParam} AND plugin_type = $${pluginParam}
|
|
12765
|
-
RETURNING ${
|
|
13701
|
+
RETURNING ${columns6}`,
|
|
12766
13702
|
params
|
|
12767
13703
|
);
|
|
12768
|
-
return result.rows[0] ?
|
|
13704
|
+
return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
|
|
12769
13705
|
}
|
|
12770
13706
|
/** Deletes a deployment by tenant + plugin type; returns whether a row was removed. */
|
|
12771
13707
|
async delete(tenantId, pluginType) {
|
|
@@ -13730,6 +14666,79 @@ function isActiveRunViolation(error) {
|
|
|
13730
14666
|
return code === "23505" && constraint === "uq_lattice_agent_runs_one_running";
|
|
13731
14667
|
}
|
|
13732
14668
|
|
|
14669
|
+
// src/stores/PostgreSQLPlatformSettingsStore.ts
|
|
14670
|
+
import {
|
|
14671
|
+
PlatformSettingsVersionConflictError
|
|
14672
|
+
} from "@axiom-lattice/protocols";
|
|
14673
|
+
var TABLE7 = "lattice_platform_settings";
|
|
14674
|
+
var SETTINGS_ID = "default";
|
|
14675
|
+
var COLUMNS = "id, document, version, updated_by, updated_at";
|
|
14676
|
+
function normalizeUpdatedBy(updatedBy) {
|
|
14677
|
+
const trimmed = updatedBy?.trim();
|
|
14678
|
+
return trimmed ? trimmed : null;
|
|
14679
|
+
}
|
|
14680
|
+
function mapRow15(row) {
|
|
14681
|
+
return {
|
|
14682
|
+
...row.document,
|
|
14683
|
+
version: Number(row.version),
|
|
14684
|
+
...row.updated_by !== null ? { updatedBy: row.updated_by } : {},
|
|
14685
|
+
updatedAt: row.updated_at
|
|
14686
|
+
};
|
|
14687
|
+
}
|
|
14688
|
+
var PostgreSQLPlatformSettingsStore = class {
|
|
14689
|
+
constructor(options) {
|
|
14690
|
+
this.options = options;
|
|
14691
|
+
}
|
|
14692
|
+
get pool() {
|
|
14693
|
+
return this.options.pool;
|
|
14694
|
+
}
|
|
14695
|
+
async get() {
|
|
14696
|
+
const result = await this.pool.query(
|
|
14697
|
+
`SELECT ${COLUMNS} FROM ${TABLE7} WHERE id = $1`,
|
|
14698
|
+
[SETTINGS_ID]
|
|
14699
|
+
);
|
|
14700
|
+
return result.rows[0] ? mapRow15(result.rows[0]) : null;
|
|
14701
|
+
}
|
|
14702
|
+
async save(document, expectedVersion, updatedBy) {
|
|
14703
|
+
const version = expectedVersion + 1;
|
|
14704
|
+
const result = expectedVersion === 0 ? await this.pool.query(
|
|
14705
|
+
`INSERT INTO ${TABLE7} (id, document, version, updated_by, updated_at)
|
|
14706
|
+
VALUES ($1, $2::jsonb, $3, $4, NOW())
|
|
14707
|
+
ON CONFLICT (id) DO NOTHING
|
|
14708
|
+
RETURNING ${COLUMNS}`,
|
|
14709
|
+
[
|
|
14710
|
+
SETTINGS_ID,
|
|
14711
|
+
JSON.stringify(document),
|
|
14712
|
+
version,
|
|
14713
|
+
normalizeUpdatedBy(updatedBy)
|
|
14714
|
+
]
|
|
14715
|
+
) : await this.pool.query(
|
|
14716
|
+
`UPDATE ${TABLE7}
|
|
14717
|
+
SET document = $2::jsonb, version = $3, updated_by = $4, updated_at = NOW()
|
|
14718
|
+
WHERE id = $1 AND version = $5
|
|
14719
|
+
RETURNING ${COLUMNS}`,
|
|
14720
|
+
[
|
|
14721
|
+
SETTINGS_ID,
|
|
14722
|
+
JSON.stringify(document),
|
|
14723
|
+
version,
|
|
14724
|
+
normalizeUpdatedBy(updatedBy),
|
|
14725
|
+
expectedVersion
|
|
14726
|
+
]
|
|
14727
|
+
);
|
|
14728
|
+
if (result.rows[0]) {
|
|
14729
|
+
return mapRow15(result.rows[0]);
|
|
14730
|
+
}
|
|
14731
|
+
const actual = await this.pool.query(
|
|
14732
|
+
`SELECT version FROM ${TABLE7} WHERE id = $1`,
|
|
14733
|
+
[SETTINGS_ID]
|
|
14734
|
+
);
|
|
14735
|
+
throw new PlatformSettingsVersionConflictError(
|
|
14736
|
+
expectedVersion,
|
|
14737
|
+
actual.rows[0] ? Number(actual.rows[0].version) : 0
|
|
14738
|
+
);
|
|
14739
|
+
}
|
|
14740
|
+
};
|
|
14741
|
+
|
|
13733
14742
|
// src/stores/PostgreSQLChannelReplyJobStore.ts
|
|
13734
14743
|
import { Pool as Pool31 } from "pg";
|
|
13735
14744
|
import {
|
|
@@ -13737,7 +14746,7 @@ import {
|
|
|
13737
14746
|
normalizeChannelReplyJobListLimit
|
|
13738
14747
|
} from "@axiom-lattice/protocols";
|
|
13739
14748
|
var DB_CLOCK_MS2 = "FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint";
|
|
13740
|
-
var
|
|
14749
|
+
var COLUMNS2 = `run_id, tenant_id, assistant_id, thread_id, adapter_channel,
|
|
13741
14750
|
channel_installation_id, reply_target, input_message_id, stream_cursor, status,
|
|
13742
14751
|
attempts, available_at, lease_owner, lease_expires_at, last_error,
|
|
13743
14752
|
output_message_id, created_at, updated_at, delivered_at`;
|
|
@@ -13755,7 +14764,7 @@ function toStatus(value) {
|
|
|
13755
14764
|
}
|
|
13756
14765
|
throw new Error(`Invalid channel reply job status: ${value}`);
|
|
13757
14766
|
}
|
|
13758
|
-
function
|
|
14767
|
+
function mapRow16(row) {
|
|
13759
14768
|
return {
|
|
13760
14769
|
runId: row.run_id,
|
|
13761
14770
|
tenantId: row.tenant_id,
|
|
@@ -13841,7 +14850,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13841
14850
|
'pending', 0, $10, NULL, NULL,
|
|
13842
14851
|
NULL, NULL, clock.now_ms, clock.now_ms, NULL
|
|
13843
14852
|
FROM clock
|
|
13844
|
-
RETURNING ${
|
|
14853
|
+
RETURNING ${COLUMNS2}`,
|
|
13845
14854
|
[
|
|
13846
14855
|
input.runId,
|
|
13847
14856
|
input.tenantId,
|
|
@@ -13855,22 +14864,22 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13855
14864
|
input.availableAt
|
|
13856
14865
|
]
|
|
13857
14866
|
);
|
|
13858
|
-
return
|
|
14867
|
+
return mapRow16(result.rows[0]);
|
|
13859
14868
|
}
|
|
13860
14869
|
/** Reads one job within a tenant scope. */
|
|
13861
14870
|
async get(tenantId, runId) {
|
|
13862
14871
|
const result = await this.pool.query(
|
|
13863
|
-
`SELECT ${
|
|
14872
|
+
`SELECT ${COLUMNS2}
|
|
13864
14873
|
FROM lattice_channel_reply_jobs
|
|
13865
14874
|
WHERE tenant_id = $1 AND run_id = $2`,
|
|
13866
14875
|
[tenantId, runId]
|
|
13867
14876
|
);
|
|
13868
|
-
return result.rowCount === 0 ? null :
|
|
14877
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13869
14878
|
}
|
|
13870
14879
|
/** Lists claimable pending jobs that are due at `now`, oldest first. */
|
|
13871
14880
|
async listPending(now, limit) {
|
|
13872
14881
|
const result = await this.pool.query(
|
|
13873
|
-
`SELECT ${
|
|
14882
|
+
`SELECT ${COLUMNS2}
|
|
13874
14883
|
FROM lattice_channel_reply_jobs
|
|
13875
14884
|
WHERE status = 'pending'
|
|
13876
14885
|
AND available_at <= $1
|
|
@@ -13879,7 +14888,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13879
14888
|
LIMIT $2`,
|
|
13880
14889
|
[now, normalizeChannelReplyJobListLimit(limit)]
|
|
13881
14890
|
);
|
|
13882
|
-
return result.rows.map(
|
|
14891
|
+
return result.rows.map(mapRow16);
|
|
13883
14892
|
}
|
|
13884
14893
|
/**
|
|
13885
14894
|
* Atomically leases a claimable job.
|
|
@@ -13900,10 +14909,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13900
14909
|
AND j.status = 'pending'
|
|
13901
14910
|
AND j.available_at <= clock.now_ms
|
|
13902
14911
|
AND (j.lease_owner IS NULL OR j.lease_expires_at IS NULL OR j.lease_expires_at <= clock.now_ms)
|
|
13903
|
-
RETURNING ${
|
|
14912
|
+
RETURNING ${COLUMNS2}`,
|
|
13904
14913
|
[tenantId, runId, owner, leaseMs]
|
|
13905
14914
|
);
|
|
13906
|
-
return result.rowCount === 0 ? null :
|
|
14915
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13907
14916
|
}
|
|
13908
14917
|
/**
|
|
13909
14918
|
* Marks a job delivered when `owner` still holds the lease.
|
|
@@ -13926,10 +14935,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13926
14935
|
AND j.run_id = $2
|
|
13927
14936
|
AND j.lease_owner = $3
|
|
13928
14937
|
AND j.status = 'pending'
|
|
13929
|
-
RETURNING ${
|
|
14938
|
+
RETURNING ${COLUMNS2}`,
|
|
13930
14939
|
[tenantId, runId, owner, outputMessageId]
|
|
13931
14940
|
);
|
|
13932
|
-
return result.rowCount === 0 ? null :
|
|
14941
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13933
14942
|
}
|
|
13934
14943
|
/**
|
|
13935
14944
|
* Returns a job to pending with an incremented attempt count.
|
|
@@ -13953,10 +14962,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13953
14962
|
AND j.run_id = $2
|
|
13954
14963
|
AND j.lease_owner = $3
|
|
13955
14964
|
AND j.status = 'pending'
|
|
13956
|
-
RETURNING ${
|
|
14965
|
+
RETURNING ${COLUMNS2}`,
|
|
13957
14966
|
[tenantId, runId, owner, error, availableAt]
|
|
13958
14967
|
);
|
|
13959
|
-
return result.rowCount === 0 ? null :
|
|
14968
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13960
14969
|
}
|
|
13961
14970
|
/**
|
|
13962
14971
|
* Marks a job terminally failed when `owner` still holds the lease.
|
|
@@ -13978,10 +14987,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13978
14987
|
AND j.run_id = $2
|
|
13979
14988
|
AND j.lease_owner = $3
|
|
13980
14989
|
AND j.status = 'pending'
|
|
13981
|
-
RETURNING ${
|
|
14990
|
+
RETURNING ${COLUMNS2}`,
|
|
13982
14991
|
[tenantId, runId, owner, error]
|
|
13983
14992
|
);
|
|
13984
|
-
return result.rowCount === 0 ? null :
|
|
14993
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13985
14994
|
}
|
|
13986
14995
|
/** Deletes one job. Returns whether a row was removed. */
|
|
13987
14996
|
async delete(tenantId, runId) {
|
|
@@ -14041,7 +15050,7 @@ async function rollbackQuietly(client) {
|
|
|
14041
15050
|
} catch {
|
|
14042
15051
|
}
|
|
14043
15052
|
}
|
|
14044
|
-
function
|
|
15053
|
+
function isUniqueViolation5(error) {
|
|
14045
15054
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
14046
15055
|
}
|
|
14047
15056
|
function isRecord5(value) {
|
|
@@ -14169,7 +15178,7 @@ var PostgreSQLCheckpointStorage = class {
|
|
|
14169
15178
|
await client.query("COMMIT");
|
|
14170
15179
|
} catch (error) {
|
|
14171
15180
|
await rollbackQuietly(client);
|
|
14172
|
-
if (
|
|
15181
|
+
if (isUniqueViolation5(error)) {
|
|
14173
15182
|
throw new CheckpointTargetNotEmptyError(targetThreadId);
|
|
14174
15183
|
}
|
|
14175
15184
|
throw error;
|
|
@@ -14463,7 +15472,7 @@ import {
|
|
|
14463
15472
|
storeExpiresAt,
|
|
14464
15473
|
storePaginationValue
|
|
14465
15474
|
} from "@axiom-lattice/protocols";
|
|
14466
|
-
function
|
|
15475
|
+
function mapRow17(row) {
|
|
14467
15476
|
return {
|
|
14468
15477
|
namespace: row.namespace,
|
|
14469
15478
|
key: row.key,
|
|
@@ -14495,7 +15504,7 @@ var PostgreSQLStore = class {
|
|
|
14495
15504
|
RETURNING tenant_id, namespace, key, value, created_at, updated_at, expires_at`,
|
|
14496
15505
|
[tenantId, input.namespace, input.key, JSON.stringify(input.value), now, expiresAt]
|
|
14497
15506
|
);
|
|
14498
|
-
return
|
|
15507
|
+
return mapRow17(result.rows[0]);
|
|
14499
15508
|
}
|
|
14500
15509
|
async get(tenantId, namespace, key) {
|
|
14501
15510
|
assertStoreIdentity(namespace, key);
|
|
@@ -14506,7 +15515,7 @@ var PostgreSQLStore = class {
|
|
|
14506
15515
|
AND (expires_at IS NULL OR expires_at > $4)`,
|
|
14507
15516
|
[tenantId, namespace, key, /* @__PURE__ */ new Date()]
|
|
14508
15517
|
);
|
|
14509
|
-
return result.rows[0] ?
|
|
15518
|
+
return result.rows[0] ? mapRow17(result.rows[0]) : null;
|
|
14510
15519
|
}
|
|
14511
15520
|
async delete(tenantId, namespace, key) {
|
|
14512
15521
|
assertStoreIdentity(namespace, key);
|
|
@@ -14550,7 +15559,7 @@ var PostgreSQLStore = class {
|
|
|
14550
15559
|
LIMIT $${limitParam} OFFSET $${offsetParam}`,
|
|
14551
15560
|
params
|
|
14552
15561
|
);
|
|
14553
|
-
return result.rows.map(
|
|
15562
|
+
return result.rows.map(mapRow17);
|
|
14554
15563
|
}
|
|
14555
15564
|
async listNamespaces(tenantId, prefix = [], limit = 100) {
|
|
14556
15565
|
assertStoreNamespacePrefix(prefix);
|
|
@@ -14721,6 +15730,16 @@ async function createPgStoreConfig(connectionString) {
|
|
|
14721
15730
|
mm.register(agentRunProjectTaskRunningIndexMigration);
|
|
14722
15731
|
mm.register(addModelProviderKindDefault);
|
|
14723
15732
|
mm.register(addShareKind);
|
|
15733
|
+
mm.register(createUserExternalIdentityTable);
|
|
15734
|
+
mm.register(createUserEmailLowerUniqueIndex);
|
|
15735
|
+
mm.register(createOidcProviderConfigsTable);
|
|
15736
|
+
mm.register(createPlatformMembersTable);
|
|
15737
|
+
mm.register(createPlatformInvitationsTable);
|
|
15738
|
+
mm.register(createOidcIdentityClaimsTable);
|
|
15739
|
+
mm.register(createInviteLinksTable);
|
|
15740
|
+
mm.register(addOidcIdentityClaimInviteFields);
|
|
15741
|
+
mm.register(createPlatformSettingsTable);
|
|
15742
|
+
mm.register(addOidcProviderAllowedHosts);
|
|
14724
15743
|
await mm.migrate();
|
|
14725
15744
|
const checkpoint = PostgresSaver.fromConnString(connectionString);
|
|
14726
15745
|
try {
|
|
@@ -14737,6 +15756,13 @@ async function createPgStoreConfig(connectionString) {
|
|
|
14737
15756
|
user: new PostgreSQLUserStore(opts),
|
|
14738
15757
|
tenant: new PostgreSQLTenantStore(opts),
|
|
14739
15758
|
userTenantLink: new PostgreSQLUserTenantLinkStore(opts),
|
|
15759
|
+
userExternalIdentity: new PostgreSQLUserExternalIdentityStore(opts),
|
|
15760
|
+
oidcProviderConfig: new PostgreSQLOidcProviderConfigStore(opts),
|
|
15761
|
+
platformMember: new PostgreSQLPlatformMemberStore(opts),
|
|
15762
|
+
platformInvitation: new PostgreSQLPlatformInvitationStore(opts),
|
|
15763
|
+
oidcIdentityClaim: new PostgreSQLOidcIdentityClaimStore(opts),
|
|
15764
|
+
inviteLink: new PostgreSQLInviteLinkStore(opts),
|
|
15765
|
+
platformSettings: new PostgreSQLPlatformSettingsStore(opts),
|
|
14740
15766
|
channelBinding: new ChannelBindingStore(opts),
|
|
14741
15767
|
channelInstallation: new PostgreSQLChannelInstallationStore(opts),
|
|
14742
15768
|
thread: new PostgreSQLThreadStore(opts),
|
|
@@ -15333,6 +16359,7 @@ export {
|
|
|
15333
16359
|
InvalidFileShareAddressError,
|
|
15334
16360
|
InvalidMcpAppShareAddressError,
|
|
15335
16361
|
InvalidMcpAppShareVisibilityError,
|
|
16362
|
+
InvalidUserExternalIdentityRowError,
|
|
15336
16363
|
MenuStore,
|
|
15337
16364
|
MigrationManager,
|
|
15338
16365
|
PGVectorStoreProvider,
|
|
@@ -15349,10 +16376,16 @@ export {
|
|
|
15349
16376
|
PostgreSQLConnectionStore,
|
|
15350
16377
|
PostgreSQLDatabaseConfigStore,
|
|
15351
16378
|
PostgreSQLEvalStore,
|
|
16379
|
+
PostgreSQLInviteLinkStore,
|
|
15352
16380
|
PostgreSQLMcpServerConfigStore,
|
|
15353
16381
|
PostgreSQLMetricsServerConfigStore,
|
|
15354
16382
|
PostgreSQLModelProviderStore,
|
|
16383
|
+
PostgreSQLOidcIdentityClaimStore,
|
|
16384
|
+
PostgreSQLOidcProviderConfigStore,
|
|
15355
16385
|
PostgreSQLOpenAuditStore,
|
|
16386
|
+
PostgreSQLPlatformInvitationStore,
|
|
16387
|
+
PostgreSQLPlatformMemberStore,
|
|
16388
|
+
PostgreSQLPlatformSettingsStore,
|
|
15356
16389
|
PostgreSQLPluginDeploymentStore,
|
|
15357
16390
|
PostgreSQLProjectBotMembershipStore,
|
|
15358
16391
|
PostgreSQLProjectMembershipStore,
|
|
@@ -15366,6 +16399,7 @@ export {
|
|
|
15366
16399
|
PostgreSQLTaskWorkItemStore,
|
|
15367
16400
|
PostgreSQLTenantStore,
|
|
15368
16401
|
PostgreSQLThreadStore,
|
|
16402
|
+
PostgreSQLUserExternalIdentityStore,
|
|
15369
16403
|
PostgreSQLUserStore,
|
|
15370
16404
|
PostgreSQLUserTenantLinkStore,
|
|
15371
16405
|
PostgreSQLWorkflowTrackingStore,
|
|
@@ -15383,6 +16417,7 @@ export {
|
|
|
15383
16417
|
addInterruptColumnsToEval,
|
|
15384
16418
|
addInterruptPolicyToEvalCases,
|
|
15385
16419
|
addMcpAppContentType,
|
|
16420
|
+
addOidcIdentityClaimInviteFields,
|
|
15386
16421
|
addProjectConfigColumn,
|
|
15387
16422
|
addScheduleTenantId,
|
|
15388
16423
|
addShareKind,
|
|
@@ -15412,11 +16447,17 @@ export {
|
|
|
15412
16447
|
createEvalRunResultsTable,
|
|
15413
16448
|
createEvalRunsTable,
|
|
15414
16449
|
createEvalSuitesTable,
|
|
16450
|
+
createInviteLinksTable,
|
|
15415
16451
|
createMcpServerConfigsTable,
|
|
15416
16452
|
createMenuItemsTable,
|
|
15417
16453
|
createMetricsConfigsTable,
|
|
16454
|
+
createOidcIdentityClaimsTable,
|
|
16455
|
+
createOidcProviderConfigsTable,
|
|
15418
16456
|
createPGVectorStoreProvider,
|
|
15419
16457
|
createPgStoreConfig,
|
|
16458
|
+
createPlatformInvitationsTable,
|
|
16459
|
+
createPlatformMembersTable,
|
|
16460
|
+
createPlatformSettingsTable,
|
|
15420
16461
|
createPluginDeployments,
|
|
15421
16462
|
createProjectRoomReadStates,
|
|
15422
16463
|
createProjectRoomTables,
|
|
@@ -15428,6 +16469,8 @@ export {
|
|
|
15428
16469
|
createTenantsTable,
|
|
15429
16470
|
createThreadMessageQueueTable,
|
|
15430
16471
|
createThreadsTable,
|
|
16472
|
+
createUserEmailLowerUniqueIndex,
|
|
16473
|
+
createUserExternalIdentityTable,
|
|
15431
16474
|
createUsersTable,
|
|
15432
16475
|
createWorkflowTrackingTables,
|
|
15433
16476
|
createWorkspacesTable,
|