@axiom-lattice/pg-stores 1.0.80 → 1.0.82
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 +18 -0
- package/dist/index.d.mts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +253 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +250 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/createPgStoreConfig.ts +2 -0
- package/src/index.ts +10 -0
- package/src/migrations/menu_items_add_file_type.ts +29 -0
- package/src/migrations/menu_items_migration.ts +1 -1
- package/src/migrations/shared_resources_migration.ts +43 -0
- package/src/stores/MenuStore.ts +2 -0
- package/src/stores/PostgresSharedResourceStore.ts +220 -0
- package/src/stores/SharedResourceStore.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/pg-stores",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.82",
|
|
4
4
|
"description": "PG stores implementation for Axiom Lattice framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"@langchain/langgraph-checkpoint-postgres": "1.0.0",
|
|
24
24
|
"pg": "^8.16.3",
|
|
25
25
|
"uuid": "^9.0.1",
|
|
26
|
-
"@axiom-lattice/core": "2.1.
|
|
27
|
-
"@axiom-lattice/protocols": "2.1.
|
|
26
|
+
"@axiom-lattice/core": "2.1.91",
|
|
27
|
+
"@axiom-lattice/protocols": "2.1.46"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^20.11.24",
|
|
@@ -17,6 +17,7 @@ import { PostgreSQLA2AApiKeyStore } from "./stores/PostgreSQLA2AApiKeyStore";
|
|
|
17
17
|
import { PostgreSQLScheduleStorage } from "./stores/PostgreSQLScheduleStorage";
|
|
18
18
|
import { PostgreSQLTaskStore } from "./stores/PostgreSQLTaskStore";
|
|
19
19
|
import { MenuStore } from "./stores/MenuStore";
|
|
20
|
+
import { PostgresSharedResourceStore } from "./stores/PostgresSharedResourceStore";
|
|
20
21
|
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
|
|
21
22
|
|
|
22
23
|
export function createPgStoreConfig(connectionString: string) {
|
|
@@ -47,6 +48,7 @@ export function createPgStoreConfig(connectionString: string) {
|
|
|
47
48
|
a2aApiKey: new PostgreSQLA2AApiKeyStore(opts),
|
|
48
49
|
schedule: new PostgreSQLScheduleStorage(opts),
|
|
49
50
|
menu: new MenuStore(opts),
|
|
51
|
+
sharedResource: new PostgresSharedResourceStore(opts),
|
|
50
52
|
checkpoint,
|
|
51
53
|
};
|
|
52
54
|
}
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ export * from "./migrations/channel_identity_mapping_migration";
|
|
|
47
47
|
export * from "./migrations/channel_installation_migrations";
|
|
48
48
|
export * from "./migrations/channel_installations_alter_migration";
|
|
49
49
|
export * from "./migrations/channel_bindings_migration";
|
|
50
|
+
export * from "./migrations/shared_resources_migration";
|
|
50
51
|
export * from "./migrations/a2a_api_key_migration";
|
|
51
52
|
export * from "./migrations/workflow_tracking_migrations";
|
|
52
53
|
export * from "./stores/ChannelBindingStore";
|
|
@@ -58,7 +59,10 @@ export * from "./stores/PostgreSQLWorkflowTrackingStore";
|
|
|
58
59
|
export * from "./stores/PostgreSQLEvalStore";
|
|
59
60
|
export * from "./migrations/eval_migrations";
|
|
60
61
|
export * from "./migrations/menu_items_migration";
|
|
62
|
+
export * from "./migrations/menu_items_add_file_type";
|
|
61
63
|
export * from "./stores/MenuStore";
|
|
64
|
+
export * from "./stores/SharedResourceStore";
|
|
65
|
+
export * from "./stores/PostgresSharedResourceStore";
|
|
62
66
|
|
|
63
67
|
// Re-export for convenience
|
|
64
68
|
export { PostgreSQLThreadStore } from "./stores/PostgreSQLThreadStore";
|
|
@@ -80,6 +84,7 @@ export { PostgreSQLChannelInstallationStore } from "./stores/PostgreSQLChannelIn
|
|
|
80
84
|
export { PostgreSQLA2AApiKeyStore } from "./stores/PostgreSQLA2AApiKeyStore";
|
|
81
85
|
export { PostgreSQLWorkflowTrackingStore } from "./stores/PostgreSQLWorkflowTrackingStore";
|
|
82
86
|
export { PostgreSQLEvalStore } from "./stores/PostgreSQLEvalStore";
|
|
87
|
+
export { PostgresSharedResourceStore } from "./stores/PostgresSharedResourceStore";
|
|
83
88
|
|
|
84
89
|
// Re-export types from protocols
|
|
85
90
|
export type {
|
|
@@ -163,4 +168,9 @@ export type {
|
|
|
163
168
|
CreateEvalRunRequest,
|
|
164
169
|
EvalRunResult,
|
|
165
170
|
EvalProjectReport,
|
|
171
|
+
ShareRecord,
|
|
172
|
+
ResourceAddress,
|
|
173
|
+
ShareVisibility,
|
|
174
|
+
CreateShareRequest,
|
|
175
|
+
ShareResult,
|
|
166
176
|
} from "@axiom-lattice/protocols";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PoolClient } from "pg";
|
|
2
|
+
import { Migration } from "./migration";
|
|
3
|
+
|
|
4
|
+
export const addFileContentType: Migration = {
|
|
5
|
+
version: 136,
|
|
6
|
+
name: "add_file_content_type_to_menu_items",
|
|
7
|
+
up: async (client: PoolClient) => {
|
|
8
|
+
await client.query(`
|
|
9
|
+
ALTER TABLE lattice_menu_items
|
|
10
|
+
DROP CONSTRAINT IF EXISTS lattice_menu_items_content_type_check
|
|
11
|
+
`);
|
|
12
|
+
await client.query(`
|
|
13
|
+
ALTER TABLE lattice_menu_items
|
|
14
|
+
ADD CONSTRAINT lattice_menu_items_content_type_check
|
|
15
|
+
CHECK (content_type IN ('agent', 'html', 'custom', 'file'))
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
down: async (client: PoolClient) => {
|
|
19
|
+
await client.query(`
|
|
20
|
+
ALTER TABLE lattice_menu_items
|
|
21
|
+
DROP CONSTRAINT IF EXISTS lattice_menu_items_content_type_check
|
|
22
|
+
`);
|
|
23
|
+
await client.query(`
|
|
24
|
+
ALTER TABLE lattice_menu_items
|
|
25
|
+
ADD CONSTRAINT lattice_menu_items_content_type_check
|
|
26
|
+
CHECK (content_type IN ('agent', 'html', 'custom'))
|
|
27
|
+
`);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -14,7 +14,7 @@ export const createMenuItemsTable: Migration = {
|
|
|
14
14
|
name VARCHAR(255) NOT NULL,
|
|
15
15
|
icon VARCHAR(50),
|
|
16
16
|
sort_order INT NOT NULL DEFAULT 0,
|
|
17
|
-
|
|
17
|
+
content_type VARCHAR(20) NOT NULL CHECK (content_type IN ('agent', 'html', 'custom', 'file')),
|
|
18
18
|
content_config JSONB NOT NULL DEFAULT '{}',
|
|
19
19
|
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
20
20
|
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { PoolClient } from "pg";
|
|
2
|
+
import { Migration } from "./migration";
|
|
3
|
+
|
|
4
|
+
export const createSharedResourcesTable: Migration = {
|
|
5
|
+
version: 135,
|
|
6
|
+
name: "create_shared_resources_table",
|
|
7
|
+
up: async (client: PoolClient) => {
|
|
8
|
+
await client.query(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS lattice_shared_resources (
|
|
10
|
+
token VARCHAR(64) PRIMARY KEY,
|
|
11
|
+
address JSONB NOT NULL,
|
|
12
|
+
title VARCHAR(256),
|
|
13
|
+
created_by VARCHAR(64) NOT NULL,
|
|
14
|
+
tenant_id VARCHAR(64) NOT NULL,
|
|
15
|
+
workspace_id VARCHAR(64) NOT NULL,
|
|
16
|
+
project_id VARCHAR(64) NOT NULL,
|
|
17
|
+
assistant_id VARCHAR(64),
|
|
18
|
+
vm_isolation VARCHAR(16) NOT NULL DEFAULT 'project',
|
|
19
|
+
visibility VARCHAR(16) NOT NULL DEFAULT 'public',
|
|
20
|
+
password_hash VARCHAR(128),
|
|
21
|
+
expires_at TIMESTAMPTZ,
|
|
22
|
+
max_access INT,
|
|
23
|
+
access_count INT DEFAULT 0,
|
|
24
|
+
revoked BOOLEAN DEFAULT FALSE,
|
|
25
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
26
|
+
version INT DEFAULT 1
|
|
27
|
+
)
|
|
28
|
+
`);
|
|
29
|
+
|
|
30
|
+
await client.query(`
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_shared_resources_tenant
|
|
32
|
+
ON lattice_shared_resources(tenant_id)
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
await client.query(`
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_shared_resources_user
|
|
37
|
+
ON lattice_shared_resources(created_by)
|
|
38
|
+
`);
|
|
39
|
+
},
|
|
40
|
+
down: async (client: PoolClient) => {
|
|
41
|
+
await client.query("DROP TABLE IF EXISTS lattice_shared_resources");
|
|
42
|
+
},
|
|
43
|
+
};
|
package/src/stores/MenuStore.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "@axiom-lattice/protocols";
|
|
10
10
|
import { MigrationManager } from "../migrations/migration";
|
|
11
11
|
import { createMenuItemsTable } from "../migrations/menu_items_migration";
|
|
12
|
+
import { addFileContentType } from "../migrations/menu_items_add_file_type";
|
|
12
13
|
|
|
13
14
|
export interface MenuStoreOptions {
|
|
14
15
|
poolConfig: string | PoolConfig;
|
|
@@ -44,6 +45,7 @@ export class MenuStore implements MenuRegistry {
|
|
|
44
45
|
|
|
45
46
|
this.migrationManager = new MigrationManager(this.pool);
|
|
46
47
|
this.migrationManager.register(createMenuItemsTable);
|
|
48
|
+
this.migrationManager.register(addFileContentType);
|
|
47
49
|
|
|
48
50
|
if (options.autoMigrate !== false) {
|
|
49
51
|
this.initialize().catch((error) => {
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Pool } from "pg";
|
|
2
|
+
import type { PoolConfig } from "pg";
|
|
3
|
+
import type { ShareRecord, ResourceAddress } from "@axiom-lattice/protocols";
|
|
4
|
+
import type { SharedResourceStore } from "./SharedResourceStore";
|
|
5
|
+
import { MigrationManager } from "../migrations/migration";
|
|
6
|
+
import { createSharedResourcesTable } from "../migrations/shared_resources_migration";
|
|
7
|
+
|
|
8
|
+
export interface PostgresSharedResourceStoreOptions {
|
|
9
|
+
poolConfig: string | PoolConfig;
|
|
10
|
+
autoMigrate?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ShareRow = {
|
|
14
|
+
token: string;
|
|
15
|
+
address: ResourceAddress;
|
|
16
|
+
title: string | null;
|
|
17
|
+
created_by: string;
|
|
18
|
+
tenant_id: string;
|
|
19
|
+
workspace_id: string;
|
|
20
|
+
project_id: string;
|
|
21
|
+
assistant_id: string | null;
|
|
22
|
+
vm_isolation: string;
|
|
23
|
+
visibility: string;
|
|
24
|
+
password_hash: string | null;
|
|
25
|
+
expires_at: string | null;
|
|
26
|
+
max_access: number | null;
|
|
27
|
+
access_count: number;
|
|
28
|
+
revoked: boolean;
|
|
29
|
+
created_at: string;
|
|
30
|
+
version: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export class PostgresSharedResourceStore implements SharedResourceStore {
|
|
34
|
+
private pool: Pool;
|
|
35
|
+
private migrationManager: MigrationManager;
|
|
36
|
+
private initialized = false;
|
|
37
|
+
private initPromise: Promise<void> | null = null;
|
|
38
|
+
|
|
39
|
+
constructor(options: PostgresSharedResourceStoreOptions) {
|
|
40
|
+
this.pool =
|
|
41
|
+
typeof options.poolConfig === "string"
|
|
42
|
+
? new Pool({ connectionString: options.poolConfig })
|
|
43
|
+
: new Pool(options.poolConfig);
|
|
44
|
+
|
|
45
|
+
this.migrationManager = new MigrationManager(this.pool);
|
|
46
|
+
this.migrationManager.register(createSharedResourcesTable);
|
|
47
|
+
|
|
48
|
+
if (options.autoMigrate !== false) {
|
|
49
|
+
this.initialize().catch((error) => {
|
|
50
|
+
console.error(
|
|
51
|
+
"Failed to initialize PostgresSharedResourceStore:",
|
|
52
|
+
error,
|
|
53
|
+
);
|
|
54
|
+
throw error;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async initialize(): Promise<void> {
|
|
60
|
+
if (this.initialized) return;
|
|
61
|
+
if (this.initPromise) return this.initPromise;
|
|
62
|
+
this.initPromise = (async () => {
|
|
63
|
+
try {
|
|
64
|
+
await this.migrationManager.migrate();
|
|
65
|
+
this.initialized = true;
|
|
66
|
+
} finally {
|
|
67
|
+
this.initPromise = null;
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
return this.initPromise;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async findByToken(token: string): Promise<ShareRecord | null> {
|
|
74
|
+
await this.ensureInitialized();
|
|
75
|
+
const { rows } = await this.pool.query<ShareRow>(
|
|
76
|
+
"SELECT * FROM lattice_shared_resources WHERE token = $1",
|
|
77
|
+
[token],
|
|
78
|
+
);
|
|
79
|
+
if (rows.length === 0) return null;
|
|
80
|
+
return this._mapRow(rows[0]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async create(
|
|
84
|
+
record: Omit<ShareRecord, "accessCount" | "createdAt" | "version">,
|
|
85
|
+
): Promise<ShareRecord> {
|
|
86
|
+
await this.ensureInitialized();
|
|
87
|
+
const { rows } = await this.pool.query<ShareRow>(
|
|
88
|
+
`INSERT INTO lattice_shared_resources
|
|
89
|
+
(token, address, title, created_by, tenant_id, workspace_id, project_id,
|
|
90
|
+
assistant_id, vm_isolation, visibility, password_hash,
|
|
91
|
+
expires_at, max_access, revoked)
|
|
92
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
|
|
93
|
+
RETURNING *`,
|
|
94
|
+
[
|
|
95
|
+
record.token,
|
|
96
|
+
JSON.stringify(record.address),
|
|
97
|
+
record.title,
|
|
98
|
+
record.createdBy,
|
|
99
|
+
record.tenantId,
|
|
100
|
+
record.workspaceId,
|
|
101
|
+
record.projectId,
|
|
102
|
+
record.assistantId,
|
|
103
|
+
record.vmIsolation,
|
|
104
|
+
record.visibility,
|
|
105
|
+
record.passwordHash,
|
|
106
|
+
record.expiresAt ? record.expiresAt.toISOString() : null,
|
|
107
|
+
record.maxAccess,
|
|
108
|
+
record.revoked,
|
|
109
|
+
],
|
|
110
|
+
);
|
|
111
|
+
return this._mapRow(rows[0]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async update(
|
|
115
|
+
token: string,
|
|
116
|
+
patch: Record<string, unknown>,
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
await this.ensureInitialized();
|
|
119
|
+
const ALLOWED_COLUMNS = new Set([
|
|
120
|
+
"title", "expiresAt", "maxAccess", "revoked", "visibility", "passwordHash",
|
|
121
|
+
]);
|
|
122
|
+
const COLUMN_MAP: Record<string, string> = {
|
|
123
|
+
"title": "title",
|
|
124
|
+
"expiresAt": "expires_at",
|
|
125
|
+
"maxAccess": "max_access",
|
|
126
|
+
"revoked": "revoked",
|
|
127
|
+
"visibility": "visibility",
|
|
128
|
+
"passwordHash": "password_hash",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const sets: string[] = [];
|
|
132
|
+
const vals: unknown[] = [];
|
|
133
|
+
let i = 1;
|
|
134
|
+
|
|
135
|
+
for (const [col, val] of Object.entries(patch)) {
|
|
136
|
+
const dbCol = COLUMN_MAP[col];
|
|
137
|
+
if (dbCol && val !== undefined) {
|
|
138
|
+
sets.push(`${dbCol} = $${i++}`);
|
|
139
|
+
vals.push(val);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (sets.length === 0) return;
|
|
143
|
+
|
|
144
|
+
vals.push(token);
|
|
145
|
+
await this.pool.query(
|
|
146
|
+
`UPDATE lattice_shared_resources SET ${sets.join(", ")} WHERE token = $${i}`,
|
|
147
|
+
vals,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async listByUser(
|
|
152
|
+
tenantId: string,
|
|
153
|
+
userId: string,
|
|
154
|
+
): Promise<ShareRecord[]> {
|
|
155
|
+
await this.ensureInitialized();
|
|
156
|
+
const { rows } = await this.pool.query<ShareRow>(
|
|
157
|
+
`SELECT * FROM lattice_shared_resources
|
|
158
|
+
WHERE tenant_id = $1 AND created_by = $2
|
|
159
|
+
ORDER BY created_at DESC`,
|
|
160
|
+
[tenantId, userId],
|
|
161
|
+
);
|
|
162
|
+
return rows.map((r) => this._mapRow(r));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async incrementAccess(token: string): Promise<void> {
|
|
166
|
+
await this.ensureInitialized();
|
|
167
|
+
await this.pool.query(
|
|
168
|
+
"UPDATE lattice_shared_resources SET access_count = access_count + 1 WHERE token = $1",
|
|
169
|
+
[token],
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async atomicIncrementAccess(token: string): Promise<boolean> {
|
|
174
|
+
await this.ensureInitialized();
|
|
175
|
+
const { rows } = await this.pool.query(
|
|
176
|
+
`UPDATE lattice_shared_resources SET access_count = access_count + 1
|
|
177
|
+
WHERE token = $1 AND NOT revoked
|
|
178
|
+
AND (expires_at IS NULL OR expires_at > NOW())
|
|
179
|
+
AND (max_access IS NULL OR access_count < max_access)
|
|
180
|
+
RETURNING 1`,
|
|
181
|
+
[token],
|
|
182
|
+
);
|
|
183
|
+
return rows.length > 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private async ensureInitialized(): Promise<void> {
|
|
187
|
+
if (!this.initialized) {
|
|
188
|
+
await this.initialize();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private _mapRow(row: ShareRow): ShareRecord {
|
|
193
|
+
let address: ResourceAddress;
|
|
194
|
+
if (typeof row.address === "string") {
|
|
195
|
+
address = JSON.parse(row.address) as ResourceAddress;
|
|
196
|
+
} else {
|
|
197
|
+
address = row.address as ResourceAddress;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
token: row.token,
|
|
202
|
+
address,
|
|
203
|
+
title: row.title,
|
|
204
|
+
createdBy: row.created_by,
|
|
205
|
+
tenantId: row.tenant_id,
|
|
206
|
+
workspaceId: row.workspace_id,
|
|
207
|
+
projectId: row.project_id,
|
|
208
|
+
assistantId: row.assistant_id,
|
|
209
|
+
vmIsolation: row.vm_isolation,
|
|
210
|
+
visibility: row.visibility as ShareRecord["visibility"],
|
|
211
|
+
passwordHash: row.password_hash,
|
|
212
|
+
expiresAt: row.expires_at ? new Date(row.expires_at) : null,
|
|
213
|
+
maxAccess: row.max_access,
|
|
214
|
+
accessCount: row.access_count ?? 0,
|
|
215
|
+
revoked: row.revoked ?? false,
|
|
216
|
+
createdAt: new Date(row.created_at),
|
|
217
|
+
version: row.version ?? 1,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { SharedResourceStore } from "@axiom-lattice/protocols";
|