@kysera/rls 0.7.3 → 0.8.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.
@@ -4,8 +4,8 @@
4
4
  * Provides functions to define, validate, and merge RLS schemas.
5
5
  */
6
6
 
7
- import type { RLSSchema, TableRLSConfig, PolicyDefinition } from './types.js';
8
- import { RLSSchemaError } from '../errors.js';
7
+ import type { RLSSchema, TableRLSConfig, PolicyDefinition } from './types.js'
8
+ import { RLSSchemaError } from '../errors.js'
9
9
 
10
10
  /**
11
11
  * Define RLS schema with full type safety
@@ -42,12 +42,10 @@ import { RLSSchemaError } from '../errors.js';
42
42
  * });
43
43
  * ```
44
44
  */
45
- export function defineRLSSchema<DB>(
46
- schema: RLSSchema<DB>
47
- ): RLSSchema<DB> {
45
+ export function defineRLSSchema<DB>(schema: RLSSchema<DB>): RLSSchema<DB> {
48
46
  // Validate schema
49
- validateSchema(schema);
50
- return schema;
47
+ validateSchema(schema)
48
+ return schema
51
49
  }
52
50
 
53
51
  /**
@@ -58,22 +56,19 @@ export function defineRLSSchema<DB>(
58
56
  */
59
57
  function validateSchema<DB>(schema: RLSSchema<DB>): void {
60
58
  for (const [table, config] of Object.entries(schema)) {
61
- if (!config) continue;
59
+ if (!config) continue
62
60
 
63
- const tableConfig = config as TableRLSConfig;
61
+ const tableConfig = config as TableRLSConfig
64
62
 
65
63
  if (!Array.isArray(tableConfig.policies)) {
66
- throw new RLSSchemaError(
67
- `Invalid policies for table "${table}": must be an array`,
68
- { table }
69
- );
64
+ throw new RLSSchemaError(`Invalid policies for table "${table}": must be an array`, { table })
70
65
  }
71
66
 
72
67
  // Validate each policy
73
68
  for (let i = 0; i < tableConfig.policies.length; i++) {
74
- const policy = tableConfig.policies[i];
69
+ const policy = tableConfig.policies[i]
75
70
  if (policy !== undefined) {
76
- validatePolicy(policy, table, i);
71
+ validatePolicy(policy, table, i)
77
72
  }
78
73
  }
79
74
 
@@ -83,7 +78,7 @@ function validateSchema<DB>(schema: RLSSchema<DB>): void {
83
78
  throw new RLSSchemaError(
84
79
  `Invalid skipFor for table "${table}": must be an array of role names`,
85
80
  { table }
86
- );
81
+ )
87
82
  }
88
83
 
89
84
  // skipFor contains role names (strings), not operations
@@ -92,17 +87,16 @@ function validateSchema<DB>(schema: RLSSchema<DB>): void {
92
87
  throw new RLSSchemaError(
93
88
  `Invalid role in skipFor for table "${table}": must be a non-empty string`,
94
89
  { table }
95
- );
90
+ )
96
91
  }
97
92
  }
98
93
  }
99
94
 
100
95
  // Validate defaultDeny if present
101
96
  if (tableConfig.defaultDeny !== undefined && typeof tableConfig.defaultDeny !== 'boolean') {
102
- throw new RLSSchemaError(
103
- `Invalid defaultDeny for table "${table}": must be a boolean`,
104
- { table }
105
- );
97
+ throw new RLSSchemaError(`Invalid defaultDeny for table "${table}": must be a boolean`, {
98
+ table
99
+ })
106
100
  }
107
101
  }
108
102
  }
@@ -113,73 +107,66 @@ function validateSchema<DB>(schema: RLSSchema<DB>): void {
113
107
  *
114
108
  * @internal
115
109
  */
116
- function validatePolicy(
117
- policy: PolicyDefinition,
118
- table: string,
119
- index: number
120
- ): void {
110
+ function validatePolicy(policy: PolicyDefinition, table: string, index: number): void {
121
111
  if (!policy.type) {
122
- throw new RLSSchemaError(
123
- `Policy ${index} for table "${table}" missing type`,
124
- { table, index }
125
- );
112
+ throw new RLSSchemaError(`Policy ${index} for table "${table}" missing type`, { table, index })
126
113
  }
127
114
 
128
- const validTypes = ['allow', 'deny', 'filter', 'validate'];
115
+ const validTypes = ['allow', 'deny', 'filter', 'validate']
129
116
  if (!validTypes.includes(policy.type)) {
130
117
  throw new RLSSchemaError(
131
118
  `Policy ${index} for table "${table}" has invalid type: ${policy.type}`,
132
119
  { table, index, type: policy.type }
133
- );
120
+ )
134
121
  }
135
122
 
136
123
  if (!policy.operation) {
137
- throw new RLSSchemaError(
138
- `Policy ${index} for table "${table}" missing operation`,
139
- { table, index }
140
- );
124
+ throw new RLSSchemaError(`Policy ${index} for table "${table}" missing operation`, {
125
+ table,
126
+ index
127
+ })
141
128
  }
142
129
 
143
- const validOps = ['read', 'create', 'update', 'delete', 'all'];
144
- const ops = Array.isArray(policy.operation) ? policy.operation : [policy.operation];
130
+ const validOps = ['read', 'create', 'update', 'delete', 'all']
131
+ const ops = Array.isArray(policy.operation) ? policy.operation : [policy.operation]
145
132
 
146
133
  for (const op of ops) {
147
134
  if (!validOps.includes(op)) {
148
135
  throw new RLSSchemaError(
149
136
  `Policy ${index} for table "${table}" has invalid operation: ${op}`,
150
137
  { table, index, operation: op }
151
- );
138
+ )
152
139
  }
153
140
  }
154
141
 
155
142
  if (policy.condition === undefined || policy.condition === null) {
156
- throw new RLSSchemaError(
157
- `Policy ${index} for table "${table}" missing condition`,
158
- { table, index }
159
- );
143
+ throw new RLSSchemaError(`Policy ${index} for table "${table}" missing condition`, {
144
+ table,
145
+ index
146
+ })
160
147
  }
161
148
 
162
149
  if (typeof policy.condition !== 'function' && typeof policy.condition !== 'string') {
163
150
  throw new RLSSchemaError(
164
151
  `Policy ${index} for table "${table}" condition must be a function or string`,
165
152
  { table, index }
166
- );
153
+ )
167
154
  }
168
155
 
169
156
  // Validate priority if present
170
157
  if (policy.priority !== undefined && typeof policy.priority !== 'number') {
171
- throw new RLSSchemaError(
172
- `Policy ${index} for table "${table}" priority must be a number`,
173
- { table, index }
174
- );
158
+ throw new RLSSchemaError(`Policy ${index} for table "${table}" priority must be a number`, {
159
+ table,
160
+ index
161
+ })
175
162
  }
176
163
 
177
164
  // Validate name if present
178
165
  if (policy.name !== undefined && typeof policy.name !== 'string') {
179
- throw new RLSSchemaError(
180
- `Policy ${index} for table "${table}" name must be a string`,
181
- { table, index }
182
- );
166
+ throw new RLSSchemaError(`Policy ${index} for table "${table}" name must be a string`, {
167
+ table,
168
+ index
169
+ })
183
170
  }
184
171
  }
185
172
 
@@ -209,49 +196,49 @@ function validatePolicy(
209
196
  * const merged = mergeRLSSchemas(baseSchema, adminSchema);
210
197
  * ```
211
198
  */
212
- export function mergeRLSSchemas<DB>(
213
- ...schemas: RLSSchema<DB>[]
214
- ): RLSSchema<DB> {
215
- const merged: RLSSchema<DB> = {};
199
+ export function mergeRLSSchemas<DB>(...schemas: RLSSchema<DB>[]): RLSSchema<DB> {
200
+ const merged: RLSSchema<DB> = {}
216
201
 
217
202
  for (const schema of schemas) {
218
203
  for (const [table, config] of Object.entries(schema)) {
219
- if (!config) continue;
204
+ if (!config) continue
220
205
 
221
- const existingConfig = merged[table as keyof DB] as TableRLSConfig | undefined;
222
- const newConfig = config as TableRLSConfig;
206
+ const existingConfig = merged[table as keyof DB]
207
+ const newConfig = config as TableRLSConfig
223
208
 
224
209
  if (existingConfig) {
225
210
  // Merge policies (append new policies)
226
- existingConfig.policies = [
227
- ...existingConfig.policies,
228
- ...newConfig.policies,
229
- ];
211
+ existingConfig.policies = [...existingConfig.policies, ...newConfig.policies]
230
212
 
231
213
  // Merge skipFor (combine arrays and deduplicate)
232
214
  if (newConfig.skipFor) {
233
- const existingSkipFor = existingConfig.skipFor ?? [];
234
- const combinedSkipFor = [...existingSkipFor, ...newConfig.skipFor];
235
- existingConfig.skipFor = Array.from(new Set(combinedSkipFor));
215
+ const existingSkipFor = existingConfig.skipFor ?? []
216
+ const combinedSkipFor = [...existingSkipFor, ...newConfig.skipFor]
217
+ existingConfig.skipFor = Array.from(new Set(combinedSkipFor))
236
218
  }
237
219
 
238
220
  // Override defaultDeny if explicitly set in new config
239
221
  if (newConfig.defaultDeny !== undefined) {
240
- existingConfig.defaultDeny = newConfig.defaultDeny;
222
+ existingConfig.defaultDeny = newConfig.defaultDeny
241
223
  }
242
224
  } else {
243
225
  // Deep copy the config to avoid mutation
226
+ // Type cast necessary: TypeScript cannot infer that the spread object matches
227
+ // the TableRLSConfig type exactly due to optional properties and type narrowing.
228
+ // Runtime safety: We've validated the schema structure via validateSchema(),
229
+ // and we're creating a proper TableRLSConfig from validated components.
230
+
244
231
  merged[table as keyof DB] = {
245
232
  policies: [...newConfig.policies],
246
233
  skipFor: newConfig.skipFor ? [...newConfig.skipFor] : undefined,
247
- defaultDeny: newConfig.defaultDeny,
248
- } as any;
234
+ defaultDeny: newConfig.defaultDeny
235
+ } as TableRLSConfig
249
236
  }
250
237
  }
251
238
  }
252
239
 
253
240
  // Validate merged schema
254
- validateSchema(merged);
241
+ validateSchema(merged)
255
242
 
256
- return merged;
243
+ return merged
257
244
  }
@@ -8,7 +8,7 @@
8
8
  * @module @kysera/rls/policy/types
9
9
  */
10
10
 
11
- import type { Kysely } from 'kysely';
11
+ import type { Kysely } from 'kysely'
12
12
 
13
13
  // ============================================================================
14
14
  // Operation Types
@@ -32,7 +32,7 @@ import type { Kysely } from 'kysely';
32
32
  * };
33
33
  * ```
34
34
  */
35
- export type Operation = 'read' | 'create' | 'update' | 'delete' | 'all';
35
+ export type Operation = 'read' | 'create' | 'update' | 'delete' | 'all'
36
36
 
37
37
  // ============================================================================
38
38
  // Authentication Context
@@ -63,43 +63,43 @@ export interface RLSAuthContext<TUser = unknown> {
63
63
  * Unique identifier for the authenticated user
64
64
  * Can be a string or number depending on your ID strategy
65
65
  */
66
- userId: string | number;
66
+ userId: string | number
67
67
 
68
68
  /**
69
69
  * List of roles assigned to the user
70
70
  * Used for role-based access control (RBAC)
71
71
  */
72
- roles: string[];
72
+ roles: string[]
73
73
 
74
74
  /**
75
75
  * Optional tenant identifier for multi-tenancy
76
76
  * Use for tenant isolation in SaaS applications
77
77
  */
78
- tenantId?: string | number;
78
+ tenantId?: string | number
79
79
 
80
80
  /**
81
81
  * Optional list of organization IDs the user belongs to
82
82
  * Use for hierarchical multi-tenancy or organization-based access
83
83
  */
84
- organizationIds?: (string | number)[];
84
+ organizationIds?: (string | number)[]
85
85
 
86
86
  /**
87
87
  * Optional list of granular permissions
88
88
  * Use for fine-grained access control
89
89
  */
90
- permissions?: string[];
90
+ permissions?: string[]
91
91
 
92
92
  /**
93
93
  * Optional custom attributes for advanced policy logic
94
94
  * Can contain any additional context needed for policy evaluation
95
95
  */
96
- attributes?: Record<string, unknown>;
96
+ attributes?: Record<string, unknown>
97
97
 
98
98
  /**
99
99
  * Optional full user object for accessing user properties
100
100
  * Useful when policies need to check user-specific attributes
101
101
  */
102
- user?: TUser;
102
+ user?: TUser
103
103
 
104
104
  /**
105
105
  * Flag indicating if this is a system/admin context
@@ -107,7 +107,7 @@ export interface RLSAuthContext<TUser = unknown> {
107
107
  *
108
108
  * @default false
109
109
  */
110
- isSystem?: boolean;
110
+ isSystem?: boolean
111
111
  }
112
112
 
113
113
  // ============================================================================
@@ -136,31 +136,31 @@ export interface RLSRequestContext {
136
136
  * Unique identifier for the request
137
137
  * Useful for tracing and debugging
138
138
  */
139
- requestId?: string;
139
+ requestId?: string
140
140
 
141
141
  /**
142
142
  * Client IP address
143
143
  * Can be used for IP-based access policies
144
144
  */
145
- ipAddress?: string;
145
+ ipAddress?: string
146
146
 
147
147
  /**
148
148
  * Client user agent string
149
149
  * Useful for device-based access policies
150
150
  */
151
- userAgent?: string;
151
+ userAgent?: string
152
152
 
153
153
  /**
154
154
  * Request timestamp
155
155
  * Required for time-based policies and audit logs
156
156
  */
157
- timestamp: Date;
157
+ timestamp: Date
158
158
 
159
159
  /**
160
160
  * HTTP headers
161
161
  * Can contain custom authentication or context headers
162
162
  */
163
- headers?: Record<string, string>;
163
+ headers?: Record<string, string>
164
164
  }
165
165
 
166
166
  // ============================================================================
@@ -201,26 +201,26 @@ export interface RLSContext<TUser = unknown, TMeta = unknown> {
201
201
  * Authentication context (required)
202
202
  * Contains user identity and authorization information
203
203
  */
204
- auth: RLSAuthContext<TUser>;
204
+ auth: RLSAuthContext<TUser>
205
205
 
206
206
  /**
207
207
  * Request context (optional)
208
208
  * Contains HTTP request information
209
209
  */
210
- request?: RLSRequestContext;
210
+ request?: RLSRequestContext
211
211
 
212
212
  /**
213
213
  * Custom metadata (optional)
214
214
  * Can contain any additional context needed for policy evaluation
215
215
  * Examples: feature flags, A/B test groups, regional settings
216
216
  */
217
- meta?: TMeta;
217
+ meta?: TMeta
218
218
 
219
219
  /**
220
220
  * Context creation timestamp
221
221
  * Used for temporal policies and audit trails
222
222
  */
223
- timestamp: Date;
223
+ timestamp: Date
224
224
  }
225
225
 
226
226
  // ============================================================================
@@ -237,7 +237,7 @@ export interface RLSContext<TUser = unknown, TMeta = unknown> {
237
237
  * @typeParam TAuth - Custom user type for auth context
238
238
  * @typeParam TRow - Type of the database row being evaluated
239
239
  * @typeParam TData - Type of the data being inserted/updated
240
- * @typeParam TDB - Database schema type for Kysely
240
+ * @typeParam DB - Database schema type for Kysely
241
241
  *
242
242
  * @example
243
243
  * ```typescript
@@ -259,58 +259,58 @@ export interface PolicyEvaluationContext<
259
259
  TAuth = unknown,
260
260
  TRow = unknown,
261
261
  TData = unknown,
262
- TDB = unknown
262
+ DB = unknown
263
263
  > {
264
264
  /**
265
265
  * Authentication context
266
266
  * Contains user identity and authorization information
267
267
  */
268
- auth: RLSAuthContext<TAuth>;
268
+ auth: RLSAuthContext<TAuth>
269
269
 
270
270
  /**
271
271
  * Current row being evaluated (optional)
272
272
  * Available during read/update/delete operations
273
273
  * Used for row-level policies that check row attributes
274
274
  */
275
- row?: TRow;
275
+ row?: TRow
276
276
 
277
277
  /**
278
278
  * Data being inserted or updated (optional)
279
279
  * Available during create/update operations
280
280
  * Used for validation policies
281
281
  */
282
- data?: TData;
282
+ data?: TData
283
283
 
284
284
  /**
285
285
  * Request context (optional)
286
286
  * Contains HTTP request information
287
287
  */
288
- request?: RLSRequestContext;
288
+ request?: RLSRequestContext
289
289
 
290
290
  /**
291
291
  * Kysely database instance (optional)
292
292
  * Available for policies that need to perform additional queries
293
293
  * Use sparingly as it can impact performance
294
294
  */
295
- db?: Kysely<TDB>;
295
+ db?: Kysely<DB>
296
296
 
297
297
  /**
298
298
  * Custom metadata (optional)
299
299
  * Can contain any additional context needed for policy evaluation
300
300
  */
301
- meta?: Record<string, unknown>;
301
+ meta?: Record<string, unknown>
302
302
 
303
303
  /**
304
304
  * Table name being accessed (optional)
305
305
  * Available during mutation operations
306
306
  */
307
- table?: string;
307
+ table?: string
308
308
 
309
309
  /**
310
310
  * Operation being performed (optional)
311
311
  * E.g., 'create', 'update', 'delete'
312
312
  */
313
- operation?: string;
313
+ operation?: string
314
314
  }
315
315
 
316
316
  // ============================================================================
@@ -346,7 +346,7 @@ export interface PolicyEvaluationContext<
346
346
  */
347
347
  export type PolicyCondition<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> =
348
348
  | ((ctx: TCtx) => boolean | Promise<boolean>)
349
- | string;
349
+ | string
350
350
 
351
351
  /**
352
352
  * Filter condition that returns WHERE clause conditions
@@ -375,7 +375,7 @@ export type PolicyCondition<TCtx extends PolicyEvaluationContext = PolicyEvaluat
375
375
  */
376
376
  export type FilterCondition<TCtx extends PolicyEvaluationContext = PolicyEvaluationContext> =
377
377
  | ((ctx: TCtx) => Record<string, unknown>)
378
- | Record<string, string>;
378
+ | Record<string, string>
379
379
 
380
380
  // ============================================================================
381
381
  // Policy Types
@@ -420,7 +420,7 @@ export type FilterCondition<TCtx extends PolicyEvaluationContext = PolicyEvaluat
420
420
  * };
421
421
  * ```
422
422
  */
423
- export type PolicyType = 'allow' | 'deny' | 'filter' | 'validate';
423
+ export type PolicyType = 'allow' | 'deny' | 'filter' | 'validate'
424
424
 
425
425
  // ============================================================================
426
426
  // Policy Definition
@@ -474,13 +474,13 @@ export interface PolicyDefinition<
474
474
  * Policy behavior type
475
475
  * Determines how the policy affects access control
476
476
  */
477
- type: PolicyType;
477
+ type: PolicyType
478
478
 
479
479
  /**
480
480
  * Operation(s) this policy applies to
481
481
  * Can be a single operation or an array of operations
482
482
  */
483
- operation: TOperation | TOperation[];
483
+ operation: TOperation | TOperation[]
484
484
 
485
485
  /**
486
486
  * Condition function or expression
@@ -488,13 +488,13 @@ export interface PolicyDefinition<
488
488
  * - For 'filter': returns WHERE conditions object
489
489
  * - For native RLS: SQL expression string
490
490
  */
491
- condition: TCondition;
491
+ condition: TCondition
492
492
 
493
493
  /**
494
494
  * Optional policy name for debugging and logging
495
495
  * Recommended for easier policy management
496
496
  */
497
- name?: string;
497
+ name?: string
498
498
 
499
499
  /**
500
500
  * Optional priority for policy evaluation order
@@ -503,7 +503,7 @@ export interface PolicyDefinition<
503
503
  *
504
504
  * @default 0
505
505
  */
506
- priority?: number;
506
+ priority?: number
507
507
 
508
508
  /**
509
509
  * Optional SQL expression for native RLS USING clause
@@ -511,7 +511,7 @@ export interface PolicyDefinition<
511
511
  *
512
512
  * @example 'user_id = current_user_id()'
513
513
  */
514
- using?: string;
514
+ using?: string
515
515
 
516
516
  /**
517
517
  * Optional SQL expression for native RLS WITH CHECK clause
@@ -519,7 +519,7 @@ export interface PolicyDefinition<
519
519
  *
520
520
  * @example 'tenant_id = current_tenant_id()'
521
521
  */
522
- withCheck?: string;
522
+ withCheck?: string
523
523
 
524
524
  /**
525
525
  * Optional database role this policy applies to
@@ -527,13 +527,13 @@ export interface PolicyDefinition<
527
527
  *
528
528
  * @example 'authenticated'
529
529
  */
530
- role?: string;
530
+ role?: string
531
531
 
532
532
  /**
533
533
  * Optional performance optimization hints
534
534
  * Used for query optimization and index suggestions
535
535
  */
536
- hints?: PolicyHints;
536
+ hints?: PolicyHints
537
537
  }
538
538
 
539
539
  // ============================================================================
@@ -573,7 +573,7 @@ export interface TableRLSConfig {
573
573
  * List of policies to enforce on this table
574
574
  * Policies are evaluated in priority order (highest first)
575
575
  */
576
- policies: PolicyDefinition[];
576
+ policies: PolicyDefinition[]
577
577
 
578
578
  /**
579
579
  * Default behavior when no policies match
@@ -582,7 +582,7 @@ export interface TableRLSConfig {
582
582
  *
583
583
  * @default true
584
584
  */
585
- defaultDeny?: boolean;
585
+ defaultDeny?: boolean
586
586
 
587
587
  /**
588
588
  * List of roles that bypass RLS policies
@@ -590,7 +590,7 @@ export interface TableRLSConfig {
590
590
  *
591
591
  * @example ['system', 'admin', 'superuser']
592
592
  */
593
- skipFor?: string[];
593
+ skipFor?: string[]
594
594
  }
595
595
 
596
596
  // ============================================================================
@@ -636,8 +636,8 @@ export interface TableRLSConfig {
636
636
  * ```
637
637
  */
638
638
  export type RLSSchema<DB> = {
639
- [K in keyof DB]?: TableRLSConfig;
640
- };
639
+ [K in keyof DB]?: TableRLSConfig
640
+ }
641
641
 
642
642
  // ============================================================================
643
643
  // Compiled Policies
@@ -654,29 +654,29 @@ export interface CompiledPolicy<TCtx = PolicyEvaluationContext> {
654
654
  /**
655
655
  * Policy behavior type
656
656
  */
657
- type: PolicyType;
657
+ type: PolicyType
658
658
 
659
659
  /**
660
660
  * Operations this policy applies to
661
661
  * Always an array after compilation
662
662
  */
663
- operation: Operation[];
663
+ operation: Operation[]
664
664
 
665
665
  /**
666
666
  * Compiled evaluation function
667
667
  * Returns boolean or Promise<boolean>
668
668
  */
669
- evaluate: (ctx: TCtx) => boolean | Promise<boolean>;
669
+ evaluate: (ctx: TCtx) => boolean | Promise<boolean>
670
670
 
671
671
  /**
672
672
  * Policy name for debugging
673
673
  */
674
- name: string;
674
+ name: string
675
675
 
676
676
  /**
677
677
  * Priority for evaluation order
678
678
  */
679
- priority: number;
679
+ priority: number
680
680
  }
681
681
 
682
682
  /**
@@ -690,18 +690,18 @@ export interface CompiledFilterPolicy<TCtx = PolicyEvaluationContext> {
690
690
  /**
691
691
  * Always 'read' for filter policies
692
692
  */
693
- operation: 'read';
693
+ operation: 'read'
694
694
 
695
695
  /**
696
696
  * Function to get WHERE conditions
697
697
  * Returns object with column-value pairs
698
698
  */
699
- getConditions: (ctx: TCtx) => Record<string, unknown>;
699
+ getConditions: (ctx: TCtx) => Record<string, unknown>
700
700
 
701
701
  /**
702
702
  * Policy name for debugging
703
703
  */
704
- name: string;
704
+ name: string
705
705
  }
706
706
 
707
707
  // ============================================================================
@@ -729,7 +729,7 @@ export interface PolicyHints {
729
729
  * Columns that should be indexed for optimal performance
730
730
  * Suggests which columns are frequently used in policy conditions
731
731
  */
732
- indexColumns?: string[];
732
+ indexColumns?: string[]
733
733
 
734
734
  /**
735
735
  * Expected selectivity of the policy
@@ -737,18 +737,18 @@ export interface PolicyHints {
737
737
  * - 'medium': Filters moderate number of rows
738
738
  * - 'low': Filters few rows (evaluate later)
739
739
  */
740
- selectivity?: 'high' | 'medium' | 'low';
740
+ selectivity?: 'high' | 'medium' | 'low'
741
741
 
742
742
  /**
743
743
  * Whether the policy is leakproof (PostgreSQL concept)
744
744
  * Leakproof functions don't reveal information about their inputs
745
745
  * Safe to execute before other security checks
746
746
  */
747
- leakproof?: boolean;
747
+ leakproof?: boolean
748
748
 
749
749
  /**
750
750
  * Whether the policy result is stable for the same inputs
751
751
  * Stable policies can be cached during a query execution
752
752
  */
753
- stable?: boolean;
753
+ stable?: boolean
754
754
  }
@@ -1,2 +1,2 @@
1
- export { SelectTransformer } from './select.js';
2
- export { MutationGuard } from './mutation.js';
1
+ export { SelectTransformer } from './select.js'
2
+ export { MutationGuard } from './mutation.js'