@bernierllc/database-adapter-postgresql 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.PostgreSQLAdapter = void 0;
11
+ const pg_1 = require("pg");
12
+ const supabase_js_1 = require("@supabase/supabase-js");
13
+ const database_adapter_core_1 = require("@bernierllc/database-adapter-core");
14
+ const retry_policy_1 = require("@bernierllc/retry-policy");
15
+ const logger_1 = require("@bernierllc/logger");
16
+ const PostgreSQLTransaction_1 = require("./PostgreSQLTransaction");
17
+ /**
18
+ * PostgreSQL database adapter with Supabase support
19
+ * Implements connection pooling, RLS, and real-time features
20
+ */
21
+ class PostgreSQLAdapter extends database_adapter_core_1.BaseDatabaseAdapter {
22
+ constructor(config) {
23
+ super(config);
24
+ this.pool = null;
25
+ this.supabase = null;
26
+ this.rlsManager = new database_adapter_core_1.RLSContextManager();
27
+ this.logger = (0, logger_1.createLogger)({ context: { component: 'PostgreSQLAdapter' } });
28
+ // Initialize Supabase client if configured
29
+ if (config.supabaseUrl && config.supabaseAnonKey) {
30
+ this.supabase = (0, supabase_js_1.createClient)(config.supabaseUrl, config.supabaseAnonKey, {
31
+ auth: {
32
+ autoRefreshToken: true,
33
+ persistSession: true,
34
+ detectSessionInUrl: false
35
+ }
36
+ });
37
+ }
38
+ }
39
+ /**
40
+ * Connect to PostgreSQL with connection pooling
41
+ */
42
+ async connect() {
43
+ if (this.connected)
44
+ return;
45
+ this.pool = new pg_1.Pool({
46
+ host: this.config.host,
47
+ port: this.config.port || 5432,
48
+ database: this.config.database,
49
+ user: this.config.username,
50
+ password: this.config.password,
51
+ ssl: this.config.ssl,
52
+ min: this.config.pooling?.min || 2,
53
+ max: this.config.pooling?.max || 10,
54
+ idleTimeoutMillis: this.config.pooling?.idleTimeoutMs || 30000,
55
+ connectionTimeoutMillis: this.config.connectionTimeoutMs || 5000
56
+ });
57
+ // Test connection with retry
58
+ let attempt = 0;
59
+ const maxRetries = 3;
60
+ while (attempt < maxRetries) {
61
+ try {
62
+ const client = await this.pool.connect();
63
+ await client.query('SELECT NOW()');
64
+ client.release();
65
+ this.connected = true;
66
+ this.logger.info('PostgreSQL connection established', {
67
+ database: this.config.database,
68
+ host: this.config.host,
69
+ poolMin: this.config.pooling?.min || 2,
70
+ poolMax: this.config.pooling?.max || 10
71
+ });
72
+ return;
73
+ }
74
+ catch (error) {
75
+ attempt++;
76
+ if ((0, retry_policy_1.shouldRetry)(attempt, error, { maxRetries })) {
77
+ const delay = (0, retry_policy_1.calculateRetryDelay)(attempt, {
78
+ initialDelayMs: 1000,
79
+ maxDelayMs: 5000,
80
+ jitter: true
81
+ });
82
+ this.logger.warn('PostgreSQL connection failed, retrying', {
83
+ attempt,
84
+ maxRetries,
85
+ delayMs: delay,
86
+ error: this.sanitizeError(error)
87
+ });
88
+ await new Promise(resolve => setTimeout(resolve, delay));
89
+ }
90
+ else {
91
+ throw this.sanitizeError(error);
92
+ }
93
+ }
94
+ }
95
+ throw new Error('Failed to connect to PostgreSQL after retries');
96
+ }
97
+ /**
98
+ * Disconnect and close pool
99
+ */
100
+ async disconnect() {
101
+ if (!this.pool)
102
+ return;
103
+ await this.pool.end();
104
+ this.pool = null;
105
+ this.connected = false;
106
+ this.logger.info('PostgreSQL connection closed');
107
+ }
108
+ /**
109
+ * Execute query with RLS context
110
+ */
111
+ async query(sql, params) {
112
+ this.ensureConnected();
113
+ const startTime = Date.now();
114
+ const client = await this.pool.connect();
115
+ try {
116
+ // Set RLS context if available
117
+ const rlsStatements = this.rlsManager.generateRLSStatements();
118
+ for (const stmt of rlsStatements) {
119
+ await client.query(stmt);
120
+ }
121
+ // Execute query
122
+ const result = await client.query(sql, params);
123
+ const duration = Date.now() - startTime;
124
+ this.logger.debug('Query executed', {
125
+ duration,
126
+ rowCount: result.rowCount,
127
+ command: result.command
128
+ });
129
+ return {
130
+ rows: result.rows,
131
+ rowCount: result.rowCount || 0,
132
+ fields: result.fields.map(f => ({
133
+ name: f.name,
134
+ dataTypeID: f.dataTypeID,
135
+ dataTypeName: this.getDataTypeName(f.dataTypeID),
136
+ nullable: true // PostgreSQL doesn't expose this easily
137
+ })),
138
+ command: result.command
139
+ };
140
+ }
141
+ catch (error) {
142
+ const sanitized = this.sanitizeError(error);
143
+ this.logger.error('Query execution failed', sanitized, {
144
+ duration: Date.now() - startTime
145
+ });
146
+ throw sanitized;
147
+ }
148
+ finally {
149
+ client.release();
150
+ }
151
+ }
152
+ /**
153
+ * Begin transaction with RLS context
154
+ */
155
+ async beginTransaction() {
156
+ this.ensureConnected();
157
+ const client = await this.pool.connect();
158
+ // Set RLS context for transaction
159
+ const rlsStatements = this.rlsManager.generateRLSStatements();
160
+ for (const stmt of rlsStatements) {
161
+ await client.query(stmt);
162
+ }
163
+ await client.query('BEGIN');
164
+ this.logger.debug('Transaction started');
165
+ return new PostgreSQLTransaction_1.PostgreSQLTransaction(client, this.rlsManager, this.logger);
166
+ }
167
+ /**
168
+ * Health check with connection pool stats
169
+ */
170
+ async healthCheck() {
171
+ if (!this.connected || !this.pool) {
172
+ return {
173
+ connected: false,
174
+ latencyMs: -1,
175
+ version: 'unknown'
176
+ };
177
+ }
178
+ const start = Date.now();
179
+ try {
180
+ const result = await this.pool.query('SELECT version()');
181
+ const latencyMs = Date.now() - start;
182
+ return {
183
+ connected: true,
184
+ latencyMs,
185
+ version: result.rows[0].version,
186
+ activeConnections: this.pool.totalCount,
187
+ maxConnections: this.pool.options.max
188
+ };
189
+ }
190
+ catch (error) {
191
+ const sanitized = this.sanitizeError(error);
192
+ this.logger.error('Health check failed', sanitized);
193
+ return {
194
+ connected: false,
195
+ latencyMs: -1,
196
+ version: 'error'
197
+ };
198
+ }
199
+ }
200
+ /**
201
+ * Get Supabase client for real-time features
202
+ */
203
+ getSupabaseClient() {
204
+ return this.supabase;
205
+ }
206
+ /**
207
+ * Set RLS context for current operation
208
+ */
209
+ setRLSContext(userId, userRole, tenantId) {
210
+ this.rlsManager.setContext({ userId, userRole, tenantId });
211
+ this.logger.debug('RLS context set', { userId, userRole, tenantId });
212
+ }
213
+ /**
214
+ * Clear RLS context
215
+ */
216
+ clearRLSContext() {
217
+ this.rlsManager.clearContext();
218
+ this.logger.debug('RLS context cleared');
219
+ }
220
+ /**
221
+ * Get current connection pool statistics
222
+ */
223
+ getPoolStats() {
224
+ if (!this.pool)
225
+ return null;
226
+ return {
227
+ total: this.pool.totalCount,
228
+ idle: this.pool.idleCount,
229
+ waiting: this.pool.waitingCount
230
+ };
231
+ }
232
+ /**
233
+ * Map PostgreSQL data type OID to name
234
+ */
235
+ getDataTypeName(oid) {
236
+ const typeMap = {
237
+ 16: 'BOOL',
238
+ 20: 'BIGINT',
239
+ 21: 'SMALLINT',
240
+ 23: 'INTEGER',
241
+ 25: 'TEXT',
242
+ 114: 'JSON',
243
+ 1043: 'VARCHAR',
244
+ 1082: 'DATE',
245
+ 1114: 'TIMESTAMP',
246
+ 1184: 'TIMESTAMPTZ',
247
+ 3802: 'JSONB'
248
+ };
249
+ return typeMap[oid] || 'UNKNOWN';
250
+ }
251
+ }
252
+ exports.PostgreSQLAdapter = PostgreSQLAdapter;
@@ -0,0 +1,46 @@
1
+ import { PoolClient } from 'pg';
2
+ import { ITransaction, IQueryResult, RLSContextManager } from '@bernierllc/database-adapter-core';
3
+ import { Logger } from '@bernierllc/logger';
4
+ /**
5
+ * PostgreSQL transaction implementation with RLS context support
6
+ */
7
+ export declare class PostgreSQLTransaction implements ITransaction {
8
+ private client;
9
+ private _rlsManager;
10
+ id: string;
11
+ private active;
12
+ private logger?;
13
+ constructor(client: PoolClient, _rlsManager: RLSContextManager, logger?: Logger);
14
+ /**
15
+ * Execute query within transaction
16
+ */
17
+ query<T = unknown>(sql: string, params?: unknown[]): Promise<IQueryResult<T>>;
18
+ /**
19
+ * Commit transaction and release client
20
+ */
21
+ commit(): Promise<void>;
22
+ /**
23
+ * Rollback transaction and release client
24
+ */
25
+ rollback(): Promise<void>;
26
+ /**
27
+ * Create a savepoint within the transaction
28
+ */
29
+ savepoint(name: string): Promise<void>;
30
+ /**
31
+ * Rollback to a savepoint
32
+ */
33
+ rollbackToSavepoint(name: string): Promise<void>;
34
+ /**
35
+ * Release a savepoint
36
+ */
37
+ releaseSavepoint(name: string): Promise<void>;
38
+ /**
39
+ * Check if transaction is still active
40
+ */
41
+ isActive(): boolean;
42
+ /**
43
+ * Map PostgreSQL data type OID to name
44
+ */
45
+ private getDataTypeName;
46
+ }
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.PostgreSQLTransaction = void 0;
11
+ /**
12
+ * PostgreSQL transaction implementation with RLS context support
13
+ */
14
+ class PostgreSQLTransaction {
15
+ constructor(client,
16
+ // @ts-expect-error - Reserved for future RLS context support in transactions
17
+ _rlsManager, logger) {
18
+ this.client = client;
19
+ this._rlsManager = _rlsManager;
20
+ this.active = true;
21
+ this.id = `txn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
22
+ this.logger = logger;
23
+ if (this.logger) {
24
+ this.logger.debug('Transaction created', { id: this.id });
25
+ }
26
+ }
27
+ /**
28
+ * Execute query within transaction
29
+ */
30
+ async query(sql, params) {
31
+ if (!this.active) {
32
+ throw new Error('Transaction is not active');
33
+ }
34
+ const startTime = Date.now();
35
+ try {
36
+ const result = await this.client.query(sql, params);
37
+ const duration = Date.now() - startTime;
38
+ if (this.logger) {
39
+ this.logger.debug('Transaction query executed', {
40
+ transactionId: this.id,
41
+ duration,
42
+ rowCount: result.rowCount,
43
+ command: result.command
44
+ });
45
+ }
46
+ return {
47
+ rows: result.rows,
48
+ rowCount: result.rowCount || 0,
49
+ fields: result.fields.map(f => ({
50
+ name: f.name,
51
+ dataTypeID: f.dataTypeID,
52
+ dataTypeName: this.getDataTypeName(f.dataTypeID),
53
+ nullable: true
54
+ })),
55
+ command: result.command
56
+ };
57
+ }
58
+ catch (error) {
59
+ if (this.logger) {
60
+ this.logger.error('Transaction query failed', error instanceof Error ? error : new Error(String(error)), {
61
+ transactionId: this.id,
62
+ duration: Date.now() - startTime
63
+ });
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+ /**
69
+ * Commit transaction and release client
70
+ */
71
+ async commit() {
72
+ if (!this.active) {
73
+ throw new Error('Transaction is not active');
74
+ }
75
+ const startTime = Date.now();
76
+ try {
77
+ await this.client.query('COMMIT');
78
+ this.active = false;
79
+ if (this.logger) {
80
+ this.logger.debug('Transaction committed', {
81
+ transactionId: this.id,
82
+ duration: Date.now() - startTime
83
+ });
84
+ }
85
+ }
86
+ catch (error) {
87
+ if (this.logger) {
88
+ this.logger.error('Transaction commit failed', error instanceof Error ? error : new Error(String(error)), {
89
+ transactionId: this.id
90
+ });
91
+ }
92
+ throw error;
93
+ }
94
+ finally {
95
+ this.client.release();
96
+ }
97
+ }
98
+ /**
99
+ * Rollback transaction and release client
100
+ */
101
+ async rollback() {
102
+ if (!this.active) {
103
+ throw new Error('Transaction is not active');
104
+ }
105
+ const startTime = Date.now();
106
+ try {
107
+ await this.client.query('ROLLBACK');
108
+ this.active = false;
109
+ if (this.logger) {
110
+ this.logger.debug('Transaction rolled back', {
111
+ transactionId: this.id,
112
+ duration: Date.now() - startTime
113
+ });
114
+ }
115
+ }
116
+ catch (error) {
117
+ if (this.logger) {
118
+ this.logger.error('Transaction rollback failed', error instanceof Error ? error : new Error(String(error)), {
119
+ transactionId: this.id
120
+ });
121
+ }
122
+ throw error;
123
+ }
124
+ finally {
125
+ this.client.release();
126
+ }
127
+ }
128
+ /**
129
+ * Create a savepoint within the transaction
130
+ */
131
+ async savepoint(name) {
132
+ if (!this.active) {
133
+ throw new Error('Transaction is not active');
134
+ }
135
+ await this.client.query(`SAVEPOINT ${name}`);
136
+ if (this.logger) {
137
+ this.logger.debug('Savepoint created', {
138
+ transactionId: this.id,
139
+ savepoint: name
140
+ });
141
+ }
142
+ }
143
+ /**
144
+ * Rollback to a savepoint
145
+ */
146
+ async rollbackToSavepoint(name) {
147
+ if (!this.active) {
148
+ throw new Error('Transaction is not active');
149
+ }
150
+ await this.client.query(`ROLLBACK TO SAVEPOINT ${name}`);
151
+ if (this.logger) {
152
+ this.logger.debug('Rolled back to savepoint', {
153
+ transactionId: this.id,
154
+ savepoint: name
155
+ });
156
+ }
157
+ }
158
+ /**
159
+ * Release a savepoint
160
+ */
161
+ async releaseSavepoint(name) {
162
+ if (!this.active) {
163
+ throw new Error('Transaction is not active');
164
+ }
165
+ await this.client.query(`RELEASE SAVEPOINT ${name}`);
166
+ if (this.logger) {
167
+ this.logger.debug('Savepoint released', {
168
+ transactionId: this.id,
169
+ savepoint: name
170
+ });
171
+ }
172
+ }
173
+ /**
174
+ * Check if transaction is still active
175
+ */
176
+ isActive() {
177
+ return this.active;
178
+ }
179
+ /**
180
+ * Map PostgreSQL data type OID to name
181
+ */
182
+ getDataTypeName(oid) {
183
+ const typeMap = {
184
+ 16: 'BOOL',
185
+ 20: 'BIGINT',
186
+ 21: 'SMALLINT',
187
+ 23: 'INTEGER',
188
+ 25: 'TEXT',
189
+ 114: 'JSON',
190
+ 1043: 'VARCHAR',
191
+ 1082: 'DATE',
192
+ 1114: 'TIMESTAMP',
193
+ 1184: 'TIMESTAMPTZ',
194
+ 3802: 'JSONB'
195
+ };
196
+ return typeMap[oid] || 'UNKNOWN';
197
+ }
198
+ }
199
+ exports.PostgreSQLTransaction = PostgreSQLTransaction;
@@ -0,0 +1,7 @@
1
+ export { PostgreSQLAdapter, ISupabaseConfig } from './PostgreSQLAdapter';
2
+ export { PostgreSQLTransaction } from './PostgreSQLTransaction';
3
+ export { RLSPolicyGenerator } from './rls/RLSPolicyGenerator';
4
+ export { ClerkIntegration } from './rls/ClerkIntegration';
5
+ export { RealtimeManager, RealtimePayload, RealtimeCallbacks, PresenceData } from './realtime/RealtimeManager';
6
+ export { FullTextSearchManager, SearchResult, SearchOptions } from './search/FullTextSearchManager';
7
+ export { migration_001_initial_schema } from './migrations/001_initial_schema';
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.migration_001_initial_schema = exports.FullTextSearchManager = exports.RealtimeManager = exports.ClerkIntegration = exports.RLSPolicyGenerator = exports.PostgreSQLTransaction = exports.PostgreSQLAdapter = void 0;
11
+ // Main adapter
12
+ var PostgreSQLAdapter_1 = require("./PostgreSQLAdapter");
13
+ Object.defineProperty(exports, "PostgreSQLAdapter", { enumerable: true, get: function () { return PostgreSQLAdapter_1.PostgreSQLAdapter; } });
14
+ var PostgreSQLTransaction_1 = require("./PostgreSQLTransaction");
15
+ Object.defineProperty(exports, "PostgreSQLTransaction", { enumerable: true, get: function () { return PostgreSQLTransaction_1.PostgreSQLTransaction; } });
16
+ // RLS components
17
+ var RLSPolicyGenerator_1 = require("./rls/RLSPolicyGenerator");
18
+ Object.defineProperty(exports, "RLSPolicyGenerator", { enumerable: true, get: function () { return RLSPolicyGenerator_1.RLSPolicyGenerator; } });
19
+ var ClerkIntegration_1 = require("./rls/ClerkIntegration");
20
+ Object.defineProperty(exports, "ClerkIntegration", { enumerable: true, get: function () { return ClerkIntegration_1.ClerkIntegration; } });
21
+ // Real-time
22
+ var RealtimeManager_1 = require("./realtime/RealtimeManager");
23
+ Object.defineProperty(exports, "RealtimeManager", { enumerable: true, get: function () { return RealtimeManager_1.RealtimeManager; } });
24
+ // Search
25
+ var FullTextSearchManager_1 = require("./search/FullTextSearchManager");
26
+ Object.defineProperty(exports, "FullTextSearchManager", { enumerable: true, get: function () { return FullTextSearchManager_1.FullTextSearchManager; } });
27
+ // Migrations
28
+ var _001_initial_schema_1 = require("./migrations/001_initial_schema");
29
+ Object.defineProperty(exports, "migration_001_initial_schema", { enumerable: true, get: function () { return _001_initial_schema_1.migration_001_initial_schema; } });
@@ -0,0 +1,2 @@
1
+ import { IMigration } from '@bernierllc/database-adapter-core';
2
+ export declare const migration_001_initial_schema: IMigration;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.migration_001_initial_schema = void 0;
11
+ exports.migration_001_initial_schema = {
12
+ id: '001_initial_schema',
13
+ name: 'Create initial content management schema',
14
+ async up(adapter) {
15
+ await adapter.query(`
16
+ -- Enable extensions
17
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
18
+ CREATE EXTENSION IF NOT EXISTS "pg_trgm";
19
+
20
+ -- Users table (Clerk integration)
21
+ CREATE TABLE IF NOT EXISTS users (
22
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
23
+ clerk_id VARCHAR(255) UNIQUE NOT NULL,
24
+ email VARCHAR(255) UNIQUE NOT NULL,
25
+ username VARCHAR(100),
26
+ full_name VARCHAR(255),
27
+ avatar_url TEXT,
28
+ role VARCHAR(50) DEFAULT 'user',
29
+ tenant_id UUID,
30
+ preferences JSONB DEFAULT '{}',
31
+ created_at TIMESTAMPTZ DEFAULT NOW(),
32
+ updated_at TIMESTAMPTZ DEFAULT NOW()
33
+ );
34
+
35
+ CREATE INDEX IF NOT EXISTS idx_users_clerk_id ON users(clerk_id);
36
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
37
+
38
+ -- Content table
39
+ CREATE TABLE IF NOT EXISTS content (
40
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
41
+ user_id UUID REFERENCES users(id) ON DELETE CASCADE,
42
+ title VARCHAR(500) NOT NULL,
43
+ slug VARCHAR(500) UNIQUE,
44
+ body TEXT NOT NULL,
45
+ excerpt TEXT,
46
+ type VARCHAR(50) DEFAULT 'blog',
47
+ format VARCHAR(50) DEFAULT 'markdown',
48
+ status VARCHAR(50) DEFAULT 'draft',
49
+ workflow_stage VARCHAR(100),
50
+ assigned_to UUID REFERENCES users(id),
51
+ ai_reviewed BOOLEAN DEFAULT FALSE,
52
+ ai_review_score DECIMAL(3,2),
53
+ keywords JSONB DEFAULT '[]',
54
+ view_count INTEGER DEFAULT 0,
55
+ share_count INTEGER DEFAULT 0,
56
+ version INTEGER DEFAULT 1,
57
+ published_at TIMESTAMPTZ,
58
+ scheduled_for TIMESTAMPTZ,
59
+ deleted_at TIMESTAMPTZ,
60
+ created_at TIMESTAMPTZ DEFAULT NOW(),
61
+ updated_at TIMESTAMPTZ DEFAULT NOW()
62
+ );
63
+
64
+ -- Content versions
65
+ CREATE TABLE IF NOT EXISTS content_versions (
66
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
67
+ content_id UUID REFERENCES content(id) ON DELETE CASCADE,
68
+ version_number INTEGER NOT NULL,
69
+ title VARCHAR(500),
70
+ body TEXT NOT NULL,
71
+ change_summary TEXT,
72
+ is_autosave BOOLEAN DEFAULT FALSE,
73
+ created_at TIMESTAMPTZ DEFAULT NOW(),
74
+ created_by UUID REFERENCES users(id),
75
+ UNIQUE(content_id, version_number)
76
+ );
77
+
78
+ -- Enable RLS
79
+ ALTER TABLE users ENABLE ROW LEVEL SECURITY;
80
+ ALTER TABLE content ENABLE ROW LEVEL SECURITY;
81
+ ALTER TABLE content_versions ENABLE ROW LEVEL SECURITY;
82
+ `);
83
+ },
84
+ async down(adapter) {
85
+ await adapter.query(`
86
+ DROP TABLE IF EXISTS content_versions CASCADE;
87
+ DROP TABLE IF EXISTS content CASCADE;
88
+ DROP TABLE IF EXISTS users CASCADE;
89
+ `);
90
+ }
91
+ };