@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/pg-stores",
3
- "version": "1.0.80",
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.89",
27
- "@axiom-lattice/protocols": "2.1.44"
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
- content_type VARCHAR(20) NOT NULL CHECK (content_type IN ('agent', 'html', 'custom')),
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
+ };
@@ -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";