@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.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
InvalidFileShareAddressError: () => InvalidFileShareAddressError,
|
|
40
40
|
InvalidMcpAppShareAddressError: () => InvalidMcpAppShareAddressError,
|
|
41
41
|
InvalidMcpAppShareVisibilityError: () => InvalidMcpAppShareVisibilityError,
|
|
42
|
+
InvalidUserExternalIdentityRowError: () => InvalidUserExternalIdentityRowError,
|
|
42
43
|
MenuStore: () => MenuStore,
|
|
43
44
|
MigrationManager: () => MigrationManager,
|
|
44
45
|
PGVectorStoreProvider: () => PGVectorStoreProvider,
|
|
@@ -55,10 +56,16 @@ __export(index_exports, {
|
|
|
55
56
|
PostgreSQLConnectionStore: () => PostgreSQLConnectionStore,
|
|
56
57
|
PostgreSQLDatabaseConfigStore: () => PostgreSQLDatabaseConfigStore,
|
|
57
58
|
PostgreSQLEvalStore: () => PostgreSQLEvalStore,
|
|
59
|
+
PostgreSQLInviteLinkStore: () => PostgreSQLInviteLinkStore,
|
|
58
60
|
PostgreSQLMcpServerConfigStore: () => PostgreSQLMcpServerConfigStore,
|
|
59
61
|
PostgreSQLMetricsServerConfigStore: () => PostgreSQLMetricsServerConfigStore,
|
|
60
62
|
PostgreSQLModelProviderStore: () => PostgreSQLModelProviderStore,
|
|
63
|
+
PostgreSQLOidcIdentityClaimStore: () => PostgreSQLOidcIdentityClaimStore,
|
|
64
|
+
PostgreSQLOidcProviderConfigStore: () => PostgreSQLOidcProviderConfigStore,
|
|
61
65
|
PostgreSQLOpenAuditStore: () => PostgreSQLOpenAuditStore,
|
|
66
|
+
PostgreSQLPlatformInvitationStore: () => PostgreSQLPlatformInvitationStore,
|
|
67
|
+
PostgreSQLPlatformMemberStore: () => PostgreSQLPlatformMemberStore,
|
|
68
|
+
PostgreSQLPlatformSettingsStore: () => PostgreSQLPlatformSettingsStore,
|
|
62
69
|
PostgreSQLPluginDeploymentStore: () => PostgreSQLPluginDeploymentStore,
|
|
63
70
|
PostgreSQLProjectBotMembershipStore: () => PostgreSQLProjectBotMembershipStore,
|
|
64
71
|
PostgreSQLProjectMembershipStore: () => PostgreSQLProjectMembershipStore,
|
|
@@ -72,6 +79,7 @@ __export(index_exports, {
|
|
|
72
79
|
PostgreSQLTaskWorkItemStore: () => PostgreSQLTaskWorkItemStore,
|
|
73
80
|
PostgreSQLTenantStore: () => PostgreSQLTenantStore,
|
|
74
81
|
PostgreSQLThreadStore: () => PostgreSQLThreadStore,
|
|
82
|
+
PostgreSQLUserExternalIdentityStore: () => PostgreSQLUserExternalIdentityStore,
|
|
75
83
|
PostgreSQLUserStore: () => PostgreSQLUserStore,
|
|
76
84
|
PostgreSQLUserTenantLinkStore: () => PostgreSQLUserTenantLinkStore,
|
|
77
85
|
PostgreSQLWorkflowTrackingStore: () => PostgreSQLWorkflowTrackingStore,
|
|
@@ -89,6 +97,7 @@ __export(index_exports, {
|
|
|
89
97
|
addInterruptColumnsToEval: () => addInterruptColumnsToEval,
|
|
90
98
|
addInterruptPolicyToEvalCases: () => addInterruptPolicyToEvalCases,
|
|
91
99
|
addMcpAppContentType: () => addMcpAppContentType,
|
|
100
|
+
addOidcIdentityClaimInviteFields: () => addOidcIdentityClaimInviteFields,
|
|
92
101
|
addProjectConfigColumn: () => addProjectConfigColumn,
|
|
93
102
|
addScheduleTenantId: () => addScheduleTenantId,
|
|
94
103
|
addShareKind: () => addShareKind,
|
|
@@ -118,11 +127,17 @@ __export(index_exports, {
|
|
|
118
127
|
createEvalRunResultsTable: () => createEvalRunResultsTable,
|
|
119
128
|
createEvalRunsTable: () => createEvalRunsTable,
|
|
120
129
|
createEvalSuitesTable: () => createEvalSuitesTable,
|
|
130
|
+
createInviteLinksTable: () => createInviteLinksTable,
|
|
121
131
|
createMcpServerConfigsTable: () => createMcpServerConfigsTable,
|
|
122
132
|
createMenuItemsTable: () => createMenuItemsTable,
|
|
123
133
|
createMetricsConfigsTable: () => createMetricsConfigsTable,
|
|
134
|
+
createOidcIdentityClaimsTable: () => createOidcIdentityClaimsTable,
|
|
135
|
+
createOidcProviderConfigsTable: () => createOidcProviderConfigsTable,
|
|
124
136
|
createPGVectorStoreProvider: () => createPGVectorStoreProvider,
|
|
125
137
|
createPgStoreConfig: () => createPgStoreConfig,
|
|
138
|
+
createPlatformInvitationsTable: () => createPlatformInvitationsTable,
|
|
139
|
+
createPlatformMembersTable: () => createPlatformMembersTable,
|
|
140
|
+
createPlatformSettingsTable: () => createPlatformSettingsTable,
|
|
126
141
|
createPluginDeployments: () => createPluginDeployments,
|
|
127
142
|
createProjectRoomReadStates: () => createProjectRoomReadStates,
|
|
128
143
|
createProjectRoomTables: () => createProjectRoomTables,
|
|
@@ -134,6 +149,8 @@ __export(index_exports, {
|
|
|
134
149
|
createTenantsTable: () => createTenantsTable,
|
|
135
150
|
createThreadMessageQueueTable: () => createThreadMessageQueueTable,
|
|
136
151
|
createThreadsTable: () => createThreadsTable,
|
|
152
|
+
createUserEmailLowerUniqueIndex: () => createUserEmailLowerUniqueIndex,
|
|
153
|
+
createUserExternalIdentityTable: () => createUserExternalIdentityTable,
|
|
137
154
|
createUsersTable: () => createUsersTable,
|
|
138
155
|
createWorkflowTrackingTables: () => createWorkflowTrackingTables,
|
|
139
156
|
createWorkspacesTable: () => createWorkspacesTable,
|
|
@@ -3094,9 +3111,10 @@ var PostgreSQLUserStore = class {
|
|
|
3094
3111
|
`
|
|
3095
3112
|
SELECT id, email, name, status, metadata, created_at, updated_at
|
|
3096
3113
|
FROM lattice_users
|
|
3097
|
-
WHERE email = $1
|
|
3114
|
+
WHERE lower(email) = lower($1)
|
|
3115
|
+
LIMIT 1
|
|
3098
3116
|
`,
|
|
3099
|
-
[email]
|
|
3117
|
+
[email.trim()]
|
|
3100
3118
|
);
|
|
3101
3119
|
if (result.rows.length === 0) return null;
|
|
3102
3120
|
return this.mapRowToUser(result.rows[0]);
|
|
@@ -3610,30 +3628,705 @@ var PostgreSQLUserTenantLinkStore = class {
|
|
|
3610
3628
|
`,
|
|
3611
3629
|
updateValues
|
|
3612
3630
|
);
|
|
3613
|
-
return this.getLink(userId, tenantId);
|
|
3631
|
+
return this.getLink(userId, tenantId);
|
|
3632
|
+
}
|
|
3633
|
+
async deleteLink(userId, tenantId) {
|
|
3634
|
+
await this.ensureInitialized();
|
|
3635
|
+
const result = await this.pool.query(
|
|
3636
|
+
`
|
|
3637
|
+
DELETE FROM lattice_user_tenant_links
|
|
3638
|
+
WHERE user_id = $1 AND tenant_id = $2
|
|
3639
|
+
`,
|
|
3640
|
+
[userId, tenantId]
|
|
3641
|
+
);
|
|
3642
|
+
return result.rowCount !== null && result.rowCount > 0;
|
|
3643
|
+
}
|
|
3644
|
+
async hasLink(userId, tenantId) {
|
|
3645
|
+
await this.ensureInitialized();
|
|
3646
|
+
const result = await this.pool.query(
|
|
3647
|
+
`
|
|
3648
|
+
SELECT 1 FROM lattice_user_tenant_links
|
|
3649
|
+
WHERE user_id = $1 AND tenant_id = $2
|
|
3650
|
+
LIMIT 1
|
|
3651
|
+
`,
|
|
3652
|
+
[userId, tenantId]
|
|
3653
|
+
);
|
|
3654
|
+
return result.rows.length > 0;
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
|
|
3658
|
+
// src/stores/PostgreSQLUserExternalIdentityStore.ts
|
|
3659
|
+
var import_protocols2 = require("@axiom-lattice/protocols");
|
|
3660
|
+
var InvalidUserExternalIdentityRowError = class extends Error {
|
|
3661
|
+
constructor() {
|
|
3662
|
+
super("Invalid user external identity row");
|
|
3663
|
+
this.code = "INVALID_USER_EXTERNAL_IDENTITY_ROW";
|
|
3664
|
+
this.name = "InvalidUserExternalIdentityRowError";
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
var columns = "user_id, provider_id, issuer, subject, email, linked_at";
|
|
3668
|
+
function mapRow(row) {
|
|
3669
|
+
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())) {
|
|
3670
|
+
throw new InvalidUserExternalIdentityRowError();
|
|
3671
|
+
}
|
|
3672
|
+
return {
|
|
3673
|
+
userId: row.user_id,
|
|
3674
|
+
providerId: row.provider_id,
|
|
3675
|
+
issuer: row.issuer,
|
|
3676
|
+
subject: row.subject,
|
|
3677
|
+
...typeof row.email === "string" ? { email: row.email } : {},
|
|
3678
|
+
linkedAt: row.linked_at
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
var PostgreSQLUserExternalIdentityStore = class {
|
|
3682
|
+
constructor(options) {
|
|
3683
|
+
this.pool = options.pool;
|
|
3684
|
+
}
|
|
3685
|
+
async getByProviderSubject(providerId, issuer, subject) {
|
|
3686
|
+
const result = await this.pool.query(
|
|
3687
|
+
`SELECT ${columns} FROM lattice_user_identities
|
|
3688
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
|
|
3689
|
+
[providerId, issuer, subject]
|
|
3690
|
+
);
|
|
3691
|
+
return result.rows[0] ? mapRow(result.rows[0]) : null;
|
|
3692
|
+
}
|
|
3693
|
+
async listByUser(userId) {
|
|
3694
|
+
const result = await this.pool.query(
|
|
3695
|
+
`SELECT ${columns} FROM lattice_user_identities WHERE user_id = $1 ORDER BY linked_at ASC`,
|
|
3696
|
+
[userId]
|
|
3697
|
+
);
|
|
3698
|
+
return result.rows.map(mapRow);
|
|
3699
|
+
}
|
|
3700
|
+
async link(identity) {
|
|
3701
|
+
const inserted = await this.pool.query(
|
|
3702
|
+
`INSERT INTO lattice_user_identities (${columns})
|
|
3703
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3704
|
+
ON CONFLICT (provider_id, issuer, subject) DO NOTHING
|
|
3705
|
+
RETURNING ${columns}`,
|
|
3706
|
+
[
|
|
3707
|
+
identity.userId,
|
|
3708
|
+
identity.providerId,
|
|
3709
|
+
identity.issuer,
|
|
3710
|
+
identity.subject,
|
|
3711
|
+
identity.email ?? null,
|
|
3712
|
+
identity.linkedAt
|
|
3713
|
+
]
|
|
3714
|
+
);
|
|
3715
|
+
if (inserted.rows[0]) return;
|
|
3716
|
+
const existing = await this.getByProviderSubject(
|
|
3717
|
+
identity.providerId,
|
|
3718
|
+
identity.issuer,
|
|
3719
|
+
identity.subject
|
|
3720
|
+
);
|
|
3721
|
+
if (!existing) {
|
|
3722
|
+
throw new import_protocols2.ExternalIdentityPersistError(
|
|
3723
|
+
identity.providerId,
|
|
3724
|
+
identity.issuer,
|
|
3725
|
+
identity.subject
|
|
3726
|
+
);
|
|
3727
|
+
}
|
|
3728
|
+
if (existing.userId !== identity.userId) {
|
|
3729
|
+
throw new import_protocols2.ExternalIdentityConflictError(
|
|
3730
|
+
identity.providerId,
|
|
3731
|
+
identity.issuer,
|
|
3732
|
+
identity.subject
|
|
3733
|
+
);
|
|
3734
|
+
}
|
|
3735
|
+
if (identity.email !== void 0 && identity.email !== existing.email) {
|
|
3736
|
+
await this.pool.query(
|
|
3737
|
+
`UPDATE lattice_user_identities SET email = $1
|
|
3738
|
+
WHERE provider_id = $2 AND issuer = $3 AND subject = $4`,
|
|
3739
|
+
[identity.email, identity.providerId, identity.issuer, identity.subject]
|
|
3740
|
+
);
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
async unlink(providerId, issuer, subject) {
|
|
3744
|
+
await this.pool.query(
|
|
3745
|
+
`DELETE FROM lattice_user_identities
|
|
3746
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
|
|
3747
|
+
[providerId, issuer, subject]
|
|
3748
|
+
);
|
|
3749
|
+
}
|
|
3750
|
+
};
|
|
3751
|
+
|
|
3752
|
+
// src/stores/PostgreSQLOidcProviderConfigStore.ts
|
|
3753
|
+
var import_core5 = require("@axiom-lattice/core");
|
|
3754
|
+
var import_protocols3 = require("@axiom-lattice/protocols");
|
|
3755
|
+
var TABLE2 = "lattice_oidc_provider_configs";
|
|
3756
|
+
function parseAllowedPrivateHosts(value) {
|
|
3757
|
+
if (!Array.isArray(value)) return void 0;
|
|
3758
|
+
return value.filter((entry) => typeof entry === "string");
|
|
3759
|
+
}
|
|
3760
|
+
function serializeAllowedPrivateHosts(value) {
|
|
3761
|
+
return value === void 0 ? null : JSON.stringify(value);
|
|
3762
|
+
}
|
|
3763
|
+
function normalizeTenantId(value) {
|
|
3764
|
+
if (typeof value !== "string") return void 0;
|
|
3765
|
+
const trimmed = value.trim();
|
|
3766
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3767
|
+
}
|
|
3768
|
+
function mapRow2(row) {
|
|
3769
|
+
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)) {
|
|
3770
|
+
throw new Error("Invalid OIDC provider config row");
|
|
3771
|
+
}
|
|
3772
|
+
const allowedPrivateHosts = parseAllowedPrivateHosts(
|
|
3773
|
+
row.allowed_private_hosts
|
|
3774
|
+
);
|
|
3775
|
+
return {
|
|
3776
|
+
id: row.id,
|
|
3777
|
+
displayName: row.display_name,
|
|
3778
|
+
issuer: row.issuer,
|
|
3779
|
+
clientId: row.client_id,
|
|
3780
|
+
clientSecret: (0, import_core5.safeDecrypt)(
|
|
3781
|
+
row.client_secret,
|
|
3782
|
+
`OIDC provider config "${row.id}" client secret`
|
|
3783
|
+
),
|
|
3784
|
+
scopes: row.scopes,
|
|
3785
|
+
enabled: row.enabled,
|
|
3786
|
+
jitProvisioning: row.jit_provisioning,
|
|
3787
|
+
autoLinkEmail: row.auto_link_email,
|
|
3788
|
+
...typeof row.default_tenant_id === "string" ? { defaultTenantId: row.default_tenant_id } : {},
|
|
3789
|
+
...allowedPrivateHosts !== void 0 ? { allowedPrivateHosts } : {},
|
|
3790
|
+
createdAt: row.created_at,
|
|
3791
|
+
updatedAt: row.updated_at
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
function isUniqueViolation(error) {
|
|
3795
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
3796
|
+
}
|
|
3797
|
+
var PostgreSQLOidcProviderConfigStore = class {
|
|
3798
|
+
constructor(options) {
|
|
3799
|
+
this.pool = options.pool;
|
|
3800
|
+
}
|
|
3801
|
+
async list() {
|
|
3802
|
+
const result = await this.pool.query(
|
|
3803
|
+
`SELECT * FROM ${TABLE2} ORDER BY created_at ASC, id ASC`
|
|
3804
|
+
);
|
|
3805
|
+
return result.rows.map(mapRow2);
|
|
3806
|
+
}
|
|
3807
|
+
async get(id) {
|
|
3808
|
+
const result = await this.pool.query(
|
|
3809
|
+
`SELECT * FROM ${TABLE2} WHERE id = $1`,
|
|
3810
|
+
[id]
|
|
3811
|
+
);
|
|
3812
|
+
return result.rows[0] ? mapRow2(result.rows[0]) : null;
|
|
3813
|
+
}
|
|
3814
|
+
async create(id, input) {
|
|
3815
|
+
const tenantId = normalizeTenantId(input.defaultTenantId);
|
|
3816
|
+
try {
|
|
3817
|
+
await this.pool.query(
|
|
3818
|
+
`INSERT INTO ${TABLE2}
|
|
3819
|
+
(id, display_name, issuer, client_id, client_secret, scopes,
|
|
3820
|
+
enabled, jit_provisioning, auto_link_email, default_tenant_id,
|
|
3821
|
+
allowed_private_hosts)
|
|
3822
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
|
3823
|
+
[
|
|
3824
|
+
id,
|
|
3825
|
+
input.displayName,
|
|
3826
|
+
input.issuer,
|
|
3827
|
+
input.clientId,
|
|
3828
|
+
(0, import_core5.encrypt)(input.clientSecret),
|
|
3829
|
+
input.scopes,
|
|
3830
|
+
input.enabled,
|
|
3831
|
+
input.jitProvisioning,
|
|
3832
|
+
input.autoLinkEmail,
|
|
3833
|
+
tenantId ?? null,
|
|
3834
|
+
serializeAllowedPrivateHosts(input.allowedPrivateHosts)
|
|
3835
|
+
]
|
|
3836
|
+
);
|
|
3837
|
+
} catch (error) {
|
|
3838
|
+
if (isUniqueViolation(error)) {
|
|
3839
|
+
throw new import_protocols3.OidcProviderConfigConflictError(id);
|
|
3840
|
+
}
|
|
3841
|
+
throw error;
|
|
3842
|
+
}
|
|
3843
|
+
const created = await this.get(id);
|
|
3844
|
+
if (!created) {
|
|
3845
|
+
throw new Error(`OIDC provider config "${id}" was not persisted`);
|
|
3846
|
+
}
|
|
3847
|
+
return created;
|
|
3848
|
+
}
|
|
3849
|
+
async update(id, updates) {
|
|
3850
|
+
const existing = await this.get(id);
|
|
3851
|
+
if (!existing) return null;
|
|
3852
|
+
const setClauses = [];
|
|
3853
|
+
const values = [];
|
|
3854
|
+
let paramIndex = 1;
|
|
3855
|
+
const set = (column, value) => {
|
|
3856
|
+
setClauses.push(`${column} = $${paramIndex++}`);
|
|
3857
|
+
values.push(value);
|
|
3858
|
+
};
|
|
3859
|
+
if (updates.displayName !== void 0) set("display_name", updates.displayName);
|
|
3860
|
+
if (updates.issuer !== void 0) set("issuer", updates.issuer);
|
|
3861
|
+
if (updates.clientId !== void 0) set("client_id", updates.clientId);
|
|
3862
|
+
if (updates.clientSecret !== void 0 && updates.clientSecret !== "") {
|
|
3863
|
+
set("client_secret", (0, import_core5.encrypt)(updates.clientSecret));
|
|
3864
|
+
}
|
|
3865
|
+
if (updates.scopes !== void 0) set("scopes", updates.scopes);
|
|
3866
|
+
if (updates.enabled !== void 0) set("enabled", updates.enabled);
|
|
3867
|
+
if (updates.jitProvisioning !== void 0) {
|
|
3868
|
+
set("jit_provisioning", updates.jitProvisioning);
|
|
3869
|
+
}
|
|
3870
|
+
if (updates.autoLinkEmail !== void 0) {
|
|
3871
|
+
set("auto_link_email", updates.autoLinkEmail);
|
|
3872
|
+
}
|
|
3873
|
+
if (updates.defaultTenantId !== void 0) {
|
|
3874
|
+
set("default_tenant_id", normalizeTenantId(updates.defaultTenantId) ?? null);
|
|
3875
|
+
}
|
|
3876
|
+
if (updates.allowedPrivateHosts !== void 0) {
|
|
3877
|
+
set(
|
|
3878
|
+
"allowed_private_hosts",
|
|
3879
|
+
serializeAllowedPrivateHosts(updates.allowedPrivateHosts)
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
if (setClauses.length === 0) return existing;
|
|
3883
|
+
setClauses.push("updated_at = NOW()");
|
|
3884
|
+
values.push(id);
|
|
3885
|
+
await this.pool.query(
|
|
3886
|
+
`UPDATE ${TABLE2} SET ${setClauses.join(", ")} WHERE id = $${paramIndex}`,
|
|
3887
|
+
values
|
|
3888
|
+
);
|
|
3889
|
+
return this.get(id);
|
|
3890
|
+
}
|
|
3891
|
+
async delete(id) {
|
|
3892
|
+
const result = await this.pool.query(`DELETE FROM ${TABLE2} WHERE id = $1`, [id]);
|
|
3893
|
+
return (result.rowCount ?? 0) > 0;
|
|
3894
|
+
}
|
|
3895
|
+
};
|
|
3896
|
+
|
|
3897
|
+
// src/stores/PostgreSQLPlatformMemberStore.ts
|
|
3898
|
+
var TABLE3 = "lattice_platform_members";
|
|
3899
|
+
var ROLES = ["owner", "admin", "auditor"];
|
|
3900
|
+
function isPlatformRole(value) {
|
|
3901
|
+
return ROLES.includes(value);
|
|
3902
|
+
}
|
|
3903
|
+
function mapRow3(row) {
|
|
3904
|
+
if (typeof row.user_id !== "string" || typeof row.role !== "string" || !isPlatformRole(row.role) || !(row.granted_at instanceof Date) || !(row.updated_at instanceof Date)) {
|
|
3905
|
+
throw new Error("Invalid platform member row");
|
|
3906
|
+
}
|
|
3907
|
+
return {
|
|
3908
|
+
userId: row.user_id,
|
|
3909
|
+
role: row.role,
|
|
3910
|
+
...typeof row.granted_by === "string" ? { grantedBy: row.granted_by } : {},
|
|
3911
|
+
grantedAt: row.granted_at,
|
|
3912
|
+
updatedAt: row.updated_at
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
var PostgreSQLPlatformMemberStore = class {
|
|
3916
|
+
constructor(options) {
|
|
3917
|
+
this.pool = options.pool;
|
|
3918
|
+
}
|
|
3919
|
+
/** Lists every platform member ordered by user id. */
|
|
3920
|
+
async list() {
|
|
3921
|
+
const result = await this.pool.query(
|
|
3922
|
+
`SELECT * FROM ${TABLE3} ORDER BY user_id ASC`
|
|
3923
|
+
);
|
|
3924
|
+
return result.rows.map(mapRow3);
|
|
3925
|
+
}
|
|
3926
|
+
/** Returns the platform member for a user, or null. */
|
|
3927
|
+
async get(userId) {
|
|
3928
|
+
const result = await this.pool.query(
|
|
3929
|
+
`SELECT * FROM ${TABLE3} WHERE user_id = $1`,
|
|
3930
|
+
[userId]
|
|
3931
|
+
);
|
|
3932
|
+
return result.rows[0] ? mapRow3(result.rows[0]) : null;
|
|
3933
|
+
}
|
|
3934
|
+
/**
|
|
3935
|
+
* Inserts or replaces a member keyed by user id.
|
|
3936
|
+
*
|
|
3937
|
+
* The supplied grant replaces any existing row in full (last write wins),
|
|
3938
|
+
* including `grantedAt` and `updatedAt`.
|
|
3939
|
+
*/
|
|
3940
|
+
async upsert(member) {
|
|
3941
|
+
const result = await this.pool.query(
|
|
3942
|
+
`INSERT INTO ${TABLE3} (user_id, role, granted_by, granted_at, updated_at)
|
|
3943
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3944
|
+
ON CONFLICT (user_id) DO UPDATE SET
|
|
3945
|
+
role = EXCLUDED.role,
|
|
3946
|
+
granted_by = EXCLUDED.granted_by,
|
|
3947
|
+
granted_at = EXCLUDED.granted_at,
|
|
3948
|
+
updated_at = EXCLUDED.updated_at
|
|
3949
|
+
RETURNING *`,
|
|
3950
|
+
[
|
|
3951
|
+
member.userId,
|
|
3952
|
+
member.role,
|
|
3953
|
+
member.grantedBy ?? null,
|
|
3954
|
+
member.grantedAt,
|
|
3955
|
+
member.updatedAt
|
|
3956
|
+
]
|
|
3957
|
+
);
|
|
3958
|
+
return mapRow3(result.rows[0]);
|
|
3959
|
+
}
|
|
3960
|
+
/** Deletes a member. Returns whether a row was removed. */
|
|
3961
|
+
async delete(userId) {
|
|
3962
|
+
const result = await this.pool.query(`DELETE FROM ${TABLE3} WHERE user_id = $1`, [
|
|
3963
|
+
userId
|
|
3964
|
+
]);
|
|
3965
|
+
return (result.rowCount ?? 0) > 0;
|
|
3966
|
+
}
|
|
3967
|
+
};
|
|
3968
|
+
|
|
3969
|
+
// src/stores/PostgreSQLPlatformInvitationStore.ts
|
|
3970
|
+
var import_protocols4 = require("@axiom-lattice/protocols");
|
|
3971
|
+
var TABLE4 = "lattice_platform_invitations";
|
|
3972
|
+
var ROLES2 = ["owner", "admin", "auditor"];
|
|
3973
|
+
function isPlatformRole2(value) {
|
|
3974
|
+
return ROLES2.includes(value);
|
|
3975
|
+
}
|
|
3976
|
+
function mapRow4(row) {
|
|
3977
|
+
if (typeof row.email !== "string" || typeof row.role !== "string" || !isPlatformRole2(row.role) || !(row.created_at instanceof Date)) {
|
|
3978
|
+
throw new Error("Invalid platform invitation row");
|
|
3979
|
+
}
|
|
3980
|
+
return {
|
|
3981
|
+
email: row.email,
|
|
3982
|
+
role: row.role,
|
|
3983
|
+
...typeof row.invited_by === "string" ? { invitedBy: row.invited_by } : {},
|
|
3984
|
+
createdAt: row.created_at,
|
|
3985
|
+
...row.expires_at instanceof Date ? { expiresAt: row.expires_at } : {},
|
|
3986
|
+
...row.consumed_at instanceof Date ? { consumedAt: row.consumed_at } : {}
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
var PostgreSQLPlatformInvitationStore = class {
|
|
3990
|
+
constructor(options) {
|
|
3991
|
+
this.pool = options.pool;
|
|
3992
|
+
}
|
|
3993
|
+
/** Lists every invitation ordered by normalized email. */
|
|
3994
|
+
async list() {
|
|
3995
|
+
const result = await this.pool.query(
|
|
3996
|
+
`SELECT * FROM ${TABLE4} ORDER BY email ASC`
|
|
3997
|
+
);
|
|
3998
|
+
return result.rows.map(mapRow4);
|
|
3999
|
+
}
|
|
4000
|
+
/** Returns the invitation for an email, or null. */
|
|
4001
|
+
async get(email) {
|
|
4002
|
+
const result = await this.pool.query(
|
|
4003
|
+
`SELECT * FROM ${TABLE4} WHERE email = $1`,
|
|
4004
|
+
[(0, import_protocols4.normalizePlatformInvitationEmail)(email)]
|
|
4005
|
+
);
|
|
4006
|
+
return result.rows[0] ? mapRow4(result.rows[0]) : null;
|
|
4007
|
+
}
|
|
4008
|
+
/**
|
|
4009
|
+
* Inserts or replaces an invitation keyed by normalized email.
|
|
4010
|
+
*
|
|
4011
|
+
* The supplied invitation replaces any existing row in full (last write wins),
|
|
4012
|
+
* including `expiresAt` and `consumedAt`.
|
|
4013
|
+
*/
|
|
4014
|
+
async upsert(invitation) {
|
|
4015
|
+
const result = await this.pool.query(
|
|
4016
|
+
`INSERT INTO ${TABLE4}
|
|
4017
|
+
(email, role, invited_by, created_at, expires_at, consumed_at)
|
|
4018
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
4019
|
+
ON CONFLICT (email) DO UPDATE SET
|
|
4020
|
+
role = EXCLUDED.role,
|
|
4021
|
+
invited_by = EXCLUDED.invited_by,
|
|
4022
|
+
created_at = EXCLUDED.created_at,
|
|
4023
|
+
expires_at = EXCLUDED.expires_at,
|
|
4024
|
+
consumed_at = EXCLUDED.consumed_at
|
|
4025
|
+
RETURNING *`,
|
|
4026
|
+
[
|
|
4027
|
+
(0, import_protocols4.normalizePlatformInvitationEmail)(invitation.email),
|
|
4028
|
+
invitation.role,
|
|
4029
|
+
invitation.invitedBy ?? null,
|
|
4030
|
+
invitation.createdAt,
|
|
4031
|
+
invitation.expiresAt ?? null,
|
|
4032
|
+
invitation.consumedAt ?? null
|
|
4033
|
+
]
|
|
4034
|
+
);
|
|
4035
|
+
return mapRow4(result.rows[0]);
|
|
4036
|
+
}
|
|
4037
|
+
/** Deletes an invitation. Returns whether a row was removed. */
|
|
4038
|
+
async delete(email) {
|
|
4039
|
+
const result = await this.pool.query(
|
|
4040
|
+
`DELETE FROM ${TABLE4} WHERE email = $1`,
|
|
4041
|
+
[(0, import_protocols4.normalizePlatformInvitationEmail)(email)]
|
|
4042
|
+
);
|
|
4043
|
+
return (result.rowCount ?? 0) > 0;
|
|
4044
|
+
}
|
|
4045
|
+
/**
|
|
4046
|
+
* Atomically consumes a pending, unexpired invitation.
|
|
4047
|
+
*
|
|
4048
|
+
* Returns the consumed invitation, or null when the row is missing, already
|
|
4049
|
+
* consumed, or expired.
|
|
4050
|
+
*/
|
|
4051
|
+
async consume(email, at) {
|
|
4052
|
+
const result = await this.pool.query(
|
|
4053
|
+
`UPDATE ${TABLE4}
|
|
4054
|
+
SET consumed_at = $1
|
|
4055
|
+
WHERE email = $2
|
|
4056
|
+
AND consumed_at IS NULL
|
|
4057
|
+
AND (expires_at IS NULL OR expires_at > $1)
|
|
4058
|
+
RETURNING *`,
|
|
4059
|
+
[at, (0, import_protocols4.normalizePlatformInvitationEmail)(email)]
|
|
4060
|
+
);
|
|
4061
|
+
return result.rows[0] ? mapRow4(result.rows[0]) : null;
|
|
4062
|
+
}
|
|
4063
|
+
};
|
|
4064
|
+
|
|
4065
|
+
// src/stores/PostgreSQLOidcIdentityClaimStore.ts
|
|
4066
|
+
var import_protocols5 = require("@axiom-lattice/protocols");
|
|
4067
|
+
var TABLE5 = "lattice_oidc_identity_claims";
|
|
4068
|
+
function mapRow5(row) {
|
|
4069
|
+
return {
|
|
4070
|
+
id: row.id,
|
|
4071
|
+
providerId: row.provider_id,
|
|
4072
|
+
issuer: row.issuer,
|
|
4073
|
+
subject: row.subject,
|
|
4074
|
+
email: row.email,
|
|
4075
|
+
...row.name !== null ? { name: row.name } : {},
|
|
4076
|
+
...row.picture !== null ? { picture: row.picture } : {},
|
|
4077
|
+
status: row.status,
|
|
4078
|
+
createdAt: row.created_at,
|
|
4079
|
+
updatedAt: row.updated_at,
|
|
4080
|
+
expiresAt: row.expires_at,
|
|
4081
|
+
...row.reviewed_by !== null ? { reviewedBy: row.reviewed_by } : {},
|
|
4082
|
+
...row.reviewed_at !== null ? { reviewedAt: row.reviewed_at } : {},
|
|
4083
|
+
...row.linked_user_id !== null ? { linkedUserId: row.linked_user_id } : {},
|
|
4084
|
+
...row.source !== null ? { source: row.source } : {},
|
|
4085
|
+
...row.invite_link_id !== null ? { inviteLinkId: row.invite_link_id } : {},
|
|
4086
|
+
...row.requested_user_id !== null ? { requestedUserId: row.requested_user_id } : {},
|
|
4087
|
+
...row.requested_platform_role !== null ? { requestedPlatformRole: row.requested_platform_role } : {},
|
|
4088
|
+
...row.requested_tenant_id !== null ? { requestedTenantId: row.requested_tenant_id } : {},
|
|
4089
|
+
...row.requested_tenant_role !== null ? { requestedTenantRole: row.requested_tenant_role } : {}
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
4092
|
+
function isUniqueViolation2(error) {
|
|
4093
|
+
return typeof error === "object" && error !== null && error.code === "23505";
|
|
4094
|
+
}
|
|
4095
|
+
var PostgreSQLOidcIdentityClaimStore = class {
|
|
4096
|
+
constructor(options) {
|
|
4097
|
+
this.pool = options.pool;
|
|
4098
|
+
}
|
|
4099
|
+
async list(status) {
|
|
4100
|
+
const result = status ? await this.pool.query(
|
|
4101
|
+
`SELECT * FROM ${TABLE5} WHERE status = $1
|
|
4102
|
+
ORDER BY created_at ASC, id ASC`,
|
|
4103
|
+
[status]
|
|
4104
|
+
) : await this.pool.query(
|
|
4105
|
+
`SELECT * FROM ${TABLE5} ORDER BY created_at ASC, id ASC`
|
|
4106
|
+
);
|
|
4107
|
+
return result.rows.map(mapRow5);
|
|
4108
|
+
}
|
|
4109
|
+
async get(id) {
|
|
4110
|
+
const result = await this.pool.query(
|
|
4111
|
+
`SELECT * FROM ${TABLE5} WHERE id = $1`,
|
|
4112
|
+
[id]
|
|
4113
|
+
);
|
|
4114
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4115
|
+
}
|
|
4116
|
+
async getPendingBySubject(providerId, issuer, subject, now) {
|
|
4117
|
+
const result = await this.pool.query(
|
|
4118
|
+
`SELECT * FROM ${TABLE5}
|
|
4119
|
+
WHERE provider_id = $1 AND issuer = $2 AND subject = $3
|
|
4120
|
+
AND status = 'pending' AND expires_at > $4`,
|
|
4121
|
+
[providerId, issuer, subject, now]
|
|
4122
|
+
);
|
|
4123
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4124
|
+
}
|
|
4125
|
+
async create(input) {
|
|
4126
|
+
const now = /* @__PURE__ */ new Date();
|
|
4127
|
+
await this.pool.query(
|
|
4128
|
+
`UPDATE ${TABLE5}
|
|
4129
|
+
SET status = 'expired', updated_at = $1
|
|
4130
|
+
WHERE provider_id = $2 AND issuer = $3 AND subject = $4
|
|
4131
|
+
AND status = 'pending' AND expires_at <= $1`,
|
|
4132
|
+
[now, input.providerId, input.issuer, input.subject]
|
|
4133
|
+
);
|
|
4134
|
+
try {
|
|
4135
|
+
const result = await this.pool.query(
|
|
4136
|
+
`INSERT INTO ${TABLE5}
|
|
4137
|
+
(id, provider_id, issuer, subject, email, name, picture, status,
|
|
4138
|
+
created_at, updated_at, expires_at, source, invite_link_id,
|
|
4139
|
+
requested_user_id, requested_platform_role, requested_tenant_id,
|
|
4140
|
+
requested_tenant_role)
|
|
4141
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8, $8, $9,
|
|
4142
|
+
$10, $11, $12, $13, $14, $15)
|
|
4143
|
+
RETURNING *`,
|
|
4144
|
+
[
|
|
4145
|
+
input.id,
|
|
4146
|
+
input.providerId,
|
|
4147
|
+
input.issuer,
|
|
4148
|
+
input.subject,
|
|
4149
|
+
(0, import_protocols5.normalizeOidcIdentityEmail)(input.email),
|
|
4150
|
+
input.name ?? null,
|
|
4151
|
+
input.picture ?? null,
|
|
4152
|
+
now,
|
|
4153
|
+
input.expiresAt,
|
|
4154
|
+
input.source ?? null,
|
|
4155
|
+
input.inviteLinkId ?? null,
|
|
4156
|
+
input.requestedUserId ?? null,
|
|
4157
|
+
input.requestedPlatformRole ?? null,
|
|
4158
|
+
input.requestedTenantId ?? null,
|
|
4159
|
+
input.requestedTenantRole ?? null
|
|
4160
|
+
]
|
|
4161
|
+
);
|
|
4162
|
+
return mapRow5(result.rows[0]);
|
|
4163
|
+
} catch (error) {
|
|
4164
|
+
if (isUniqueViolation2(error)) {
|
|
4165
|
+
throw new import_protocols5.OidcIdentityClaimConflictError(
|
|
4166
|
+
input.providerId,
|
|
4167
|
+
input.issuer,
|
|
4168
|
+
input.subject
|
|
4169
|
+
);
|
|
4170
|
+
}
|
|
4171
|
+
throw error;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
async updatePending(id, updates, now) {
|
|
4175
|
+
const sets = [];
|
|
4176
|
+
const params = [];
|
|
4177
|
+
if (updates.email !== void 0) {
|
|
4178
|
+
params.push((0, import_protocols5.normalizeOidcIdentityEmail)(updates.email));
|
|
4179
|
+
sets.push(`email = $${params.length}`);
|
|
4180
|
+
}
|
|
4181
|
+
if (updates.name !== void 0) {
|
|
4182
|
+
params.push(updates.name);
|
|
4183
|
+
sets.push(`name = $${params.length}`);
|
|
4184
|
+
}
|
|
4185
|
+
if (updates.picture !== void 0) {
|
|
4186
|
+
params.push(updates.picture);
|
|
4187
|
+
sets.push(`picture = $${params.length}`);
|
|
4188
|
+
}
|
|
4189
|
+
if (sets.length === 0) return this.get(id);
|
|
4190
|
+
params.push(now);
|
|
4191
|
+
sets.push(`updated_at = $${params.length}`);
|
|
4192
|
+
params.push(id);
|
|
4193
|
+
const result = await this.pool.query(
|
|
4194
|
+
`UPDATE ${TABLE5} SET ${sets.join(", ")}
|
|
4195
|
+
WHERE id = $${params.length} AND status = 'pending'
|
|
4196
|
+
RETURNING *`,
|
|
4197
|
+
params
|
|
4198
|
+
);
|
|
4199
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4200
|
+
}
|
|
4201
|
+
async approve(id, approval, now) {
|
|
4202
|
+
const result = await this.pool.query(
|
|
4203
|
+
`UPDATE ${TABLE5}
|
|
4204
|
+
SET status = 'approved', reviewed_by = $1, reviewed_at = $2,
|
|
4205
|
+
linked_user_id = $3, updated_at = $2
|
|
4206
|
+
WHERE id = $4 AND status = 'pending'
|
|
4207
|
+
RETURNING *`,
|
|
4208
|
+
[approval.reviewedBy, now, approval.linkedUserId, id]
|
|
4209
|
+
);
|
|
4210
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4211
|
+
}
|
|
4212
|
+
async reject(id, reviewedBy, now) {
|
|
4213
|
+
const result = await this.pool.query(
|
|
4214
|
+
`UPDATE ${TABLE5}
|
|
4215
|
+
SET status = 'rejected', reviewed_by = $1, reviewed_at = $2,
|
|
4216
|
+
updated_at = $2
|
|
4217
|
+
WHERE id = $3 AND status = 'pending'
|
|
4218
|
+
RETURNING *`,
|
|
4219
|
+
[reviewedBy, now, id]
|
|
4220
|
+
);
|
|
4221
|
+
return result.rows[0] ? mapRow5(result.rows[0]) : null;
|
|
4222
|
+
}
|
|
4223
|
+
async delete(id) {
|
|
4224
|
+
const result = await this.pool.query(
|
|
4225
|
+
`DELETE FROM ${TABLE5} WHERE id = $1`,
|
|
4226
|
+
[id]
|
|
4227
|
+
);
|
|
4228
|
+
return (result.rowCount ?? 0) > 0;
|
|
4229
|
+
}
|
|
4230
|
+
};
|
|
4231
|
+
|
|
4232
|
+
// src/stores/PostgreSQLInviteLinkStore.ts
|
|
4233
|
+
var import_core6 = require("@axiom-lattice/core");
|
|
4234
|
+
var import_protocols6 = require("@axiom-lattice/protocols");
|
|
4235
|
+
var TABLE6 = "lattice_invite_links";
|
|
4236
|
+
function mapRow6(row) {
|
|
4237
|
+
return {
|
|
4238
|
+
id: row.id,
|
|
4239
|
+
scope: row.scope,
|
|
4240
|
+
token: (0, import_core6.safeDecrypt)(row.token_encrypted, `Invite link "${row.id}" token`),
|
|
4241
|
+
tokenHash: row.token_hash,
|
|
4242
|
+
...row.email !== null ? { email: row.email } : {},
|
|
4243
|
+
...row.platform_role !== null ? { platformRole: row.platform_role } : {},
|
|
4244
|
+
...row.tenant_id !== null ? { tenantId: row.tenant_id } : {},
|
|
4245
|
+
...row.tenant_role !== null ? { tenantRole: row.tenant_role } : {},
|
|
4246
|
+
...row.created_by !== null ? { createdBy: row.created_by } : {},
|
|
4247
|
+
createdAt: row.created_at,
|
|
4248
|
+
updatedAt: row.updated_at,
|
|
4249
|
+
expiresAt: row.expires_at,
|
|
4250
|
+
maxUses: Number(row.max_uses),
|
|
4251
|
+
usedCount: Number(row.used_count),
|
|
4252
|
+
...row.revoked_at !== null ? { revokedAt: row.revoked_at } : {}
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
4255
|
+
var PostgreSQLInviteLinkStore = class {
|
|
4256
|
+
constructor(options) {
|
|
4257
|
+
this.options = options;
|
|
4258
|
+
}
|
|
4259
|
+
get pool() {
|
|
4260
|
+
return this.options.pool;
|
|
4261
|
+
}
|
|
4262
|
+
async list() {
|
|
4263
|
+
const result = await this.pool.query(
|
|
4264
|
+
`SELECT * FROM ${TABLE6} ORDER BY created_at DESC, id DESC`
|
|
4265
|
+
);
|
|
4266
|
+
return result.rows.map(mapRow6);
|
|
3614
4267
|
}
|
|
3615
|
-
async
|
|
3616
|
-
await this.ensureInitialized();
|
|
4268
|
+
async get(id) {
|
|
3617
4269
|
const result = await this.pool.query(
|
|
3618
|
-
`
|
|
3619
|
-
|
|
3620
|
-
WHERE user_id = $1 AND tenant_id = $2
|
|
3621
|
-
`,
|
|
3622
|
-
[userId, tenantId]
|
|
4270
|
+
`SELECT * FROM ${TABLE6} WHERE id = $1`,
|
|
4271
|
+
[id]
|
|
3623
4272
|
);
|
|
3624
|
-
return result.
|
|
4273
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
3625
4274
|
}
|
|
3626
|
-
async
|
|
3627
|
-
await this.ensureInitialized();
|
|
4275
|
+
async getByTokenHash(tokenHash) {
|
|
3628
4276
|
const result = await this.pool.query(
|
|
3629
|
-
`
|
|
3630
|
-
|
|
3631
|
-
WHERE user_id = $1 AND tenant_id = $2
|
|
3632
|
-
LIMIT 1
|
|
3633
|
-
`,
|
|
3634
|
-
[userId, tenantId]
|
|
4277
|
+
`SELECT * FROM ${TABLE6} WHERE token_hash = $1`,
|
|
4278
|
+
[tokenHash]
|
|
3635
4279
|
);
|
|
3636
|
-
return result.rows.
|
|
4280
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4281
|
+
}
|
|
4282
|
+
async create(input) {
|
|
4283
|
+
const now = /* @__PURE__ */ new Date();
|
|
4284
|
+
const result = await this.pool.query(
|
|
4285
|
+
`INSERT INTO ${TABLE6}
|
|
4286
|
+
(id, scope, token_hash, token_encrypted, email, platform_role,
|
|
4287
|
+
tenant_id, tenant_role, created_by, created_at, updated_at,
|
|
4288
|
+
expires_at, max_uses, used_count)
|
|
4289
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $10, $11, $12, 0)
|
|
4290
|
+
RETURNING *`,
|
|
4291
|
+
[
|
|
4292
|
+
input.id,
|
|
4293
|
+
input.scope,
|
|
4294
|
+
input.tokenHash,
|
|
4295
|
+
(0, import_core6.encrypt)(input.token),
|
|
4296
|
+
input.email ? (0, import_protocols6.normalizeInviteLinkEmail)(input.email) : null,
|
|
4297
|
+
input.platformRole ?? null,
|
|
4298
|
+
input.tenantId ?? null,
|
|
4299
|
+
input.tenantRole ?? null,
|
|
4300
|
+
input.createdBy ?? null,
|
|
4301
|
+
now,
|
|
4302
|
+
input.expiresAt,
|
|
4303
|
+
input.maxUses
|
|
4304
|
+
]
|
|
4305
|
+
);
|
|
4306
|
+
return mapRow6(result.rows[0]);
|
|
4307
|
+
}
|
|
4308
|
+
async consume(tokenHash, now) {
|
|
4309
|
+
const result = await this.pool.query(
|
|
4310
|
+
`UPDATE ${TABLE6}
|
|
4311
|
+
SET used_count = used_count + 1, updated_at = $1
|
|
4312
|
+
WHERE token_hash = $2
|
|
4313
|
+
AND revoked_at IS NULL
|
|
4314
|
+
AND expires_at > $1
|
|
4315
|
+
AND used_count < max_uses
|
|
4316
|
+
RETURNING *`,
|
|
4317
|
+
[now, tokenHash]
|
|
4318
|
+
);
|
|
4319
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
4320
|
+
}
|
|
4321
|
+
async revoke(id, now) {
|
|
4322
|
+
const result = await this.pool.query(
|
|
4323
|
+
`UPDATE ${TABLE6}
|
|
4324
|
+
SET revoked_at = COALESCE(revoked_at, $1), updated_at = $1
|
|
4325
|
+
WHERE id = $2
|
|
4326
|
+
RETURNING *`,
|
|
4327
|
+
[now, id]
|
|
4328
|
+
);
|
|
4329
|
+
return result.rows[0] ? mapRow6(result.rows[0]) : null;
|
|
3637
4330
|
}
|
|
3638
4331
|
};
|
|
3639
4332
|
|
|
@@ -5253,7 +5946,7 @@ var PostgreSQLEvalStore = class {
|
|
|
5253
5946
|
// src/stores/ThreadMessageQueueStore.ts
|
|
5254
5947
|
var import_pg13 = require("pg");
|
|
5255
5948
|
var import_crypto = __toESM(require("crypto"));
|
|
5256
|
-
var
|
|
5949
|
+
var import_protocols7 = require("@axiom-lattice/protocols");
|
|
5257
5950
|
|
|
5258
5951
|
// src/migrations/thread_message_queue_migrations.ts
|
|
5259
5952
|
var createThreadMessageQueueTable = {
|
|
@@ -5740,15 +6433,15 @@ var ThreadMessageQueueStore = class {
|
|
|
5740
6433
|
priority: row.priority || 0,
|
|
5741
6434
|
command: row.command ? typeof row.command === "string" ? JSON.parse(row.command) : row.command : void 0,
|
|
5742
6435
|
custom_run_config: row.custom_run_config ? typeof row.custom_run_config === "string" ? JSON.parse(row.custom_run_config) : row.custom_run_config : void 0,
|
|
5743
|
-
trusted_run_context: row.trusted_run_context ? (0,
|
|
5744
|
-
execution_mode: row.execution_mode == null ? void 0 : (0,
|
|
6436
|
+
trusted_run_context: row.trusted_run_context ? (0, import_protocols7.parseTrustedRunContext)(typeof row.trusted_run_context === "string" ? JSON.parse(row.trusted_run_context) : row.trusted_run_context) : void 0,
|
|
6437
|
+
execution_mode: row.execution_mode == null ? void 0 : (0, import_protocols7.parseQueuedExecutionMode)(row.execution_mode)
|
|
5745
6438
|
};
|
|
5746
6439
|
}
|
|
5747
6440
|
};
|
|
5748
6441
|
function validateQueueTrust(params) {
|
|
5749
6442
|
return {
|
|
5750
|
-
trustedRunContext: params.trusted_run_context === void 0 ? void 0 : (0,
|
|
5751
|
-
executionMode: params.execution_mode === void 0 ? void 0 : (0,
|
|
6443
|
+
trustedRunContext: params.trusted_run_context === void 0 ? void 0 : (0, import_protocols7.parseTrustedRunContext)(params.trusted_run_context),
|
|
6444
|
+
executionMode: params.execution_mode === void 0 ? void 0 : (0, import_protocols7.parseQueuedExecutionMode)(params.execution_mode)
|
|
5752
6445
|
};
|
|
5753
6446
|
}
|
|
5754
6447
|
function scopeClause(scope, start) {
|
|
@@ -5766,7 +6459,7 @@ function scopeClause(scope, start) {
|
|
|
5766
6459
|
|
|
5767
6460
|
// src/stores/ChannelBindingStore.ts
|
|
5768
6461
|
var import_pg14 = require("pg");
|
|
5769
|
-
var
|
|
6462
|
+
var import_protocols8 = require("@axiom-lattice/protocols");
|
|
5770
6463
|
|
|
5771
6464
|
// src/migrations/channel_bindings_migration.ts
|
|
5772
6465
|
var createChannelBindingsTable = {
|
|
@@ -5915,7 +6608,7 @@ var ChannelBindingStore = class {
|
|
|
5915
6608
|
return this.mapRowToBinding(result.rows[0]);
|
|
5916
6609
|
} catch (error) {
|
|
5917
6610
|
if (isBindingSubjectConflict(error)) {
|
|
5918
|
-
throw new
|
|
6611
|
+
throw new import_protocols8.DuplicateChannelBindingSubjectError();
|
|
5919
6612
|
}
|
|
5920
6613
|
throw error;
|
|
5921
6614
|
}
|
|
@@ -6117,7 +6810,7 @@ var alterChannelInstallationsTable = {
|
|
|
6117
6810
|
};
|
|
6118
6811
|
|
|
6119
6812
|
// src/stores/PostgreSQLChannelInstallationStore.ts
|
|
6120
|
-
var
|
|
6813
|
+
var import_core7 = require("@axiom-lattice/core");
|
|
6121
6814
|
var PostgreSQLChannelInstallationStore = class {
|
|
6122
6815
|
constructor(options) {
|
|
6123
6816
|
this.initialized = false;
|
|
@@ -6339,17 +7032,17 @@ var PostgreSQLChannelInstallationStore = class {
|
|
|
6339
7032
|
encryptSecrets(config) {
|
|
6340
7033
|
return {
|
|
6341
7034
|
...config,
|
|
6342
|
-
...typeof config.appSecret === "string" ? { appSecret: (0,
|
|
6343
|
-
...typeof config.verificationToken === "string" ? { verificationToken: (0,
|
|
6344
|
-
...typeof config.encryptKey === "string" ? { encryptKey: (0,
|
|
7035
|
+
...typeof config.appSecret === "string" ? { appSecret: (0, import_core7.encrypt)(config.appSecret) } : {},
|
|
7036
|
+
...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core7.encrypt)(config.verificationToken) } : {},
|
|
7037
|
+
...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core7.encrypt)(config.encryptKey) } : {}
|
|
6345
7038
|
};
|
|
6346
7039
|
}
|
|
6347
7040
|
decryptSecrets(config) {
|
|
6348
7041
|
return {
|
|
6349
7042
|
...config,
|
|
6350
|
-
...typeof config.appSecret === "string" ? { appSecret: (0,
|
|
6351
|
-
...typeof config.verificationToken === "string" ? { verificationToken: (0,
|
|
6352
|
-
...typeof config.encryptKey === "string" ? { encryptKey: (0,
|
|
7043
|
+
...typeof config.appSecret === "string" ? { appSecret: (0, import_core7.safeDecrypt)(config.appSecret) } : {},
|
|
7044
|
+
...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core7.safeDecrypt)(config.verificationToken) } : {},
|
|
7045
|
+
...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core7.safeDecrypt)(config.encryptKey) } : {}
|
|
6353
7046
|
};
|
|
6354
7047
|
}
|
|
6355
7048
|
};
|
|
@@ -6427,7 +7120,7 @@ var addApiKeyGrants = {
|
|
|
6427
7120
|
};
|
|
6428
7121
|
|
|
6429
7122
|
// src/stores/PostgreSQLA2AApiKeyStore.ts
|
|
6430
|
-
var
|
|
7123
|
+
var import_core8 = require("@axiom-lattice/core");
|
|
6431
7124
|
var import_crypto2 = require("crypto");
|
|
6432
7125
|
function generateApiKey() {
|
|
6433
7126
|
return `a2a_${(0, import_crypto2.randomUUID)().replace(/-/g, "")}`;
|
|
@@ -6435,7 +7128,7 @@ function generateApiKey() {
|
|
|
6435
7128
|
function mapRowToRecord(row) {
|
|
6436
7129
|
return {
|
|
6437
7130
|
id: row.id,
|
|
6438
|
-
key: (0,
|
|
7131
|
+
key: (0, import_core8.safeDecrypt)(row.key_value),
|
|
6439
7132
|
tenantId: row.tenant_id,
|
|
6440
7133
|
projectId: row.project_id,
|
|
6441
7134
|
assistantIds: row.assistant_ids ?? void 0,
|
|
@@ -6499,7 +7192,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6499
7192
|
);
|
|
6500
7193
|
for (const row of all.rows) {
|
|
6501
7194
|
try {
|
|
6502
|
-
if ((0,
|
|
7195
|
+
if ((0, import_core8.decrypt)(row.key_value) === key) return mapRowToRecord(row);
|
|
6503
7196
|
} catch {
|
|
6504
7197
|
continue;
|
|
6505
7198
|
}
|
|
@@ -6543,7 +7236,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6543
7236
|
`INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, assistant_ids, grants, label)
|
|
6544
7237
|
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
|
|
6545
7238
|
[
|
|
6546
|
-
(0,
|
|
7239
|
+
(0, import_core8.encrypt)(key),
|
|
6547
7240
|
input.tenantId,
|
|
6548
7241
|
input.projectId,
|
|
6549
7242
|
input.assistantIds ? JSON.stringify(input.assistantIds) : null,
|
|
@@ -6612,7 +7305,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6612
7305
|
const key = generateApiKey();
|
|
6613
7306
|
const result = await this.pool.query(
|
|
6614
7307
|
`UPDATE lattice_a2a_api_keys SET key_value = $1, updated_at = NOW() WHERE id = $2 RETURNING *`,
|
|
6615
|
-
[(0,
|
|
7308
|
+
[(0, import_core8.encrypt)(key), id]
|
|
6616
7309
|
);
|
|
6617
7310
|
if (result.rows.length === 0) throw new Error(`A2A API key not found: ${id}`);
|
|
6618
7311
|
const record = mapRowToRecord(result.rows[0]);
|
|
@@ -6631,7 +7324,7 @@ var PostgreSQLA2AApiKeyStore = class {
|
|
|
6631
7324
|
const map2 = /* @__PURE__ */ new Map();
|
|
6632
7325
|
for (const row of result.rows) {
|
|
6633
7326
|
try {
|
|
6634
|
-
const key = (0,
|
|
7327
|
+
const key = (0, import_core8.decrypt)(row.key_value);
|
|
6635
7328
|
map2.set(key, {
|
|
6636
7329
|
key,
|
|
6637
7330
|
tenantId: row.tenant_id,
|
|
@@ -6881,7 +7574,7 @@ function isIdentity(value) {
|
|
|
6881
7574
|
function isStatus(value) {
|
|
6882
7575
|
return value === "draft" || value === "active" || value === "disabled";
|
|
6883
7576
|
}
|
|
6884
|
-
function
|
|
7577
|
+
function mapRow7(row) {
|
|
6885
7578
|
if (!isStatus(row.status) || !isIntegration(row.integration) || !isScope(row.scope) || !isFeatures(row.features) || !isAppearance(row.appearance) || !isIdentity(row.identity)) {
|
|
6886
7579
|
throw new Error(`Invalid agent web app row: ${row.id}`);
|
|
6887
7580
|
}
|
|
@@ -6953,7 +7646,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6953
7646
|
ORDER BY created_at DESC, id DESC`,
|
|
6954
7647
|
[tenantId, assistantId]
|
|
6955
7648
|
);
|
|
6956
|
-
return result.rows.map(
|
|
7649
|
+
return result.rows.map(mapRow7);
|
|
6957
7650
|
}
|
|
6958
7651
|
async getById(tenantId, webAppId) {
|
|
6959
7652
|
await this.ensureInitialized();
|
|
@@ -6962,7 +7655,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6962
7655
|
WHERE tenant_id = $1 AND id = $2`,
|
|
6963
7656
|
[tenantId, webAppId]
|
|
6964
7657
|
);
|
|
6965
|
-
return result.rows[0] ?
|
|
7658
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
6966
7659
|
}
|
|
6967
7660
|
async findById(webAppId) {
|
|
6968
7661
|
await this.ensureInitialized();
|
|
@@ -6971,7 +7664,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6971
7664
|
WHERE id = $1`,
|
|
6972
7665
|
[webAppId]
|
|
6973
7666
|
);
|
|
6974
|
-
return result.rows[0] ?
|
|
7667
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
6975
7668
|
}
|
|
6976
7669
|
async create(tenantId, input) {
|
|
6977
7670
|
await this.ensureInitialized();
|
|
@@ -6995,7 +7688,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
6995
7688
|
input.identity ? JSON.stringify(input.identity) : null
|
|
6996
7689
|
]
|
|
6997
7690
|
);
|
|
6998
|
-
return
|
|
7691
|
+
return mapRow7(result.rows[0]);
|
|
6999
7692
|
}
|
|
7000
7693
|
async update(tenantId, webAppId, patch, options) {
|
|
7001
7694
|
await this.ensureInitialized();
|
|
@@ -7037,7 +7730,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
7037
7730
|
RETURNING *`,
|
|
7038
7731
|
values
|
|
7039
7732
|
);
|
|
7040
|
-
return result.rows[0] ?
|
|
7733
|
+
return result.rows[0] ? mapRow7(result.rows[0]) : null;
|
|
7041
7734
|
}
|
|
7042
7735
|
async delete(tenantId, webAppId) {
|
|
7043
7736
|
await this.ensureInitialized();
|
|
@@ -7052,7 +7745,7 @@ var PostgreSQLAgentWebAppStore = class {
|
|
|
7052
7745
|
|
|
7053
7746
|
// src/stores/PostgreSQLScheduleStorage.ts
|
|
7054
7747
|
var import_pg19 = require("pg");
|
|
7055
|
-
var
|
|
7748
|
+
var import_protocols9 = require("@axiom-lattice/protocols");
|
|
7056
7749
|
|
|
7057
7750
|
// src/migrations/schedule_migrations.ts
|
|
7058
7751
|
var createScheduledTasksTable = {
|
|
@@ -7467,10 +8160,10 @@ var PostgreSQLScheduleStorage = class {
|
|
|
7467
8160
|
WHERE task_id = $3 AND status = $4
|
|
7468
8161
|
RETURNING task_id`,
|
|
7469
8162
|
[
|
|
7470
|
-
|
|
8163
|
+
import_protocols9.ScheduledTaskStatus.RUNNING,
|
|
7471
8164
|
/* @__PURE__ */ new Date(),
|
|
7472
8165
|
taskId,
|
|
7473
|
-
|
|
8166
|
+
import_protocols9.ScheduledTaskStatus.PENDING
|
|
7474
8167
|
]
|
|
7475
8168
|
);
|
|
7476
8169
|
return (result.rowCount ?? 0) > 0;
|
|
@@ -7490,8 +8183,8 @@ var PostgreSQLScheduleStorage = class {
|
|
|
7490
8183
|
WHERE status = $2 AND updated_at < now() - ($3::bigint || ' milliseconds')::interval
|
|
7491
8184
|
RETURNING task_id`,
|
|
7492
8185
|
[
|
|
7493
|
-
|
|
7494
|
-
|
|
8186
|
+
import_protocols9.ScheduledTaskStatus.PENDING,
|
|
8187
|
+
import_protocols9.ScheduledTaskStatus.RUNNING,
|
|
7495
8188
|
olderThanMs
|
|
7496
8189
|
]
|
|
7497
8190
|
);
|
|
@@ -7514,7 +8207,7 @@ var PostgreSQLScheduleStorage = class {
|
|
|
7514
8207
|
await this.ensureInitialized();
|
|
7515
8208
|
const result = await this.pool.query(
|
|
7516
8209
|
`SELECT * FROM lattice_scheduled_tasks WHERE status IN ($1, $2) ORDER BY created_at ASC`,
|
|
7517
|
-
[
|
|
8210
|
+
[import_protocols9.ScheduledTaskStatus.PENDING, import_protocols9.ScheduledTaskStatus.PAUSED]
|
|
7518
8211
|
);
|
|
7519
8212
|
return result.rows.map((row) => this.mapRowToTask(row));
|
|
7520
8213
|
}
|
|
@@ -7673,9 +8366,9 @@ var PostgreSQLScheduleStorage = class {
|
|
|
7673
8366
|
AND updated_at < $4
|
|
7674
8367
|
`,
|
|
7675
8368
|
[
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
8369
|
+
import_protocols9.ScheduledTaskStatus.COMPLETED,
|
|
8370
|
+
import_protocols9.ScheduledTaskStatus.CANCELLED,
|
|
8371
|
+
import_protocols9.ScheduledTaskStatus.FAILED,
|
|
7679
8372
|
cutoff
|
|
7680
8373
|
]
|
|
7681
8374
|
);
|
|
@@ -8691,7 +9384,7 @@ var PostgreSQLTaskStore = class {
|
|
|
8691
9384
|
|
|
8692
9385
|
// src/stores/PostgreSQLTaskWorkItemStore.ts
|
|
8693
9386
|
var import_pg21 = require("pg");
|
|
8694
|
-
var
|
|
9387
|
+
var import_protocols10 = require("@axiom-lattice/protocols");
|
|
8695
9388
|
var import_uuid4 = require("uuid");
|
|
8696
9389
|
|
|
8697
9390
|
// src/migrations/task_work_items_migration.ts
|
|
@@ -8981,8 +9674,8 @@ var PostgreSQLTaskWorkItemStore = class {
|
|
|
8981
9674
|
}
|
|
8982
9675
|
/** List pending execution results using one bounded PostgreSQL anti-join query. */
|
|
8983
9676
|
async listPendingExecutionResults(params) {
|
|
8984
|
-
if (!Number.isSafeInteger(params.limit) || params.limit < 0 || params.limit >
|
|
8985
|
-
const error = new RangeError(`limit must be a safe integer between 0 and ${
|
|
9677
|
+
if (!Number.isSafeInteger(params.limit) || params.limit < 0 || params.limit > import_protocols10.MAX_PENDING_EXECUTION_RESULTS_LIMIT) {
|
|
9678
|
+
const error = new RangeError(`limit must be a safe integer between 0 and ${import_protocols10.MAX_PENDING_EXECUTION_RESULTS_LIMIT}`);
|
|
8986
9679
|
error.code = "INVALID_LIMIT";
|
|
8987
9680
|
throw error;
|
|
8988
9681
|
}
|
|
@@ -9835,8 +10528,8 @@ var import_langgraph_checkpoint_postgres = require("@langchain/langgraph-checkpo
|
|
|
9835
10528
|
var import_pg25 = require("pg");
|
|
9836
10529
|
var import_pgvector = require("@langchain/community/vectorstores/pgvector");
|
|
9837
10530
|
var import_documents = require("@langchain/core/documents");
|
|
9838
|
-
var
|
|
9839
|
-
var
|
|
10531
|
+
var import_core9 = require("@axiom-lattice/core");
|
|
10532
|
+
var import_protocols11 = require("@axiom-lattice/protocols");
|
|
9840
10533
|
function safeTableName(name) {
|
|
9841
10534
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
9842
10535
|
throw new Error(`Invalid table name: ${name}`);
|
|
@@ -9851,14 +10544,14 @@ var PGVectorStoreProvider = class {
|
|
|
9851
10544
|
async create(params) {
|
|
9852
10545
|
const { tableName, embeddingKey } = params;
|
|
9853
10546
|
const tenantId = params.tenantId ?? "default";
|
|
9854
|
-
const requestedKey = embeddingKey ||
|
|
9855
|
-
const embeddings = await
|
|
10547
|
+
const requestedKey = embeddingKey || import_protocols11.EMBEDDING_DEFAULT_KEY;
|
|
10548
|
+
const embeddings = await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
|
|
9856
10549
|
tenantId,
|
|
9857
10550
|
requestedKey
|
|
9858
|
-
) ?? (requestedKey ===
|
|
10551
|
+
) ?? (requestedKey === import_protocols11.EMBEDDING_DEFAULT_KEY ? void 0 : await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
|
|
9859
10552
|
tenantId,
|
|
9860
|
-
|
|
9861
|
-
)) ?? await
|
|
10553
|
+
import_protocols11.EMBEDDING_DEFAULT_KEY
|
|
10554
|
+
)) ?? await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
|
|
9862
10555
|
tenantId,
|
|
9863
10556
|
"default"
|
|
9864
10557
|
);
|
|
@@ -10126,7 +10819,7 @@ var createChannelIdentityMappingTables = {
|
|
|
10126
10819
|
};
|
|
10127
10820
|
|
|
10128
10821
|
// src/migrations/connection_config_migrations.ts
|
|
10129
|
-
var
|
|
10822
|
+
var import_core10 = require("@axiom-lattice/core");
|
|
10130
10823
|
var createConnectionConfigsTable = {
|
|
10131
10824
|
version: 160,
|
|
10132
10825
|
name: "create_connection_configs_table",
|
|
@@ -10197,8 +10890,8 @@ var reencryptLegacyConnectionSecrets = {
|
|
|
10197
10890
|
let changed = false;
|
|
10198
10891
|
for (const field of secretFields) {
|
|
10199
10892
|
const value = config[field];
|
|
10200
|
-
if (typeof value === "string" && value.length > 0 && !(0,
|
|
10201
|
-
config[field] = (0,
|
|
10893
|
+
if (typeof value === "string" && value.length > 0 && !(0, import_core10.isEncryptedValue)(value)) {
|
|
10894
|
+
config[field] = (0, import_core10.encrypt)(value);
|
|
10202
10895
|
changed = true;
|
|
10203
10896
|
}
|
|
10204
10897
|
}
|
|
@@ -10214,6 +10907,254 @@ var reencryptLegacyConnectionSecrets = {
|
|
|
10214
10907
|
}
|
|
10215
10908
|
};
|
|
10216
10909
|
|
|
10910
|
+
// src/migrations/user_external_identity_migration.ts
|
|
10911
|
+
var createUserExternalIdentityTable = {
|
|
10912
|
+
version: 209,
|
|
10913
|
+
name: "create_user_external_identity",
|
|
10914
|
+
up: async (client) => {
|
|
10915
|
+
await client.query(`
|
|
10916
|
+
CREATE TABLE IF NOT EXISTS lattice_user_identities (
|
|
10917
|
+
user_id VARCHAR(255) NOT NULL,
|
|
10918
|
+
provider_id VARCHAR(255) NOT NULL,
|
|
10919
|
+
issuer TEXT NOT NULL,
|
|
10920
|
+
subject TEXT NOT NULL,
|
|
10921
|
+
email TEXT,
|
|
10922
|
+
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10923
|
+
PRIMARY KEY (provider_id, issuer, subject)
|
|
10924
|
+
)
|
|
10925
|
+
`);
|
|
10926
|
+
await client.query(`
|
|
10927
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_user_identities_user
|
|
10928
|
+
ON lattice_user_identities (user_id)
|
|
10929
|
+
`);
|
|
10930
|
+
},
|
|
10931
|
+
down: async (client) => {
|
|
10932
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_user_identities_user");
|
|
10933
|
+
await client.query("DROP TABLE IF EXISTS lattice_user_identities");
|
|
10934
|
+
}
|
|
10935
|
+
};
|
|
10936
|
+
|
|
10937
|
+
// src/migrations/user_email_lower_unique_migration.ts
|
|
10938
|
+
var createUserEmailLowerUniqueIndex = {
|
|
10939
|
+
version: 210,
|
|
10940
|
+
name: "create_user_email_lower_unique_index",
|
|
10941
|
+
up: async (client) => {
|
|
10942
|
+
await client.query(`
|
|
10943
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_lattice_users_email_lower
|
|
10944
|
+
ON lattice_users (lower(email))
|
|
10945
|
+
`);
|
|
10946
|
+
},
|
|
10947
|
+
down: async (client) => {
|
|
10948
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_users_email_lower");
|
|
10949
|
+
}
|
|
10950
|
+
};
|
|
10951
|
+
|
|
10952
|
+
// src/migrations/oidc_provider_config_migration.ts
|
|
10953
|
+
var createOidcProviderConfigsTable = {
|
|
10954
|
+
version: 211,
|
|
10955
|
+
name: "create_oidc_provider_configs",
|
|
10956
|
+
up: async (client) => {
|
|
10957
|
+
await client.query(`
|
|
10958
|
+
CREATE TABLE IF NOT EXISTS lattice_oidc_provider_configs (
|
|
10959
|
+
id VARCHAR(64) PRIMARY KEY,
|
|
10960
|
+
display_name TEXT NOT NULL,
|
|
10961
|
+
issuer TEXT NOT NULL,
|
|
10962
|
+
client_id TEXT NOT NULL,
|
|
10963
|
+
client_secret TEXT NOT NULL,
|
|
10964
|
+
scopes TEXT NOT NULL,
|
|
10965
|
+
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
10966
|
+
jit_provisioning BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10967
|
+
auto_link_email BOOLEAN NOT NULL DEFAULT FALSE,
|
|
10968
|
+
default_tenant_id TEXT,
|
|
10969
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10970
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
10971
|
+
)
|
|
10972
|
+
`);
|
|
10973
|
+
},
|
|
10974
|
+
down: async (client) => {
|
|
10975
|
+
await client.query("DROP TABLE IF EXISTS lattice_oidc_provider_configs");
|
|
10976
|
+
}
|
|
10977
|
+
};
|
|
10978
|
+
|
|
10979
|
+
// src/migrations/platform_member_migration.ts
|
|
10980
|
+
var createPlatformMembersTable = {
|
|
10981
|
+
version: 212,
|
|
10982
|
+
name: "create_platform_members",
|
|
10983
|
+
up: async (client) => {
|
|
10984
|
+
await client.query(`
|
|
10985
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_members (
|
|
10986
|
+
user_id VARCHAR(255) PRIMARY KEY,
|
|
10987
|
+
role VARCHAR(16) NOT NULL,
|
|
10988
|
+
granted_by VARCHAR(255),
|
|
10989
|
+
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
10990
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
10991
|
+
)
|
|
10992
|
+
`);
|
|
10993
|
+
},
|
|
10994
|
+
down: async (client) => {
|
|
10995
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_members");
|
|
10996
|
+
}
|
|
10997
|
+
};
|
|
10998
|
+
|
|
10999
|
+
// src/migrations/platform_invitation_migration.ts
|
|
11000
|
+
var createPlatformInvitationsTable = {
|
|
11001
|
+
version: 213,
|
|
11002
|
+
name: "create_platform_invitations",
|
|
11003
|
+
up: async (client) => {
|
|
11004
|
+
await client.query(`
|
|
11005
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_invitations (
|
|
11006
|
+
email VARCHAR(320) PRIMARY KEY,
|
|
11007
|
+
role VARCHAR(16) NOT NULL,
|
|
11008
|
+
invited_by VARCHAR(255),
|
|
11009
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
11010
|
+
expires_at TIMESTAMPTZ,
|
|
11011
|
+
consumed_at TIMESTAMPTZ
|
|
11012
|
+
)
|
|
11013
|
+
`);
|
|
11014
|
+
},
|
|
11015
|
+
down: async (client) => {
|
|
11016
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_invitations");
|
|
11017
|
+
}
|
|
11018
|
+
};
|
|
11019
|
+
|
|
11020
|
+
// src/migrations/oidc_identity_claim_migration.ts
|
|
11021
|
+
var createOidcIdentityClaimsTable = {
|
|
11022
|
+
version: 214,
|
|
11023
|
+
name: "create_oidc_identity_claims",
|
|
11024
|
+
up: async (client) => {
|
|
11025
|
+
await client.query(`
|
|
11026
|
+
CREATE TABLE IF NOT EXISTS lattice_oidc_identity_claims (
|
|
11027
|
+
id UUID PRIMARY KEY,
|
|
11028
|
+
provider_id VARCHAR(64) NOT NULL,
|
|
11029
|
+
issuer TEXT NOT NULL,
|
|
11030
|
+
subject TEXT NOT NULL,
|
|
11031
|
+
email VARCHAR(320) NOT NULL,
|
|
11032
|
+
name TEXT,
|
|
11033
|
+
picture TEXT,
|
|
11034
|
+
status VARCHAR(16) NOT NULL,
|
|
11035
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
11036
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
11037
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
11038
|
+
reviewed_by VARCHAR(255),
|
|
11039
|
+
reviewed_at TIMESTAMPTZ,
|
|
11040
|
+
linked_user_id VARCHAR(255)
|
|
11041
|
+
)
|
|
11042
|
+
`);
|
|
11043
|
+
await client.query(`
|
|
11044
|
+
CREATE UNIQUE INDEX IF NOT EXISTS lattice_oidc_identity_claims_pending_subject
|
|
11045
|
+
ON lattice_oidc_identity_claims (provider_id, issuer, subject)
|
|
11046
|
+
WHERE status = 'pending'
|
|
11047
|
+
`);
|
|
11048
|
+
await client.query(`
|
|
11049
|
+
CREATE INDEX IF NOT EXISTS lattice_oidc_identity_claims_status_created
|
|
11050
|
+
ON lattice_oidc_identity_claims (status, created_at)
|
|
11051
|
+
`);
|
|
11052
|
+
},
|
|
11053
|
+
down: async (client) => {
|
|
11054
|
+
await client.query("DROP TABLE IF EXISTS lattice_oidc_identity_claims");
|
|
11055
|
+
}
|
|
11056
|
+
};
|
|
11057
|
+
|
|
11058
|
+
// src/migrations/invite_link_migration.ts
|
|
11059
|
+
var createInviteLinksTable = {
|
|
11060
|
+
version: 215,
|
|
11061
|
+
name: "create_invite_links",
|
|
11062
|
+
up: async (client) => {
|
|
11063
|
+
await client.query(`
|
|
11064
|
+
CREATE TABLE IF NOT EXISTS lattice_invite_links (
|
|
11065
|
+
id UUID PRIMARY KEY,
|
|
11066
|
+
scope VARCHAR(16) NOT NULL,
|
|
11067
|
+
token_hash VARCHAR(64) NOT NULL UNIQUE,
|
|
11068
|
+
token_encrypted TEXT NOT NULL,
|
|
11069
|
+
email VARCHAR(320),
|
|
11070
|
+
platform_role VARCHAR(16),
|
|
11071
|
+
tenant_id VARCHAR(255),
|
|
11072
|
+
tenant_role VARCHAR(16),
|
|
11073
|
+
created_by VARCHAR(255),
|
|
11074
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
11075
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
11076
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
11077
|
+
max_uses INTEGER NOT NULL,
|
|
11078
|
+
used_count INTEGER NOT NULL DEFAULT 0,
|
|
11079
|
+
revoked_at TIMESTAMPTZ
|
|
11080
|
+
)
|
|
11081
|
+
`);
|
|
11082
|
+
await client.query(`
|
|
11083
|
+
CREATE INDEX IF NOT EXISTS lattice_invite_links_created
|
|
11084
|
+
ON lattice_invite_links (created_at DESC)
|
|
11085
|
+
`);
|
|
11086
|
+
},
|
|
11087
|
+
down: async (client) => {
|
|
11088
|
+
await client.query("DROP TABLE IF EXISTS lattice_invite_links");
|
|
11089
|
+
}
|
|
11090
|
+
};
|
|
11091
|
+
|
|
11092
|
+
// src/migrations/oidc_identity_claim_invite_migration.ts
|
|
11093
|
+
var addOidcIdentityClaimInviteFields = {
|
|
11094
|
+
version: 216,
|
|
11095
|
+
name: "add_oidc_identity_claim_invite_fields",
|
|
11096
|
+
up: async (client) => {
|
|
11097
|
+
await client.query(`
|
|
11098
|
+
ALTER TABLE lattice_oidc_identity_claims
|
|
11099
|
+
ADD COLUMN IF NOT EXISTS source VARCHAR(32),
|
|
11100
|
+
ADD COLUMN IF NOT EXISTS invite_link_id UUID,
|
|
11101
|
+
ADD COLUMN IF NOT EXISTS requested_user_id VARCHAR(255),
|
|
11102
|
+
ADD COLUMN IF NOT EXISTS requested_platform_role VARCHAR(16),
|
|
11103
|
+
ADD COLUMN IF NOT EXISTS requested_tenant_id VARCHAR(255),
|
|
11104
|
+
ADD COLUMN IF NOT EXISTS requested_tenant_role VARCHAR(16)
|
|
11105
|
+
`);
|
|
11106
|
+
},
|
|
11107
|
+
down: async (client) => {
|
|
11108
|
+
await client.query(`
|
|
11109
|
+
ALTER TABLE lattice_oidc_identity_claims
|
|
11110
|
+
DROP COLUMN IF EXISTS source,
|
|
11111
|
+
DROP COLUMN IF EXISTS invite_link_id,
|
|
11112
|
+
DROP COLUMN IF EXISTS requested_user_id,
|
|
11113
|
+
DROP COLUMN IF EXISTS requested_platform_role,
|
|
11114
|
+
DROP COLUMN IF EXISTS requested_tenant_id,
|
|
11115
|
+
DROP COLUMN IF EXISTS requested_tenant_role
|
|
11116
|
+
`);
|
|
11117
|
+
}
|
|
11118
|
+
};
|
|
11119
|
+
|
|
11120
|
+
// src/migrations/platform_settings_migration.ts
|
|
11121
|
+
var createPlatformSettingsTable = {
|
|
11122
|
+
version: 217,
|
|
11123
|
+
name: "create_platform_settings",
|
|
11124
|
+
up: async (client) => {
|
|
11125
|
+
await client.query(`
|
|
11126
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_settings (
|
|
11127
|
+
id VARCHAR(32) PRIMARY KEY,
|
|
11128
|
+
document JSONB NOT NULL,
|
|
11129
|
+
version INTEGER NOT NULL,
|
|
11130
|
+
updated_by VARCHAR(255),
|
|
11131
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
11132
|
+
)
|
|
11133
|
+
`);
|
|
11134
|
+
},
|
|
11135
|
+
down: async (client) => {
|
|
11136
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_settings");
|
|
11137
|
+
}
|
|
11138
|
+
};
|
|
11139
|
+
|
|
11140
|
+
// src/migrations/oidc_provider_allowed_hosts_migration.ts
|
|
11141
|
+
var addOidcProviderAllowedHosts = {
|
|
11142
|
+
version: 218,
|
|
11143
|
+
name: "add_oidc_provider_allowed_hosts",
|
|
11144
|
+
up: async (client) => {
|
|
11145
|
+
await client.query(`
|
|
11146
|
+
ALTER TABLE lattice_oidc_provider_configs
|
|
11147
|
+
ADD COLUMN IF NOT EXISTS allowed_private_hosts JSONB
|
|
11148
|
+
`);
|
|
11149
|
+
},
|
|
11150
|
+
down: async (client) => {
|
|
11151
|
+
await client.query(`
|
|
11152
|
+
ALTER TABLE lattice_oidc_provider_configs
|
|
11153
|
+
DROP COLUMN IF EXISTS allowed_private_hosts
|
|
11154
|
+
`);
|
|
11155
|
+
}
|
|
11156
|
+
};
|
|
11157
|
+
|
|
10217
11158
|
// src/migrations/collection_migrations.ts
|
|
10218
11159
|
var createCollectionsTable = {
|
|
10219
11160
|
version: 137,
|
|
@@ -11279,7 +12220,7 @@ var PostgreSQLCapabilityBundleStore = class {
|
|
|
11279
12220
|
function isValidDate(value) {
|
|
11280
12221
|
return value instanceof Date && !Number.isNaN(value.getTime());
|
|
11281
12222
|
}
|
|
11282
|
-
function
|
|
12223
|
+
function mapRow8(row) {
|
|
11283
12224
|
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)) {
|
|
11284
12225
|
throw new Error("Invalid project room row");
|
|
11285
12226
|
}
|
|
@@ -11310,7 +12251,7 @@ var PostgreSQLProjectRoomStore = class {
|
|
|
11310
12251
|
RETURNING id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at`,
|
|
11311
12252
|
[input.id, input.tenantId, input.workspaceId, input.projectId, "main", input.name]
|
|
11312
12253
|
);
|
|
11313
|
-
return
|
|
12254
|
+
return mapRow8(result.rows[0]);
|
|
11314
12255
|
}
|
|
11315
12256
|
/** Finds a tenant-scoped project's main room, or returns null. */
|
|
11316
12257
|
async getMainRoom(tenantId, projectId) {
|
|
@@ -11318,7 +12259,7 @@ var PostgreSQLProjectRoomStore = class {
|
|
|
11318
12259
|
"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'",
|
|
11319
12260
|
[tenantId, projectId]
|
|
11320
12261
|
);
|
|
11321
|
-
return result.rows[0] ?
|
|
12262
|
+
return result.rows[0] ? mapRow8(result.rows[0]) : null;
|
|
11322
12263
|
}
|
|
11323
12264
|
};
|
|
11324
12265
|
|
|
@@ -11344,7 +12285,7 @@ function isRole(value) {
|
|
|
11344
12285
|
function isStatus2(value) {
|
|
11345
12286
|
return value === "active" || value === "removed";
|
|
11346
12287
|
}
|
|
11347
|
-
function
|
|
12288
|
+
function mapRow9(row) {
|
|
11348
12289
|
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");
|
|
11349
12290
|
return {
|
|
11350
12291
|
id: row.id,
|
|
@@ -11365,7 +12306,7 @@ function mapDuplicate(error) {
|
|
|
11365
12306
|
if (isDuplicate2(error)) throw new ProjectMembershipIdConflictError();
|
|
11366
12307
|
throw error;
|
|
11367
12308
|
}
|
|
11368
|
-
var
|
|
12309
|
+
var columns2 = "id, tenant_id, project_id, user_id, role, status, joined_at, updated_at";
|
|
11369
12310
|
var PostgreSQLProjectMembershipStore = class {
|
|
11370
12311
|
/** Creates a store using an externally managed pool; the pool is not migrated or closed. */
|
|
11371
12312
|
constructor(options) {
|
|
@@ -11373,13 +12314,13 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11373
12314
|
}
|
|
11374
12315
|
/** Lists memberships in stable joined-time and ID order. */
|
|
11375
12316
|
async list(tenantId, projectId) {
|
|
11376
|
-
const result = await this.pool.query(`SELECT ${
|
|
11377
|
-
return result.rows.map(
|
|
12317
|
+
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]);
|
|
12318
|
+
return result.rows.map(mapRow9);
|
|
11378
12319
|
}
|
|
11379
12320
|
/** Finds a membership by exact tenant, project, and user identity. */
|
|
11380
12321
|
async findByUser(tenantId, projectId, userId) {
|
|
11381
|
-
const result = await this.pool.query(`SELECT ${
|
|
11382
|
-
return result.rows[0] ?
|
|
12322
|
+
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]);
|
|
12323
|
+
return result.rows[0] ? mapRow9(result.rows[0]) : null;
|
|
11383
12324
|
}
|
|
11384
12325
|
/** Inserts a membership and maps database uniqueness errors to stable typed errors. */
|
|
11385
12326
|
async create(input) {
|
|
@@ -11387,8 +12328,8 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11387
12328
|
try {
|
|
11388
12329
|
await client.query("BEGIN");
|
|
11389
12330
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11390
|
-
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 ${
|
|
11391
|
-
const membership =
|
|
12331
|
+
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]);
|
|
12332
|
+
const membership = mapRow9(result.rows[0]);
|
|
11392
12333
|
await client.query("COMMIT");
|
|
11393
12334
|
return membership;
|
|
11394
12335
|
} catch (error) {
|
|
@@ -11407,16 +12348,16 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11407
12348
|
try {
|
|
11408
12349
|
await client.query("BEGIN");
|
|
11409
12350
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11410
|
-
const existingResult = await client.query(`SELECT ${
|
|
12351
|
+
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]);
|
|
11411
12352
|
if (existingResult.rows.length > 0) {
|
|
11412
|
-
const existing = existingResult.rows.map(
|
|
12353
|
+
const existing = existingResult.rows.map(mapRow9).find((item) => item.userId === input.userId && item.role === "owner" && item.status === "active");
|
|
11413
12354
|
await client.query("COMMIT");
|
|
11414
12355
|
return existing ? { kind: "existing", membership: existing } : { kind: "already_initialized" };
|
|
11415
12356
|
}
|
|
11416
12357
|
const idResult = await client.query("SELECT id FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2", [input.tenantId, input.id]);
|
|
11417
12358
|
if (idResult.rows.length > 0) throw new ProjectMembershipIdConflictError();
|
|
11418
|
-
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 ${
|
|
11419
|
-
const membership =
|
|
12359
|
+
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]);
|
|
12360
|
+
const membership = mapRow9(inserted.rows[0]);
|
|
11420
12361
|
await client.query("COMMIT");
|
|
11421
12362
|
return { kind: "created", membership };
|
|
11422
12363
|
} catch (error) {
|
|
@@ -11448,16 +12389,16 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11448
12389
|
return { kind: "not_found" };
|
|
11449
12390
|
}
|
|
11450
12391
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${tenantId}:${identity.rows[0].project_id}`]);
|
|
11451
|
-
const activeRows = await client.query(`SELECT ${
|
|
11452
|
-
const lockedMemberships = activeRows.rows.map(
|
|
12392
|
+
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]);
|
|
12393
|
+
const lockedMemberships = activeRows.rows.map(mapRow9);
|
|
11453
12394
|
let current = lockedMemberships.find((membership) => membership.id === id);
|
|
11454
12395
|
if (!current) {
|
|
11455
|
-
const target = await client.query(`SELECT ${
|
|
12396
|
+
const target = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`, [tenantId, id]);
|
|
11456
12397
|
if (!target.rows[0]) {
|
|
11457
12398
|
await client.query("COMMIT");
|
|
11458
12399
|
return { kind: "not_found" };
|
|
11459
12400
|
}
|
|
11460
|
-
current =
|
|
12401
|
+
current = mapRow9(target.rows[0]);
|
|
11461
12402
|
}
|
|
11462
12403
|
if (current.updatedAt.getTime() !== expected.getTime()) {
|
|
11463
12404
|
await client.query("COMMIT");
|
|
@@ -11470,13 +12411,13 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11470
12411
|
return { kind: "last_owner" };
|
|
11471
12412
|
}
|
|
11472
12413
|
}
|
|
11473
|
-
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 ${
|
|
12414
|
+
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]);
|
|
11474
12415
|
if (!result.rows[0]) {
|
|
11475
12416
|
await client.query("COMMIT");
|
|
11476
12417
|
return { kind: "conflict" };
|
|
11477
12418
|
}
|
|
11478
12419
|
await client.query("COMMIT");
|
|
11479
|
-
return { kind: remove ? "removed" : "updated", membership:
|
|
12420
|
+
return { kind: remove ? "removed" : "updated", membership: mapRow9(result.rows[0]) };
|
|
11480
12421
|
} catch (error) {
|
|
11481
12422
|
try {
|
|
11482
12423
|
await client.query("ROLLBACK");
|
|
@@ -11490,7 +12431,7 @@ var PostgreSQLProjectMembershipStore = class {
|
|
|
11490
12431
|
};
|
|
11491
12432
|
|
|
11492
12433
|
// src/stores/PostgreSQLProjectBotMembershipStore.ts
|
|
11493
|
-
var
|
|
12434
|
+
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";
|
|
11494
12435
|
var ProjectBotMembershipIdConflictError = class extends Error {
|
|
11495
12436
|
/** Creates an accurate tenant-scoped membership ID collision error. */
|
|
11496
12437
|
constructor(tenantId, id) {
|
|
@@ -11507,7 +12448,7 @@ function isRole2(value) {
|
|
|
11507
12448
|
function isStatus3(value) {
|
|
11508
12449
|
return value === "active" || value === "paused" || value === "removed";
|
|
11509
12450
|
}
|
|
11510
|
-
function
|
|
12451
|
+
function mapRow10(row) {
|
|
11511
12452
|
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)) {
|
|
11512
12453
|
throw new Error("Invalid project bot membership row");
|
|
11513
12454
|
}
|
|
@@ -11528,11 +12469,11 @@ function mapRow4(row) {
|
|
|
11528
12469
|
updatedAt: row.updated_at
|
|
11529
12470
|
};
|
|
11530
12471
|
}
|
|
11531
|
-
function
|
|
12472
|
+
function isUniqueViolation3(error) {
|
|
11532
12473
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
11533
12474
|
}
|
|
11534
12475
|
function constraintConflict(error) {
|
|
11535
|
-
if (!
|
|
12476
|
+
if (!isUniqueViolation3(error) || typeof error.constraint !== "string") return void 0;
|
|
11536
12477
|
if (error.constraint === "uq_lattice_project_bot_memberships_coordinator") return "coordinator_conflict";
|
|
11537
12478
|
if (error.constraint === "uq_lattice_project_bot_memberships_mention") return "mention_conflict";
|
|
11538
12479
|
return void 0;
|
|
@@ -11551,26 +12492,26 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11551
12492
|
/** Lists retained memberships in stable join order. */
|
|
11552
12493
|
async list(tenantId, projectId) {
|
|
11553
12494
|
const result = await this.pool.query(
|
|
11554
|
-
`SELECT ${
|
|
12495
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`,
|
|
11555
12496
|
[tenantId, projectId]
|
|
11556
12497
|
);
|
|
11557
|
-
return result.rows.map(
|
|
12498
|
+
return result.rows.map(mapRow10);
|
|
11558
12499
|
}
|
|
11559
12500
|
/** Finds a membership by tenant-scoped ID. */
|
|
11560
12501
|
async findById(tenantId, id) {
|
|
11561
12502
|
const result = await this.pool.query(
|
|
11562
|
-
`SELECT ${
|
|
12503
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2`,
|
|
11563
12504
|
[tenantId, id]
|
|
11564
12505
|
);
|
|
11565
|
-
return result.rows[0] ?
|
|
12506
|
+
return result.rows[0] ? mapRow10(result.rows[0]) : null;
|
|
11566
12507
|
}
|
|
11567
12508
|
/** Finds an assistant's durable membership in a tenant-scoped project. */
|
|
11568
12509
|
async findByAssistant(tenantId, projectId, assistantId) {
|
|
11569
12510
|
const result = await this.pool.query(
|
|
11570
|
-
`SELECT ${
|
|
12511
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3`,
|
|
11571
12512
|
[tenantId, projectId, assistantId]
|
|
11572
12513
|
);
|
|
11573
|
-
return result.rows[0] ?
|
|
12514
|
+
return result.rows[0] ? mapRow10(result.rows[0]) : null;
|
|
11574
12515
|
}
|
|
11575
12516
|
/** Inserts a new membership or reactivates the assistant's durable membership atomically. */
|
|
11576
12517
|
async save(input) {
|
|
@@ -11579,17 +12520,17 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11579
12520
|
await client.query("BEGIN");
|
|
11580
12521
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
|
|
11581
12522
|
const existingResult = await client.query(
|
|
11582
|
-
`SELECT ${
|
|
12523
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3 FOR UPDATE`,
|
|
11583
12524
|
[input.tenantId, input.projectId, input.assistantId]
|
|
11584
12525
|
);
|
|
11585
|
-
const existing = existingResult.rows[0] ?
|
|
12526
|
+
const existing = existingResult.rows[0] ? mapRow10(existingResult.rows[0]) : void 0;
|
|
11586
12527
|
let result;
|
|
11587
12528
|
if (existing) {
|
|
11588
12529
|
result = await client.query(
|
|
11589
12530
|
`UPDATE lattice_project_bot_memberships
|
|
11590
12531
|
SET role = $3, title = $4, responsibility = $5, mention_name = $6, status = 'active',
|
|
11591
12532
|
updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
|
|
11592
|
-
WHERE tenant_id = $1 AND id = $2 RETURNING ${
|
|
12533
|
+
WHERE tenant_id = $1 AND id = $2 RETURNING ${columns3}`,
|
|
11593
12534
|
[input.tenantId, existing.id, input.role, input.title, input.responsibility ?? null, input.mentionName]
|
|
11594
12535
|
);
|
|
11595
12536
|
} else {
|
|
@@ -11602,7 +12543,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11602
12543
|
`INSERT INTO lattice_project_bot_memberships
|
|
11603
12544
|
(id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at)
|
|
11604
12545
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'active', $11, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp()))
|
|
11605
|
-
RETURNING ${
|
|
12546
|
+
RETURNING ${columns3}`,
|
|
11606
12547
|
[
|
|
11607
12548
|
input.id,
|
|
11608
12549
|
input.tenantId,
|
|
@@ -11618,14 +12559,14 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11618
12559
|
]
|
|
11619
12560
|
);
|
|
11620
12561
|
}
|
|
11621
|
-
const membership =
|
|
12562
|
+
const membership = mapRow10(result.rows[0]);
|
|
11622
12563
|
await client.query("COMMIT");
|
|
11623
12564
|
return { kind: existing === void 0 ? "created" : existing.status === "removed" ? "reactivated" : "updated", membership };
|
|
11624
12565
|
} catch (error) {
|
|
11625
12566
|
await rollback(client);
|
|
11626
12567
|
const conflict = constraintConflict(error);
|
|
11627
12568
|
if (conflict) return { kind: conflict };
|
|
11628
|
-
if (
|
|
12569
|
+
if (isUniqueViolation3(error) && error.constraint === "lattice_project_bot_memberships_pkey") {
|
|
11629
12570
|
throw new ProjectBotMembershipIdConflictError(input.tenantId, input.id);
|
|
11630
12571
|
}
|
|
11631
12572
|
throw error;
|
|
@@ -11649,14 +12590,14 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11649
12590
|
if (typeof identity.rows[0].project_id !== "string") throw new Error("Invalid project bot membership row");
|
|
11650
12591
|
await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${identity.rows[0].project_id}`]);
|
|
11651
12592
|
const locked = await client.query(
|
|
11652
|
-
`SELECT ${
|
|
12593
|
+
`SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`,
|
|
11653
12594
|
[input.tenantId, input.id]
|
|
11654
12595
|
);
|
|
11655
12596
|
if (!locked.rows[0]) {
|
|
11656
12597
|
await client.query("COMMIT");
|
|
11657
12598
|
return { kind: "not_found" };
|
|
11658
12599
|
}
|
|
11659
|
-
const existing =
|
|
12600
|
+
const existing = mapRow10(locked.rows[0]);
|
|
11660
12601
|
if (existing.updatedAt.getTime() !== input.expectedUpdatedAt.getTime()) {
|
|
11661
12602
|
await client.query("COMMIT");
|
|
11662
12603
|
return { kind: "conflict" };
|
|
@@ -11667,7 +12608,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11667
12608
|
SET role = $4, title = $5, responsibility = $6, mention_name = $7, status = $8,
|
|
11668
12609
|
updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
|
|
11669
12610
|
WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3
|
|
11670
|
-
RETURNING ${
|
|
12611
|
+
RETURNING ${columns3}`,
|
|
11671
12612
|
[
|
|
11672
12613
|
input.tenantId,
|
|
11673
12614
|
input.id,
|
|
@@ -11683,7 +12624,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11683
12624
|
await client.query("COMMIT");
|
|
11684
12625
|
return { kind: "conflict" };
|
|
11685
12626
|
}
|
|
11686
|
-
const membership =
|
|
12627
|
+
const membership = mapRow10(updated.rows[0]);
|
|
11687
12628
|
await client.query("COMMIT");
|
|
11688
12629
|
return { kind: "updated", membership };
|
|
11689
12630
|
} catch (error) {
|
|
@@ -11698,7 +12639,7 @@ var PostgreSQLProjectBotMembershipStore = class {
|
|
|
11698
12639
|
};
|
|
11699
12640
|
|
|
11700
12641
|
// src/stores/PostgreSQLProjectRoomMessageStore.ts
|
|
11701
|
-
var
|
|
12642
|
+
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";
|
|
11702
12643
|
var ProjectRoomMessageIdConflictError = class extends Error {
|
|
11703
12644
|
/** Creates an accurate tenant-scoped message ID collision error. */
|
|
11704
12645
|
constructor(tenantId, id) {
|
|
@@ -11739,7 +12680,7 @@ function mapMention(value) {
|
|
|
11739
12680
|
function isSource(value) {
|
|
11740
12681
|
return value === "user" || value === "agent" || value === "task" || value === "routine" || value === "system";
|
|
11741
12682
|
}
|
|
11742
|
-
function
|
|
12683
|
+
function mapRow11(row) {
|
|
11743
12684
|
const author = mapAuthor(row.author);
|
|
11744
12685
|
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;
|
|
11745
12686
|
const mentions = Array.isArray(row.mentions) ? row.mentions.map(mapMention) : void 0;
|
|
@@ -11766,7 +12707,7 @@ function mapRowsTolerant(rows, scope) {
|
|
|
11766
12707
|
const messages = [];
|
|
11767
12708
|
for (const row of rows) {
|
|
11768
12709
|
try {
|
|
11769
|
-
messages.push(
|
|
12710
|
+
messages.push(mapRow11(row));
|
|
11770
12711
|
} catch (error) {
|
|
11771
12712
|
console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
|
|
11772
12713
|
tenantId: scope.tenantId,
|
|
@@ -11778,7 +12719,7 @@ function mapRowsTolerant(rows, scope) {
|
|
|
11778
12719
|
}
|
|
11779
12720
|
return messages;
|
|
11780
12721
|
}
|
|
11781
|
-
function
|
|
12722
|
+
function isUniqueViolation4(error) {
|
|
11782
12723
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
11783
12724
|
}
|
|
11784
12725
|
var PostgreSQLProjectRoomMessageStore = class {
|
|
@@ -11793,15 +12734,15 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11793
12734
|
`INSERT INTO lattice_project_room_messages
|
|
11794
12735
|
(id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at)
|
|
11795
12736
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
|
|
11796
|
-
RETURNING ${
|
|
12737
|
+
RETURNING ${columns4}`,
|
|
11797
12738
|
this.parameters(input)
|
|
11798
12739
|
);
|
|
11799
|
-
return
|
|
12740
|
+
return mapRow11(result.rows[0]);
|
|
11800
12741
|
} catch (error) {
|
|
11801
|
-
if (
|
|
12742
|
+
if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
|
|
11802
12743
|
throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
|
|
11803
12744
|
}
|
|
11804
|
-
if (
|
|
12745
|
+
if (isUniqueViolation4(error) && error.constraint === "uq_lattice_project_room_messages_idempotency") {
|
|
11805
12746
|
throw new DuplicateProjectRoomMessageIdempotencyKeyError(input.tenantId, input.roomId, input.idempotencyKey ?? "");
|
|
11806
12747
|
}
|
|
11807
12748
|
throw error;
|
|
@@ -11816,12 +12757,12 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11816
12757
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
|
|
11817
12758
|
ON CONFLICT (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL
|
|
11818
12759
|
DO UPDATE SET idempotency_key = lattice_project_room_messages.idempotency_key
|
|
11819
|
-
RETURNING ${
|
|
12760
|
+
RETURNING ${columns4}`,
|
|
11820
12761
|
this.parameters(input)
|
|
11821
12762
|
);
|
|
11822
|
-
return
|
|
12763
|
+
return mapRow11(result.rows[0]);
|
|
11823
12764
|
} catch (error) {
|
|
11824
|
-
if (
|
|
12765
|
+
if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
|
|
11825
12766
|
throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
|
|
11826
12767
|
}
|
|
11827
12768
|
throw error;
|
|
@@ -11834,12 +12775,12 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11834
12775
|
}
|
|
11835
12776
|
const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
|
|
11836
12777
|
const result = input.before ? await this.pool.query(
|
|
11837
|
-
`SELECT ${
|
|
12778
|
+
`SELECT ${columns4} FROM lattice_project_room_messages
|
|
11838
12779
|
WHERE tenant_id = $1 AND room_id = $2 AND (created_at < $3 OR (created_at = $3 AND id < $4))
|
|
11839
12780
|
ORDER BY created_at DESC, id DESC LIMIT $5`,
|
|
11840
12781
|
[input.tenantId, input.roomId, input.before.createdAt, input.before.id, limit]
|
|
11841
12782
|
) : await this.pool.query(
|
|
11842
|
-
`SELECT ${
|
|
12783
|
+
`SELECT ${columns4} FROM lattice_project_room_messages
|
|
11843
12784
|
WHERE tenant_id = $1 AND room_id = $2 ORDER BY created_at DESC, id DESC LIMIT $3`,
|
|
11844
12785
|
[input.tenantId, input.roomId, limit]
|
|
11845
12786
|
);
|
|
@@ -11848,10 +12789,10 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11848
12789
|
/** Finds a message by tenant-scoped ID. */
|
|
11849
12790
|
async findById(tenantId, id) {
|
|
11850
12791
|
const result = await this.pool.query(
|
|
11851
|
-
`SELECT ${
|
|
12792
|
+
`SELECT ${columns4} FROM lattice_project_room_messages WHERE tenant_id = $1 AND id = $2`,
|
|
11852
12793
|
[tenantId, id]
|
|
11853
12794
|
);
|
|
11854
|
-
return result.rows[0] ?
|
|
12795
|
+
return result.rows[0] ? mapRow11(result.rows[0]) : null;
|
|
11855
12796
|
}
|
|
11856
12797
|
/** Counts messages created strictly after a horizon, optionally excluding one human author. */
|
|
11857
12798
|
async countAfter(input) {
|
|
@@ -11909,7 +12850,7 @@ var PostgreSQLProjectRoomMessageStore = class {
|
|
|
11909
12850
|
};
|
|
11910
12851
|
|
|
11911
12852
|
// src/stores/PostgreSQLProjectRoomReadStateStore.ts
|
|
11912
|
-
function
|
|
12853
|
+
function mapRow12(row) {
|
|
11913
12854
|
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())) {
|
|
11914
12855
|
throw new Error("Invalid project room read state row");
|
|
11915
12856
|
}
|
|
@@ -11927,7 +12868,7 @@ var PostgreSQLProjectRoomReadStateStore = class {
|
|
|
11927
12868
|
FROM lattice_project_room_read_states WHERE tenant_id = $1 AND room_id = $2 AND user_id = $3`,
|
|
11928
12869
|
[tenantId, roomId, userId]
|
|
11929
12870
|
);
|
|
11930
|
-
return result.rows[0] ?
|
|
12871
|
+
return result.rows[0] ? mapRow12(result.rows[0]) : null;
|
|
11931
12872
|
}
|
|
11932
12873
|
/** Upserts the marker monotonically using GREATEST so older reports never regress it. */
|
|
11933
12874
|
async markRead(input) {
|
|
@@ -11943,14 +12884,14 @@ var PostgreSQLProjectRoomReadStateStore = class {
|
|
|
11943
12884
|
RETURNING tenant_id, room_id, user_id, last_read_at, updated_at`,
|
|
11944
12885
|
[input.tenantId, input.roomId, input.userId, input.lastReadAt]
|
|
11945
12886
|
);
|
|
11946
|
-
return
|
|
12887
|
+
return mapRow12(result.rows[0]);
|
|
11947
12888
|
}
|
|
11948
12889
|
};
|
|
11949
12890
|
|
|
11950
12891
|
// src/stores/PostgreSQLUserPushSubscriptionStore.ts
|
|
11951
12892
|
var import_node_crypto2 = require("crypto");
|
|
11952
|
-
var
|
|
11953
|
-
function
|
|
12893
|
+
var columns5 = "id, tenant_id, user_id, endpoint, p256dh, auth, user_agent, created_at";
|
|
12894
|
+
function mapRow13(row) {
|
|
11954
12895
|
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())) {
|
|
11955
12896
|
throw new Error("Invalid user push subscription row");
|
|
11956
12897
|
}
|
|
@@ -11972,22 +12913,22 @@ var PostgreSQLUserPushSubscriptionStore = class {
|
|
|
11972
12913
|
/** Lists every subscription for a user. */
|
|
11973
12914
|
async list(tenantId, userId) {
|
|
11974
12915
|
const result = await this.pool.query(
|
|
11975
|
-
`SELECT ${
|
|
12916
|
+
`SELECT ${columns5} FROM lattice_user_push_subscriptions WHERE tenant_id = $1 AND user_id = $2`,
|
|
11976
12917
|
[tenantId, userId]
|
|
11977
12918
|
);
|
|
11978
|
-
return result.rows.map(
|
|
12919
|
+
return result.rows.map(mapRow13);
|
|
11979
12920
|
}
|
|
11980
12921
|
/** Upserts a subscription keyed by tenant + user + endpoint. */
|
|
11981
12922
|
async save(input) {
|
|
11982
12923
|
const result = await this.pool.query(
|
|
11983
|
-
`INSERT INTO lattice_user_push_subscriptions (${
|
|
12924
|
+
`INSERT INTO lattice_user_push_subscriptions (${columns5})
|
|
11984
12925
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
|
|
11985
12926
|
ON CONFLICT (tenant_id, user_id, endpoint) DO UPDATE
|
|
11986
12927
|
SET p256dh = EXCLUDED.p256dh, auth = EXCLUDED.auth, user_agent = EXCLUDED.user_agent
|
|
11987
|
-
RETURNING ${
|
|
12928
|
+
RETURNING ${columns5}`,
|
|
11988
12929
|
[(0, import_node_crypto2.randomUUID)(), input.tenantId, input.userId, input.endpoint, input.keys.p256dh, input.keys.auth, input.userAgent ?? null]
|
|
11989
12930
|
);
|
|
11990
|
-
return
|
|
12931
|
+
return mapRow13(result.rows[0]);
|
|
11991
12932
|
}
|
|
11992
12933
|
/** Deletes one subscription by endpoint. */
|
|
11993
12934
|
async delete(tenantId, userId, endpoint) {
|
|
@@ -12001,7 +12942,7 @@ var PostgreSQLUserPushSubscriptionStore = class {
|
|
|
12001
12942
|
|
|
12002
12943
|
// src/stores/PostgreSQLModelProviderStore.ts
|
|
12003
12944
|
var import_pg27 = require("pg");
|
|
12004
|
-
var
|
|
12945
|
+
var import_core11 = require("@axiom-lattice/core");
|
|
12005
12946
|
function extraOrUndefined(value) {
|
|
12006
12947
|
return value && Object.keys(value).length > 0 ? value : void 0;
|
|
12007
12948
|
}
|
|
@@ -12125,7 +13066,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
12125
13066
|
await this.ensureInitialized();
|
|
12126
13067
|
const now = /* @__PURE__ */ new Date();
|
|
12127
13068
|
const nowString = now.toISOString();
|
|
12128
|
-
const apiKeyEnc = data.apiKey ? (0,
|
|
13069
|
+
const apiKeyEnc = data.apiKey ? (0, import_core11.encrypt)(data.apiKey) : null;
|
|
12129
13070
|
const apiKeyHint = data.apiKey ? data.apiKey.slice(-4) : null;
|
|
12130
13071
|
await this.pool.query(
|
|
12131
13072
|
`
|
|
@@ -12201,7 +13142,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
12201
13142
|
updateData.api_key_enc = null;
|
|
12202
13143
|
updateData.api_key_hint = null;
|
|
12203
13144
|
} else if (updates.apiKey) {
|
|
12204
|
-
updateData.api_key_enc = (0,
|
|
13145
|
+
updateData.api_key_enc = (0, import_core11.encrypt)(updates.apiKey);
|
|
12205
13146
|
updateData.api_key_hint = updates.apiKey.slice(-4);
|
|
12206
13147
|
}
|
|
12207
13148
|
if (updates.enabled !== void 0) {
|
|
@@ -12521,7 +13462,7 @@ var PostgreSQLModelProviderStore = class {
|
|
|
12521
13462
|
mapRowToEntry(row) {
|
|
12522
13463
|
let apiKey;
|
|
12523
13464
|
if (row.api_key_enc) {
|
|
12524
|
-
apiKey = (0,
|
|
13465
|
+
apiKey = (0, import_core11.safeDecrypt)(row.api_key_enc);
|
|
12525
13466
|
}
|
|
12526
13467
|
return {
|
|
12527
13468
|
id: row.id,
|
|
@@ -12774,7 +13715,7 @@ var PostgreSQLOutboxStore = class {
|
|
|
12774
13715
|
};
|
|
12775
13716
|
|
|
12776
13717
|
// src/stores/PostgreSQLPluginDeploymentStore.ts
|
|
12777
|
-
var
|
|
13718
|
+
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";
|
|
12778
13719
|
var statuses = [
|
|
12779
13720
|
"requested",
|
|
12780
13721
|
"provisioning",
|
|
@@ -12787,7 +13728,7 @@ var statuses = [
|
|
|
12787
13728
|
function isStatus4(value) {
|
|
12788
13729
|
return typeof value === "string" && statuses.includes(value);
|
|
12789
13730
|
}
|
|
12790
|
-
function
|
|
13731
|
+
function mapRow14(row) {
|
|
12791
13732
|
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())) {
|
|
12792
13733
|
throw new Error("Invalid plugin deployment row");
|
|
12793
13734
|
}
|
|
@@ -12818,23 +13759,23 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12818
13759
|
/** Lists every deployment for a tenant, ordered by plugin type. */
|
|
12819
13760
|
async listByTenant(tenantId) {
|
|
12820
13761
|
const result = await this.pool.query(
|
|
12821
|
-
`SELECT ${
|
|
13762
|
+
`SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 ORDER BY plugin_type`,
|
|
12822
13763
|
[tenantId]
|
|
12823
13764
|
);
|
|
12824
|
-
return result.rows.map(
|
|
13765
|
+
return result.rows.map(mapRow14);
|
|
12825
13766
|
}
|
|
12826
13767
|
/** Returns the deployment for a tenant + plugin type, or undefined. */
|
|
12827
13768
|
async get(tenantId, pluginType) {
|
|
12828
13769
|
const result = await this.pool.query(
|
|
12829
|
-
`SELECT ${
|
|
13770
|
+
`SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 AND plugin_type = $2`,
|
|
12830
13771
|
[tenantId, pluginType]
|
|
12831
13772
|
);
|
|
12832
|
-
return result.rows[0] ?
|
|
13773
|
+
return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
|
|
12833
13774
|
}
|
|
12834
13775
|
/** Upserts a deployment keyed by tenant + plugin type, preserving the original id and createdAt. */
|
|
12835
13776
|
async upsert(deployment) {
|
|
12836
13777
|
const result = await this.pool.query(
|
|
12837
|
-
`INSERT INTO lattice_plugin_deployments (${
|
|
13778
|
+
`INSERT INTO lattice_plugin_deployments (${columns6})
|
|
12838
13779
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
|
12839
13780
|
ON CONFLICT (tenant_id, plugin_type) DO UPDATE SET
|
|
12840
13781
|
plugin_version = EXCLUDED.plugin_version,
|
|
@@ -12849,7 +13790,7 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12849
13790
|
last_health_at = EXCLUDED.last_health_at,
|
|
12850
13791
|
last_error = EXCLUDED.last_error,
|
|
12851
13792
|
updated_at = NOW()
|
|
12852
|
-
RETURNING ${
|
|
13793
|
+
RETURNING ${columns6}`,
|
|
12853
13794
|
[
|
|
12854
13795
|
deployment.id,
|
|
12855
13796
|
deployment.tenantId,
|
|
@@ -12869,7 +13810,7 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12869
13810
|
new Date(deployment.updatedAt)
|
|
12870
13811
|
]
|
|
12871
13812
|
);
|
|
12872
|
-
return
|
|
13813
|
+
return mapRow14(result.rows[0]);
|
|
12873
13814
|
}
|
|
12874
13815
|
/** Applies a partial update to mutable columns; returns undefined when no row exists. */
|
|
12875
13816
|
async updateStatus(tenantId, pluginType, patch) {
|
|
@@ -12897,10 +13838,10 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12897
13838
|
const result = await this.pool.query(
|
|
12898
13839
|
`UPDATE lattice_plugin_deployments SET ${assignments.join(", ")}
|
|
12899
13840
|
WHERE tenant_id = $${tenantParam} AND plugin_type = $${pluginParam}
|
|
12900
|
-
RETURNING ${
|
|
13841
|
+
RETURNING ${columns6}`,
|
|
12901
13842
|
params
|
|
12902
13843
|
);
|
|
12903
|
-
return result.rows[0] ?
|
|
13844
|
+
return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
|
|
12904
13845
|
}
|
|
12905
13846
|
/** Deletes a deployment by tenant + plugin type; returns whether a row was removed. */
|
|
12906
13847
|
async delete(tenantId, pluginType) {
|
|
@@ -12914,7 +13855,7 @@ var PostgreSQLPluginDeploymentStore = class {
|
|
|
12914
13855
|
|
|
12915
13856
|
// src/stores/PostgreSQLAgentRunLedger.ts
|
|
12916
13857
|
var import_pg30 = require("pg");
|
|
12917
|
-
var
|
|
13858
|
+
var import_core12 = require("@axiom-lattice/core");
|
|
12918
13859
|
var DEFAULT_LIST_LIMIT = 50;
|
|
12919
13860
|
var MAX_LIST_LIMIT = 200;
|
|
12920
13861
|
var CLAIM_RUN_SQL = `UPDATE lattice_agent_runs AS r
|
|
@@ -13048,7 +13989,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13048
13989
|
} catch (error) {
|
|
13049
13990
|
await client.query("ROLLBACK");
|
|
13050
13991
|
if (isActiveRunViolation(error)) {
|
|
13051
|
-
throw new
|
|
13992
|
+
throw new import_core12.AgentRunAlreadyActiveError();
|
|
13052
13993
|
}
|
|
13053
13994
|
throw error;
|
|
13054
13995
|
} finally {
|
|
@@ -13060,19 +14001,19 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13060
14001
|
const source = input.source ?? "run_api";
|
|
13061
14002
|
if (source === "project_task") {
|
|
13062
14003
|
if (input.taskId === void 0 || input.taskId.trim().length === 0) {
|
|
13063
|
-
throw new
|
|
14004
|
+
throw new import_core12.AgentRunInvalidTaskIdError(
|
|
13064
14005
|
"A project_task run requires a non-empty taskId"
|
|
13065
14006
|
);
|
|
13066
14007
|
}
|
|
13067
14008
|
} else if (input.taskId !== void 0) {
|
|
13068
|
-
throw new
|
|
14009
|
+
throw new import_core12.AgentRunInvalidTaskIdError(
|
|
13069
14010
|
`A ${source} run cannot carry a taskId`
|
|
13070
14011
|
);
|
|
13071
14012
|
}
|
|
13072
14013
|
if (input.input != null && input.command != null) {
|
|
13073
|
-
throw new
|
|
14014
|
+
throw new import_core12.AgentRunInputCommandConflictError();
|
|
13074
14015
|
}
|
|
13075
|
-
const inputMessageIds = input.inputMessageIds ? (0,
|
|
14016
|
+
const inputMessageIds = input.inputMessageIds ? (0, import_core12.normalizeAgentRunMessageIds)(input.inputMessageIds) : (0, import_core12.extractAgentRunInputMessageIds)(input.input);
|
|
13076
14017
|
const availableAt = input.availableAt ?? null;
|
|
13077
14018
|
const limit = input.maxActiveRunsForTenant;
|
|
13078
14019
|
const client = await this.pool.connect();
|
|
@@ -13099,7 +14040,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13099
14040
|
if ((duplicate.rowCount ?? 0) > 0) {
|
|
13100
14041
|
const existing = duplicate.rows[0].input_message_ids;
|
|
13101
14042
|
const messageId = existing.find((id) => inputMessageIds.includes(id)) ?? inputMessageIds[0];
|
|
13102
|
-
throw new
|
|
14043
|
+
throw new import_core12.AgentRunDuplicateMessageIdError(messageId);
|
|
13103
14044
|
}
|
|
13104
14045
|
}
|
|
13105
14046
|
if (limit !== void 0 && limit > 0) {
|
|
@@ -13112,7 +14053,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13112
14053
|
[input.tenantId]
|
|
13113
14054
|
);
|
|
13114
14055
|
if (Number(active.rows[0].count) >= limit) {
|
|
13115
|
-
throw new
|
|
14056
|
+
throw new import_core12.AgentRunTenantQuotaExceededError(input.tenantId, limit);
|
|
13116
14057
|
}
|
|
13117
14058
|
}
|
|
13118
14059
|
const result = await client.query(
|
|
@@ -13169,7 +14110,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13169
14110
|
}
|
|
13170
14111
|
const maxRunning = positiveLimit(options?.maxRunningPerTenant);
|
|
13171
14112
|
const sources = options?.sources === void 0 ? ["run_api"] : [...options.sources];
|
|
13172
|
-
(0,
|
|
14113
|
+
(0, import_core12.assertQueueSources)(sources);
|
|
13173
14114
|
const params = [limit];
|
|
13174
14115
|
let cursorClause = "";
|
|
13175
14116
|
if (after) {
|
|
@@ -13241,7 +14182,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13241
14182
|
async claimRun(input) {
|
|
13242
14183
|
const maxRunning = positiveLimit(input.maxRunningPerTenant);
|
|
13243
14184
|
const sources = input.sources === void 0 ? ["run_api"] : [...input.sources];
|
|
13244
|
-
(0,
|
|
14185
|
+
(0, import_core12.assertQueueSources)(sources);
|
|
13245
14186
|
if (maxRunning !== void 0) {
|
|
13246
14187
|
return this.claimRunWithTenantCap(input, maxRunning, sources);
|
|
13247
14188
|
}
|
|
@@ -13710,7 +14651,7 @@ var PostgreSQLAgentRunLedger = class {
|
|
|
13710
14651
|
}
|
|
13711
14652
|
/** Reports expired run-queue rows for the requested sources. */
|
|
13712
14653
|
async reapExpired(sources = ["run_api"]) {
|
|
13713
|
-
(0,
|
|
14654
|
+
(0, import_core12.assertQueueSources)(sources);
|
|
13714
14655
|
const result = await this.pool.query(
|
|
13715
14656
|
`SELECT * FROM lattice_agent_runs
|
|
13716
14657
|
WHERE source = ANY($1::text[]) AND status = 'running'
|
|
@@ -13856,11 +14797,82 @@ function isActiveRunViolation(error) {
|
|
|
13856
14797
|
return code === "23505" && constraint === "uq_lattice_agent_runs_one_running";
|
|
13857
14798
|
}
|
|
13858
14799
|
|
|
14800
|
+
// src/stores/PostgreSQLPlatformSettingsStore.ts
|
|
14801
|
+
var import_protocols12 = require("@axiom-lattice/protocols");
|
|
14802
|
+
var TABLE7 = "lattice_platform_settings";
|
|
14803
|
+
var SETTINGS_ID = "default";
|
|
14804
|
+
var COLUMNS = "id, document, version, updated_by, updated_at";
|
|
14805
|
+
function normalizeUpdatedBy(updatedBy) {
|
|
14806
|
+
const trimmed = updatedBy?.trim();
|
|
14807
|
+
return trimmed ? trimmed : null;
|
|
14808
|
+
}
|
|
14809
|
+
function mapRow15(row) {
|
|
14810
|
+
return {
|
|
14811
|
+
...row.document,
|
|
14812
|
+
version: Number(row.version),
|
|
14813
|
+
...row.updated_by !== null ? { updatedBy: row.updated_by } : {},
|
|
14814
|
+
updatedAt: row.updated_at
|
|
14815
|
+
};
|
|
14816
|
+
}
|
|
14817
|
+
var PostgreSQLPlatformSettingsStore = class {
|
|
14818
|
+
constructor(options) {
|
|
14819
|
+
this.options = options;
|
|
14820
|
+
}
|
|
14821
|
+
get pool() {
|
|
14822
|
+
return this.options.pool;
|
|
14823
|
+
}
|
|
14824
|
+
async get() {
|
|
14825
|
+
const result = await this.pool.query(
|
|
14826
|
+
`SELECT ${COLUMNS} FROM ${TABLE7} WHERE id = $1`,
|
|
14827
|
+
[SETTINGS_ID]
|
|
14828
|
+
);
|
|
14829
|
+
return result.rows[0] ? mapRow15(result.rows[0]) : null;
|
|
14830
|
+
}
|
|
14831
|
+
async save(document, expectedVersion, updatedBy) {
|
|
14832
|
+
const version = expectedVersion + 1;
|
|
14833
|
+
const result = expectedVersion === 0 ? await this.pool.query(
|
|
14834
|
+
`INSERT INTO ${TABLE7} (id, document, version, updated_by, updated_at)
|
|
14835
|
+
VALUES ($1, $2::jsonb, $3, $4, NOW())
|
|
14836
|
+
ON CONFLICT (id) DO NOTHING
|
|
14837
|
+
RETURNING ${COLUMNS}`,
|
|
14838
|
+
[
|
|
14839
|
+
SETTINGS_ID,
|
|
14840
|
+
JSON.stringify(document),
|
|
14841
|
+
version,
|
|
14842
|
+
normalizeUpdatedBy(updatedBy)
|
|
14843
|
+
]
|
|
14844
|
+
) : await this.pool.query(
|
|
14845
|
+
`UPDATE ${TABLE7}
|
|
14846
|
+
SET document = $2::jsonb, version = $3, updated_by = $4, updated_at = NOW()
|
|
14847
|
+
WHERE id = $1 AND version = $5
|
|
14848
|
+
RETURNING ${COLUMNS}`,
|
|
14849
|
+
[
|
|
14850
|
+
SETTINGS_ID,
|
|
14851
|
+
JSON.stringify(document),
|
|
14852
|
+
version,
|
|
14853
|
+
normalizeUpdatedBy(updatedBy),
|
|
14854
|
+
expectedVersion
|
|
14855
|
+
]
|
|
14856
|
+
);
|
|
14857
|
+
if (result.rows[0]) {
|
|
14858
|
+
return mapRow15(result.rows[0]);
|
|
14859
|
+
}
|
|
14860
|
+
const actual = await this.pool.query(
|
|
14861
|
+
`SELECT version FROM ${TABLE7} WHERE id = $1`,
|
|
14862
|
+
[SETTINGS_ID]
|
|
14863
|
+
);
|
|
14864
|
+
throw new import_protocols12.PlatformSettingsVersionConflictError(
|
|
14865
|
+
expectedVersion,
|
|
14866
|
+
actual.rows[0] ? Number(actual.rows[0].version) : 0
|
|
14867
|
+
);
|
|
14868
|
+
}
|
|
14869
|
+
};
|
|
14870
|
+
|
|
13859
14871
|
// src/stores/PostgreSQLChannelReplyJobStore.ts
|
|
13860
14872
|
var import_pg31 = require("pg");
|
|
13861
|
-
var
|
|
14873
|
+
var import_protocols13 = require("@axiom-lattice/protocols");
|
|
13862
14874
|
var DB_CLOCK_MS2 = "FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint";
|
|
13863
|
-
var
|
|
14875
|
+
var COLUMNS2 = `run_id, tenant_id, assistant_id, thread_id, adapter_channel,
|
|
13864
14876
|
channel_installation_id, reply_target, input_message_id, stream_cursor, status,
|
|
13865
14877
|
attempts, available_at, lease_owner, lease_expires_at, last_error,
|
|
13866
14878
|
output_message_id, created_at, updated_at, delivered_at`;
|
|
@@ -13878,7 +14890,7 @@ function toStatus(value) {
|
|
|
13878
14890
|
}
|
|
13879
14891
|
throw new Error(`Invalid channel reply job status: ${value}`);
|
|
13880
14892
|
}
|
|
13881
|
-
function
|
|
14893
|
+
function mapRow16(row) {
|
|
13882
14894
|
return {
|
|
13883
14895
|
runId: row.run_id,
|
|
13884
14896
|
tenantId: row.tenant_id,
|
|
@@ -13949,7 +14961,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13949
14961
|
}
|
|
13950
14962
|
/** Creates a pending job with database-clock created/updated timestamps. */
|
|
13951
14963
|
async create(input) {
|
|
13952
|
-
(0,
|
|
14964
|
+
(0, import_protocols13.assertValidCreateChannelReplyJobInput)(input);
|
|
13953
14965
|
const result = await this.pool.query(
|
|
13954
14966
|
`WITH clock AS (SELECT ${DB_CLOCK_MS2} AS now_ms)
|
|
13955
14967
|
INSERT INTO lattice_channel_reply_jobs (
|
|
@@ -13964,7 +14976,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13964
14976
|
'pending', 0, $10, NULL, NULL,
|
|
13965
14977
|
NULL, NULL, clock.now_ms, clock.now_ms, NULL
|
|
13966
14978
|
FROM clock
|
|
13967
|
-
RETURNING ${
|
|
14979
|
+
RETURNING ${COLUMNS2}`,
|
|
13968
14980
|
[
|
|
13969
14981
|
input.runId,
|
|
13970
14982
|
input.tenantId,
|
|
@@ -13978,31 +14990,31 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
13978
14990
|
input.availableAt
|
|
13979
14991
|
]
|
|
13980
14992
|
);
|
|
13981
|
-
return
|
|
14993
|
+
return mapRow16(result.rows[0]);
|
|
13982
14994
|
}
|
|
13983
14995
|
/** Reads one job within a tenant scope. */
|
|
13984
14996
|
async get(tenantId, runId) {
|
|
13985
14997
|
const result = await this.pool.query(
|
|
13986
|
-
`SELECT ${
|
|
14998
|
+
`SELECT ${COLUMNS2}
|
|
13987
14999
|
FROM lattice_channel_reply_jobs
|
|
13988
15000
|
WHERE tenant_id = $1 AND run_id = $2`,
|
|
13989
15001
|
[tenantId, runId]
|
|
13990
15002
|
);
|
|
13991
|
-
return result.rowCount === 0 ? null :
|
|
15003
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
13992
15004
|
}
|
|
13993
15005
|
/** Lists claimable pending jobs that are due at `now`, oldest first. */
|
|
13994
15006
|
async listPending(now, limit) {
|
|
13995
15007
|
const result = await this.pool.query(
|
|
13996
|
-
`SELECT ${
|
|
15008
|
+
`SELECT ${COLUMNS2}
|
|
13997
15009
|
FROM lattice_channel_reply_jobs
|
|
13998
15010
|
WHERE status = 'pending'
|
|
13999
15011
|
AND available_at <= $1
|
|
14000
15012
|
AND (lease_owner IS NULL OR lease_expires_at IS NULL OR lease_expires_at <= $1)
|
|
14001
15013
|
ORDER BY available_at ASC, created_at ASC, run_id ASC
|
|
14002
15014
|
LIMIT $2`,
|
|
14003
|
-
[now, (0,
|
|
15015
|
+
[now, (0, import_protocols13.normalizeChannelReplyJobListLimit)(limit)]
|
|
14004
15016
|
);
|
|
14005
|
-
return result.rows.map(
|
|
15017
|
+
return result.rows.map(mapRow16);
|
|
14006
15018
|
}
|
|
14007
15019
|
/**
|
|
14008
15020
|
* Atomically leases a claimable job.
|
|
@@ -14023,10 +15035,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14023
15035
|
AND j.status = 'pending'
|
|
14024
15036
|
AND j.available_at <= clock.now_ms
|
|
14025
15037
|
AND (j.lease_owner IS NULL OR j.lease_expires_at IS NULL OR j.lease_expires_at <= clock.now_ms)
|
|
14026
|
-
RETURNING ${
|
|
15038
|
+
RETURNING ${COLUMNS2}`,
|
|
14027
15039
|
[tenantId, runId, owner, leaseMs]
|
|
14028
15040
|
);
|
|
14029
|
-
return result.rowCount === 0 ? null :
|
|
15041
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
14030
15042
|
}
|
|
14031
15043
|
/**
|
|
14032
15044
|
* Marks a job delivered when `owner` still holds the lease.
|
|
@@ -14049,10 +15061,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14049
15061
|
AND j.run_id = $2
|
|
14050
15062
|
AND j.lease_owner = $3
|
|
14051
15063
|
AND j.status = 'pending'
|
|
14052
|
-
RETURNING ${
|
|
15064
|
+
RETURNING ${COLUMNS2}`,
|
|
14053
15065
|
[tenantId, runId, owner, outputMessageId]
|
|
14054
15066
|
);
|
|
14055
|
-
return result.rowCount === 0 ? null :
|
|
15067
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
14056
15068
|
}
|
|
14057
15069
|
/**
|
|
14058
15070
|
* Returns a job to pending with an incremented attempt count.
|
|
@@ -14076,10 +15088,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14076
15088
|
AND j.run_id = $2
|
|
14077
15089
|
AND j.lease_owner = $3
|
|
14078
15090
|
AND j.status = 'pending'
|
|
14079
|
-
RETURNING ${
|
|
15091
|
+
RETURNING ${COLUMNS2}`,
|
|
14080
15092
|
[tenantId, runId, owner, error, availableAt]
|
|
14081
15093
|
);
|
|
14082
|
-
return result.rowCount === 0 ? null :
|
|
15094
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
14083
15095
|
}
|
|
14084
15096
|
/**
|
|
14085
15097
|
* Marks a job terminally failed when `owner` still holds the lease.
|
|
@@ -14101,10 +15113,10 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14101
15113
|
AND j.run_id = $2
|
|
14102
15114
|
AND j.lease_owner = $3
|
|
14103
15115
|
AND j.status = 'pending'
|
|
14104
|
-
RETURNING ${
|
|
15116
|
+
RETURNING ${COLUMNS2}`,
|
|
14105
15117
|
[tenantId, runId, owner, error]
|
|
14106
15118
|
);
|
|
14107
|
-
return result.rowCount === 0 ? null :
|
|
15119
|
+
return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
|
|
14108
15120
|
}
|
|
14109
15121
|
/** Deletes one job. Returns whether a row was removed. */
|
|
14110
15122
|
async delete(tenantId, runId) {
|
|
@@ -14126,7 +15138,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14126
15138
|
ORDER BY updated_at ASC, run_id ASC
|
|
14127
15139
|
LIMIT $2
|
|
14128
15140
|
)`,
|
|
14129
|
-
[before, (0,
|
|
15141
|
+
[before, (0, import_protocols13.normalizeChannelReplyJobListLimit)(limit)]
|
|
14130
15142
|
);
|
|
14131
15143
|
return result.rowCount ?? 0;
|
|
14132
15144
|
}
|
|
@@ -14164,7 +15176,7 @@ async function rollbackQuietly(client) {
|
|
|
14164
15176
|
} catch {
|
|
14165
15177
|
}
|
|
14166
15178
|
}
|
|
14167
|
-
function
|
|
15179
|
+
function isUniqueViolation5(error) {
|
|
14168
15180
|
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
14169
15181
|
}
|
|
14170
15182
|
function isRecord5(value) {
|
|
@@ -14292,7 +15304,7 @@ var PostgreSQLCheckpointStorage = class {
|
|
|
14292
15304
|
await client.query("COMMIT");
|
|
14293
15305
|
} catch (error) {
|
|
14294
15306
|
await rollbackQuietly(client);
|
|
14295
|
-
if (
|
|
15307
|
+
if (isUniqueViolation5(error)) {
|
|
14296
15308
|
throw new CheckpointTargetNotEmptyError(targetThreadId);
|
|
14297
15309
|
}
|
|
14298
15310
|
throw error;
|
|
@@ -14580,8 +15592,8 @@ var PostgreSQLCheckpointStorage = class {
|
|
|
14580
15592
|
};
|
|
14581
15593
|
|
|
14582
15594
|
// src/stores/PostgreSQLStore.ts
|
|
14583
|
-
var
|
|
14584
|
-
function
|
|
15595
|
+
var import_protocols14 = require("@axiom-lattice/protocols");
|
|
15596
|
+
function mapRow17(row) {
|
|
14585
15597
|
return {
|
|
14586
15598
|
namespace: row.namespace,
|
|
14587
15599
|
key: row.key,
|
|
@@ -14599,9 +15611,9 @@ var PostgreSQLStore = class {
|
|
|
14599
15611
|
this.pool = pool;
|
|
14600
15612
|
}
|
|
14601
15613
|
async put(tenantId, input) {
|
|
14602
|
-
(0,
|
|
15614
|
+
(0, import_protocols14.assertStoreIdentity)(input.namespace, input.key);
|
|
14603
15615
|
const now = /* @__PURE__ */ new Date();
|
|
14604
|
-
const expiresAt = (0,
|
|
15616
|
+
const expiresAt = (0, import_protocols14.storeExpiresAt)(input.ttlMinutes, now) ?? null;
|
|
14605
15617
|
const result = await this.pool.query(
|
|
14606
15618
|
`INSERT INTO lattice_store_items
|
|
14607
15619
|
(tenant_id, namespace, key, value, created_at, updated_at, expires_at)
|
|
@@ -14613,10 +15625,10 @@ var PostgreSQLStore = class {
|
|
|
14613
15625
|
RETURNING tenant_id, namespace, key, value, created_at, updated_at, expires_at`,
|
|
14614
15626
|
[tenantId, input.namespace, input.key, JSON.stringify(input.value), now, expiresAt]
|
|
14615
15627
|
);
|
|
14616
|
-
return
|
|
15628
|
+
return mapRow17(result.rows[0]);
|
|
14617
15629
|
}
|
|
14618
15630
|
async get(tenantId, namespace, key) {
|
|
14619
|
-
(0,
|
|
15631
|
+
(0, import_protocols14.assertStoreIdentity)(namespace, key);
|
|
14620
15632
|
const result = await this.pool.query(
|
|
14621
15633
|
`SELECT tenant_id, namespace, key, value, created_at, updated_at, expires_at
|
|
14622
15634
|
FROM lattice_store_items
|
|
@@ -14624,10 +15636,10 @@ var PostgreSQLStore = class {
|
|
|
14624
15636
|
AND (expires_at IS NULL OR expires_at > $4)`,
|
|
14625
15637
|
[tenantId, namespace, key, /* @__PURE__ */ new Date()]
|
|
14626
15638
|
);
|
|
14627
|
-
return result.rows[0] ?
|
|
15639
|
+
return result.rows[0] ? mapRow17(result.rows[0]) : null;
|
|
14628
15640
|
}
|
|
14629
15641
|
async delete(tenantId, namespace, key) {
|
|
14630
|
-
(0,
|
|
15642
|
+
(0, import_protocols14.assertStoreIdentity)(namespace, key);
|
|
14631
15643
|
const result = await this.pool.query(
|
|
14632
15644
|
`DELETE FROM lattice_store_items
|
|
14633
15645
|
WHERE tenant_id = $1 AND namespace = $2 AND key = $3`,
|
|
@@ -14642,7 +15654,7 @@ var PostgreSQLStore = class {
|
|
|
14642
15654
|
];
|
|
14643
15655
|
const params = [tenantId, /* @__PURE__ */ new Date()];
|
|
14644
15656
|
if (input.namespacePrefix) {
|
|
14645
|
-
(0,
|
|
15657
|
+
(0, import_protocols14.assertStoreNamespacePrefix)(input.namespacePrefix);
|
|
14646
15658
|
}
|
|
14647
15659
|
if (input.namespacePrefix && input.namespacePrefix.length > 0) {
|
|
14648
15660
|
params.push(input.namespacePrefix);
|
|
@@ -14656,9 +15668,9 @@ var PostgreSQLStore = class {
|
|
|
14656
15668
|
params.push(input.query.replace(/[\\%_]/g, "\\$&"));
|
|
14657
15669
|
conditions.push(`value::text ILIKE '%' || $${params.length} || '%' ESCAPE '\\'`);
|
|
14658
15670
|
}
|
|
14659
|
-
params.push((0,
|
|
15671
|
+
params.push((0, import_protocols14.storePaginationValue)(input.limit, 10));
|
|
14660
15672
|
const limitParam = params.length;
|
|
14661
|
-
params.push((0,
|
|
15673
|
+
params.push((0, import_protocols14.storePaginationValue)(input.offset, 0));
|
|
14662
15674
|
const offsetParam = params.length;
|
|
14663
15675
|
const result = await this.pool.query(
|
|
14664
15676
|
`SELECT tenant_id, namespace, key, value, created_at, updated_at, expires_at
|
|
@@ -14668,10 +15680,10 @@ var PostgreSQLStore = class {
|
|
|
14668
15680
|
LIMIT $${limitParam} OFFSET $${offsetParam}`,
|
|
14669
15681
|
params
|
|
14670
15682
|
);
|
|
14671
|
-
return result.rows.map(
|
|
15683
|
+
return result.rows.map(mapRow17);
|
|
14672
15684
|
}
|
|
14673
15685
|
async listNamespaces(tenantId, prefix = [], limit = 100) {
|
|
14674
|
-
(0,
|
|
15686
|
+
(0, import_protocols14.assertStoreNamespacePrefix)(prefix);
|
|
14675
15687
|
const conditions = [
|
|
14676
15688
|
"tenant_id = $1",
|
|
14677
15689
|
"(expires_at IS NULL OR expires_at > $2)"
|
|
@@ -14681,7 +15693,7 @@ var PostgreSQLStore = class {
|
|
|
14681
15693
|
params.push(prefix);
|
|
14682
15694
|
conditions.push(`namespace[1:array_length($${params.length}::text[], 1)] = $${params.length}`);
|
|
14683
15695
|
}
|
|
14684
|
-
params.push((0,
|
|
15696
|
+
params.push((0, import_protocols14.storePaginationValue)(limit, 100));
|
|
14685
15697
|
const result = await this.pool.query(
|
|
14686
15698
|
`SELECT DISTINCT namespace
|
|
14687
15699
|
FROM lattice_store_items
|
|
@@ -14693,7 +15705,7 @@ var PostgreSQLStore = class {
|
|
|
14693
15705
|
return result.rows.map((row) => row.namespace);
|
|
14694
15706
|
}
|
|
14695
15707
|
async deleteExpired(now, limit) {
|
|
14696
|
-
const bounded = (0,
|
|
15708
|
+
const bounded = (0, import_protocols14.storePaginationValue)(limit, 0);
|
|
14697
15709
|
const result = await this.pool.query(
|
|
14698
15710
|
`WITH expired AS (
|
|
14699
15711
|
SELECT ctid FROM lattice_store_items
|
|
@@ -14839,6 +15851,16 @@ async function createPgStoreConfig(connectionString) {
|
|
|
14839
15851
|
mm.register(agentRunProjectTaskRunningIndexMigration);
|
|
14840
15852
|
mm.register(addModelProviderKindDefault);
|
|
14841
15853
|
mm.register(addShareKind);
|
|
15854
|
+
mm.register(createUserExternalIdentityTable);
|
|
15855
|
+
mm.register(createUserEmailLowerUniqueIndex);
|
|
15856
|
+
mm.register(createOidcProviderConfigsTable);
|
|
15857
|
+
mm.register(createPlatformMembersTable);
|
|
15858
|
+
mm.register(createPlatformInvitationsTable);
|
|
15859
|
+
mm.register(createOidcIdentityClaimsTable);
|
|
15860
|
+
mm.register(createInviteLinksTable);
|
|
15861
|
+
mm.register(addOidcIdentityClaimInviteFields);
|
|
15862
|
+
mm.register(createPlatformSettingsTable);
|
|
15863
|
+
mm.register(addOidcProviderAllowedHosts);
|
|
14842
15864
|
await mm.migrate();
|
|
14843
15865
|
const checkpoint = import_langgraph_checkpoint_postgres.PostgresSaver.fromConnString(connectionString);
|
|
14844
15866
|
try {
|
|
@@ -14855,6 +15877,13 @@ async function createPgStoreConfig(connectionString) {
|
|
|
14855
15877
|
user: new PostgreSQLUserStore(opts),
|
|
14856
15878
|
tenant: new PostgreSQLTenantStore(opts),
|
|
14857
15879
|
userTenantLink: new PostgreSQLUserTenantLinkStore(opts),
|
|
15880
|
+
userExternalIdentity: new PostgreSQLUserExternalIdentityStore(opts),
|
|
15881
|
+
oidcProviderConfig: new PostgreSQLOidcProviderConfigStore(opts),
|
|
15882
|
+
platformMember: new PostgreSQLPlatformMemberStore(opts),
|
|
15883
|
+
platformInvitation: new PostgreSQLPlatformInvitationStore(opts),
|
|
15884
|
+
oidcIdentityClaim: new PostgreSQLOidcIdentityClaimStore(opts),
|
|
15885
|
+
inviteLink: new PostgreSQLInviteLinkStore(opts),
|
|
15886
|
+
platformSettings: new PostgreSQLPlatformSettingsStore(opts),
|
|
14858
15887
|
channelBinding: new ChannelBindingStore(opts),
|
|
14859
15888
|
channelInstallation: new PostgreSQLChannelInstallationStore(opts),
|
|
14860
15889
|
thread: new PostgreSQLThreadStore(opts),
|
|
@@ -15452,6 +16481,7 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15452
16481
|
InvalidFileShareAddressError,
|
|
15453
16482
|
InvalidMcpAppShareAddressError,
|
|
15454
16483
|
InvalidMcpAppShareVisibilityError,
|
|
16484
|
+
InvalidUserExternalIdentityRowError,
|
|
15455
16485
|
MenuStore,
|
|
15456
16486
|
MigrationManager,
|
|
15457
16487
|
PGVectorStoreProvider,
|
|
@@ -15468,10 +16498,16 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15468
16498
|
PostgreSQLConnectionStore,
|
|
15469
16499
|
PostgreSQLDatabaseConfigStore,
|
|
15470
16500
|
PostgreSQLEvalStore,
|
|
16501
|
+
PostgreSQLInviteLinkStore,
|
|
15471
16502
|
PostgreSQLMcpServerConfigStore,
|
|
15472
16503
|
PostgreSQLMetricsServerConfigStore,
|
|
15473
16504
|
PostgreSQLModelProviderStore,
|
|
16505
|
+
PostgreSQLOidcIdentityClaimStore,
|
|
16506
|
+
PostgreSQLOidcProviderConfigStore,
|
|
15474
16507
|
PostgreSQLOpenAuditStore,
|
|
16508
|
+
PostgreSQLPlatformInvitationStore,
|
|
16509
|
+
PostgreSQLPlatformMemberStore,
|
|
16510
|
+
PostgreSQLPlatformSettingsStore,
|
|
15475
16511
|
PostgreSQLPluginDeploymentStore,
|
|
15476
16512
|
PostgreSQLProjectBotMembershipStore,
|
|
15477
16513
|
PostgreSQLProjectMembershipStore,
|
|
@@ -15485,6 +16521,7 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15485
16521
|
PostgreSQLTaskWorkItemStore,
|
|
15486
16522
|
PostgreSQLTenantStore,
|
|
15487
16523
|
PostgreSQLThreadStore,
|
|
16524
|
+
PostgreSQLUserExternalIdentityStore,
|
|
15488
16525
|
PostgreSQLUserStore,
|
|
15489
16526
|
PostgreSQLUserTenantLinkStore,
|
|
15490
16527
|
PostgreSQLWorkflowTrackingStore,
|
|
@@ -15502,6 +16539,7 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15502
16539
|
addInterruptColumnsToEval,
|
|
15503
16540
|
addInterruptPolicyToEvalCases,
|
|
15504
16541
|
addMcpAppContentType,
|
|
16542
|
+
addOidcIdentityClaimInviteFields,
|
|
15505
16543
|
addProjectConfigColumn,
|
|
15506
16544
|
addScheduleTenantId,
|
|
15507
16545
|
addShareKind,
|
|
@@ -15531,11 +16569,17 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15531
16569
|
createEvalRunResultsTable,
|
|
15532
16570
|
createEvalRunsTable,
|
|
15533
16571
|
createEvalSuitesTable,
|
|
16572
|
+
createInviteLinksTable,
|
|
15534
16573
|
createMcpServerConfigsTable,
|
|
15535
16574
|
createMenuItemsTable,
|
|
15536
16575
|
createMetricsConfigsTable,
|
|
16576
|
+
createOidcIdentityClaimsTable,
|
|
16577
|
+
createOidcProviderConfigsTable,
|
|
15537
16578
|
createPGVectorStoreProvider,
|
|
15538
16579
|
createPgStoreConfig,
|
|
16580
|
+
createPlatformInvitationsTable,
|
|
16581
|
+
createPlatformMembersTable,
|
|
16582
|
+
createPlatformSettingsTable,
|
|
15539
16583
|
createPluginDeployments,
|
|
15540
16584
|
createProjectRoomReadStates,
|
|
15541
16585
|
createProjectRoomTables,
|
|
@@ -15547,6 +16591,8 @@ function mapRowToChannelIdentityMapping(row) {
|
|
|
15547
16591
|
createTenantsTable,
|
|
15548
16592
|
createThreadMessageQueueTable,
|
|
15549
16593
|
createThreadsTable,
|
|
16594
|
+
createUserEmailLowerUniqueIndex,
|
|
16595
|
+
createUserExternalIdentityTable,
|
|
15550
16596
|
createUsersTable,
|
|
15551
16597
|
createWorkflowTrackingTables,
|
|
15552
16598
|
createWorkspacesTable,
|