@geekmidas/db 0.0.4 → 0.2.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,78 @@
1
+ import { SelectQueryBuilder } from "kysely";
2
+
3
+ //#region src/kysely/pagination.d.ts
4
+
5
+ /**
6
+ * Sort direction for cursor-based pagination.
7
+ */
8
+ declare enum Direction {
9
+ Asc = "asc",
10
+ Desc = "desc",
11
+ }
12
+ /**
13
+ * Result of a paginated query.
14
+ */
15
+ interface PaginationResult<TItem> {
16
+ items: TItem[];
17
+ pagination: {
18
+ total: number;
19
+ hasMore: boolean;
20
+ cursor?: string;
21
+ };
22
+ }
23
+ /**
24
+ * Options for paginated search.
25
+ */
26
+ interface PaginatedSearchOptions<TRow, TMapRow extends (row: TRow) => unknown> {
27
+ /** The base Kysely query to paginate */
28
+ query: SelectQueryBuilder<any, any, TRow>;
29
+ /** Cursor value for pagination (value of cursorField from previous page) */
30
+ cursor?: string;
31
+ /** Maximum number of items per page (default: 20) */
32
+ limit?: number;
33
+ /** Function to transform each row to the output format */
34
+ mapRow: TMapRow;
35
+ /** Field to use for cursor pagination (default: 'id') */
36
+ cursorField?: string;
37
+ /** Sort direction for cursor field (default: Direction.Asc) */
38
+ cursorDirection?: Direction;
39
+ }
40
+ /**
41
+ * Generic paginated search function that handles:
42
+ * - Total count calculation
43
+ * - Cursor-based pagination
44
+ * - Fetching one extra row to determine hasMore
45
+ * - Mapping rows to output format
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const result = await paginatedSearch({
50
+ * query: db.selectFrom('users').selectAll(),
51
+ * cursor: previousCursor,
52
+ * limit: 20,
53
+ * mapRow: (row) => ({ id: row.id, name: row.name }),
54
+ * cursorField: 'id',
55
+ * cursorDirection: Direction.Asc,
56
+ * });
57
+ * ```
58
+ */
59
+ declare function paginatedSearch<TRow extends Record<string, unknown>, TMapRow extends (row: TRow) => unknown>({
60
+ query,
61
+ cursor,
62
+ limit,
63
+ mapRow,
64
+ cursorField,
65
+ cursorDirection
66
+ }: PaginatedSearchOptions<TRow, TMapRow>): Promise<PaginationResult<Awaited<ReturnType<TMapRow>>>>;
67
+ /**
68
+ * Encode a cursor value for safe URL transmission.
69
+ * Supports various types: string, number, Date, etc.
70
+ */
71
+ declare function encodeCursor(value: unknown): string;
72
+ /**
73
+ * Decode a cursor string back to its original value.
74
+ */
75
+ declare function decodeCursor(cursor: string): unknown;
76
+ //#endregion
77
+ export { Direction, PaginatedSearchOptions, PaginationResult, decodeCursor, encodeCursor, paginatedSearch };
78
+ //# sourceMappingURL=pagination.d.mts.map
@@ -0,0 +1,79 @@
1
+ //#region src/kysely/pagination.ts
2
+ /**
3
+ * Sort direction for cursor-based pagination.
4
+ */
5
+ let Direction = /* @__PURE__ */ function(Direction$1) {
6
+ Direction$1["Asc"] = "asc";
7
+ Direction$1["Desc"] = "desc";
8
+ return Direction$1;
9
+ }({});
10
+ /**
11
+ * Generic paginated search function that handles:
12
+ * - Total count calculation
13
+ * - Cursor-based pagination
14
+ * - Fetching one extra row to determine hasMore
15
+ * - Mapping rows to output format
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const result = await paginatedSearch({
20
+ * query: db.selectFrom('users').selectAll(),
21
+ * cursor: previousCursor,
22
+ * limit: 20,
23
+ * mapRow: (row) => ({ id: row.id, name: row.name }),
24
+ * cursorField: 'id',
25
+ * cursorDirection: Direction.Asc,
26
+ * });
27
+ * ```
28
+ */
29
+ async function paginatedSearch({ query, cursor, limit = 20, mapRow, cursorField = "id", cursorDirection = Direction.Asc }) {
30
+ const countResult = await query.clearSelect().clearOrderBy().select((eb) => eb.fn.countAll().as("count")).executeTakeFirstOrThrow();
31
+ const count = countResult.count;
32
+ let paginatedQuery = query;
33
+ if (cursor) {
34
+ const operator = cursorDirection === Direction.Asc ? ">" : "<";
35
+ paginatedQuery = paginatedQuery.where(cursorField, operator, cursor);
36
+ }
37
+ const data = await paginatedQuery.orderBy(cursorField, cursorDirection).limit(limit + 1).execute();
38
+ const hasMore = data.length > limit;
39
+ const rows = hasMore ? data.slice(0, limit) : data;
40
+ const lastRow = rows[rows.length - 1];
41
+ const nextCursor = hasMore && lastRow ? String(lastRow[cursorField]) : void 0;
42
+ const items = await Promise.all(rows.map(mapRow));
43
+ return {
44
+ items,
45
+ pagination: {
46
+ total: Number(count),
47
+ hasMore,
48
+ cursor: nextCursor
49
+ }
50
+ };
51
+ }
52
+ /**
53
+ * Encode a cursor value for safe URL transmission.
54
+ * Supports various types: string, number, Date, etc.
55
+ */
56
+ function encodeCursor(value) {
57
+ const payload = {
58
+ v: value instanceof Date ? value.toISOString() : value,
59
+ t: value instanceof Date ? "date" : typeof value
60
+ };
61
+ return Buffer.from(JSON.stringify(payload)).toString("base64url");
62
+ }
63
+ /**
64
+ * Decode a cursor string back to its original value.
65
+ */
66
+ function decodeCursor(cursor) {
67
+ try {
68
+ const json = Buffer.from(cursor, "base64url").toString("utf-8");
69
+ const payload = JSON.parse(json);
70
+ if (payload.t === "date") return new Date(payload.v);
71
+ return payload.v;
72
+ } catch {
73
+ throw new Error("Invalid cursor format");
74
+ }
75
+ }
76
+
77
+ //#endregion
78
+ export { Direction, decodeCursor, encodeCursor, paginatedSearch };
79
+ //# sourceMappingURL=pagination.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.mjs","names":["value: unknown","cursor: string"],"sources":["../../src/kysely/pagination.ts"],"sourcesContent":["import type { SelectQueryBuilder } from 'kysely';\n\n/**\n * Sort direction for cursor-based pagination.\n */\nexport enum Direction {\n Asc = 'asc',\n Desc = 'desc',\n}\n\n/**\n * Result of a paginated query.\n */\nexport interface PaginationResult<TItem> {\n items: TItem[];\n pagination: {\n total: number;\n hasMore: boolean;\n cursor?: string;\n };\n}\n\n/**\n * Options for paginated search.\n */\nexport interface PaginatedSearchOptions<\n TRow,\n TMapRow extends (row: TRow) => unknown,\n> {\n /** The base Kysely query to paginate */\n query: SelectQueryBuilder<any, any, TRow>;\n /** Cursor value for pagination (value of cursorField from previous page) */\n cursor?: string;\n /** Maximum number of items per page (default: 20) */\n limit?: number;\n /** Function to transform each row to the output format */\n mapRow: TMapRow;\n /** Field to use for cursor pagination (default: 'id') */\n cursorField?: string;\n /** Sort direction for cursor field (default: Direction.Asc) */\n cursorDirection?: Direction;\n}\n\n/**\n * Generic paginated search function that handles:\n * - Total count calculation\n * - Cursor-based pagination\n * - Fetching one extra row to determine hasMore\n * - Mapping rows to output format\n *\n * @example\n * ```typescript\n * const result = await paginatedSearch({\n * query: db.selectFrom('users').selectAll(),\n * cursor: previousCursor,\n * limit: 20,\n * mapRow: (row) => ({ id: row.id, name: row.name }),\n * cursorField: 'id',\n * cursorDirection: Direction.Asc,\n * });\n * ```\n */\nexport async function paginatedSearch<\n TRow extends Record<string, unknown>,\n TMapRow extends (row: TRow) => unknown,\n>({\n query,\n cursor,\n limit = 20,\n mapRow,\n cursorField = 'id',\n cursorDirection = Direction.Asc,\n}: PaginatedSearchOptions<TRow, TMapRow>): Promise<\n PaginationResult<Awaited<ReturnType<TMapRow>>>\n> {\n // Get total count (without cursor)\n const countResult = await query\n .clearSelect()\n .clearOrderBy()\n .select((eb) => eb.fn.countAll().as('count'))\n .executeTakeFirstOrThrow();\n\n const count = countResult.count;\n\n // Apply cursor if provided\n let paginatedQuery = query;\n if (cursor) {\n const operator = cursorDirection === Direction.Asc ? '>' : '<';\n paginatedQuery = paginatedQuery.where(\n cursorField as any,\n operator,\n cursor,\n ) as typeof query;\n }\n\n // Fetch one extra to determine if there are more results\n const data = await paginatedQuery\n .orderBy(cursorField as any, cursorDirection)\n .limit(limit + 1)\n .execute();\n\n const hasMore = data.length > limit;\n const rows = hasMore ? data.slice(0, limit) : data;\n const lastRow = rows[rows.length - 1];\n const nextCursor =\n hasMore && lastRow ? String(lastRow[cursorField]) : undefined;\n\n const items = (await Promise.all(rows.map(mapRow))) as Awaited<\n ReturnType<TMapRow>\n >[];\n\n return {\n items,\n pagination: {\n total: Number(count),\n hasMore,\n cursor: nextCursor,\n },\n };\n}\n\n/**\n * Encode a cursor value for safe URL transmission.\n * Supports various types: string, number, Date, etc.\n */\nexport function encodeCursor(value: unknown): string {\n const payload = {\n v: value instanceof Date ? value.toISOString() : value,\n t: value instanceof Date ? 'date' : typeof value,\n };\n return Buffer.from(JSON.stringify(payload)).toString('base64url');\n}\n\n/**\n * Decode a cursor string back to its original value.\n */\nexport function decodeCursor(cursor: string): unknown {\n try {\n const json = Buffer.from(cursor, 'base64url').toString('utf-8');\n const payload = JSON.parse(json);\n\n if (payload.t === 'date') {\n return new Date(payload.v);\n }\n\n return payload.v;\n } catch {\n throw new Error('Invalid cursor format');\n }\n}\n"],"mappings":";;;;AAKA,IAAY,kDAAL;AACL;AACA;;AACD;;;;;;;;;;;;;;;;;;;;AAsDD,eAAsB,gBAGpB,EACA,OACA,QACA,QAAQ,IACR,QACA,cAAc,MACd,kBAAkB,UAAU,KACU,EAEtC;CAEA,MAAM,cAAc,MAAM,MACvB,aAAa,CACb,cAAc,CACd,OAAO,CAAC,OAAO,GAAG,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC,CAC5C,yBAAyB;CAE5B,MAAM,QAAQ,YAAY;CAG1B,IAAI,iBAAiB;AACrB,KAAI,QAAQ;EACV,MAAM,WAAW,oBAAoB,UAAU,MAAM,MAAM;AAC3D,mBAAiB,eAAe,MAC9B,aACA,UACA,OACD;CACF;CAGD,MAAM,OAAO,MAAM,eAChB,QAAQ,aAAoB,gBAAgB,CAC5C,MAAM,QAAQ,EAAE,CAChB,SAAS;CAEZ,MAAM,UAAU,KAAK,SAAS;CAC9B,MAAM,OAAO,UAAU,KAAK,MAAM,GAAG,MAAM,GAAG;CAC9C,MAAM,UAAU,KAAK,KAAK,SAAS;CACnC,MAAM,aACJ,WAAW,UAAU,OAAO,QAAQ,aAAa;CAEnD,MAAM,QAAS,MAAM,QAAQ,IAAI,KAAK,IAAI,OAAO,CAAC;AAIlD,QAAO;EACL;EACA,YAAY;GACV,OAAO,OAAO,MAAM;GACpB;GACA,QAAQ;EACT;CACF;AACF;;;;;AAMD,SAAgB,aAAaA,OAAwB;CACnD,MAAM,UAAU;EACd,GAAG,iBAAiB,OAAO,MAAM,aAAa,GAAG;EACjD,GAAG,iBAAiB,OAAO,gBAAgB;CAC5C;AACD,QAAO,OAAO,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,SAAS,YAAY;AAClE;;;;AAKD,SAAgB,aAAaC,QAAyB;AACpD,KAAI;EACF,MAAM,OAAO,OAAO,KAAK,QAAQ,YAAY,CAAC,SAAS,QAAQ;EAC/D,MAAM,UAAU,KAAK,MAAM,KAAK;AAEhC,MAAI,QAAQ,MAAM,OAChB,QAAO,IAAI,KAAK,QAAQ;AAG1B,SAAO,QAAQ;CAChB,QAAO;AACN,QAAM,IAAI,MAAM;CACjB;AACF"}
@@ -0,0 +1,11 @@
1
+ import { ControlledTransaction, IsolationLevel, Kysely, Transaction } from "kysely";
2
+
3
+ //#region src/kysely.d.ts
4
+ interface TransactionSettings {
5
+ isolationLevel?: IsolationLevel;
6
+ }
7
+ declare function withTransaction<DB, T>(db: DatabaseConnection<DB>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
8
+ type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
9
+ //#endregion
10
+ export { DatabaseConnection, TransactionSettings, withTransaction };
11
+ //# sourceMappingURL=kysely-0FOi6ZdO.d.cts.map
@@ -0,0 +1,17 @@
1
+
2
+ //#region src/kysely.ts
3
+ function withTransaction(db, cb, settings) {
4
+ if (db.isTransaction) return cb(db);
5
+ const builder = db.transaction();
6
+ if (settings?.isolationLevel) return builder.setIsolationLevel(settings.isolationLevel).execute(cb);
7
+ return builder.execute(cb);
8
+ }
9
+
10
+ //#endregion
11
+ Object.defineProperty(exports, 'withTransaction', {
12
+ enumerable: true,
13
+ get: function () {
14
+ return withTransaction;
15
+ }
16
+ });
17
+ //# sourceMappingURL=kysely-8WPSKCZG.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-8WPSKCZG.cjs","names":["db: DatabaseConnection<DB>","cb: (trx: Transaction<DB>) => Promise<T>","settings?: TransactionSettings"],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n ControlledTransaction,\n IsolationLevel,\n Kysely,\n Transaction,\n} from 'kysely';\n\nexport interface TransactionSettings {\n isolationLevel?: IsolationLevel;\n}\n\nexport function withTransaction<DB, T>(\n db: DatabaseConnection<DB>,\n cb: (trx: Transaction<DB>) => Promise<T>,\n settings?: TransactionSettings,\n): Promise<T> {\n if (db.isTransaction) {\n return cb(db as Transaction<DB>);\n }\n\n const builder = db.transaction();\n\n if (settings?.isolationLevel) {\n return builder.setIsolationLevel(settings.isolationLevel).execute(cb);\n }\n\n return builder.execute(cb);\n}\n\nexport type DatabaseConnection<T> =\n | ControlledTransaction<T>\n | Kysely<T>\n | Transaction<T>;\n"],"mappings":";;AAWA,SAAgB,gBACdA,IACAC,IACAC,UACY;AACZ,KAAI,GAAG,cACL,QAAO,GAAG,GAAsB;CAGlC,MAAM,UAAU,GAAG,aAAa;AAEhC,KAAI,UAAU,eACZ,QAAO,QAAQ,kBAAkB,SAAS,eAAe,CAAC,QAAQ,GAAG;AAGvE,QAAO,QAAQ,QAAQ,GAAG;AAC3B"}
@@ -0,0 +1,11 @@
1
+ import { ControlledTransaction, IsolationLevel, Kysely, Transaction } from "kysely";
2
+
3
+ //#region src/kysely.d.ts
4
+ interface TransactionSettings {
5
+ isolationLevel?: IsolationLevel;
6
+ }
7
+ declare function withTransaction<DB, T>(db: DatabaseConnection<DB>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
8
+ type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
9
+ //#endregion
10
+ export { DatabaseConnection, TransactionSettings, withTransaction };
11
+ //# sourceMappingURL=kysely-Di1LVvL2.d.mts.map
@@ -0,0 +1,11 @@
1
+ //#region src/kysely.ts
2
+ function withTransaction(db, cb, settings) {
3
+ if (db.isTransaction) return cb(db);
4
+ const builder = db.transaction();
5
+ if (settings?.isolationLevel) return builder.setIsolationLevel(settings.isolationLevel).execute(cb);
6
+ return builder.execute(cb);
7
+ }
8
+
9
+ //#endregion
10
+ export { withTransaction };
11
+ //# sourceMappingURL=kysely-DmfA94RY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-DmfA94RY.mjs","names":["db: DatabaseConnection<DB>","cb: (trx: Transaction<DB>) => Promise<T>","settings?: TransactionSettings"],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n ControlledTransaction,\n IsolationLevel,\n Kysely,\n Transaction,\n} from 'kysely';\n\nexport interface TransactionSettings {\n isolationLevel?: IsolationLevel;\n}\n\nexport function withTransaction<DB, T>(\n db: DatabaseConnection<DB>,\n cb: (trx: Transaction<DB>) => Promise<T>,\n settings?: TransactionSettings,\n): Promise<T> {\n if (db.isTransaction) {\n return cb(db as Transaction<DB>);\n }\n\n const builder = db.transaction();\n\n if (settings?.isolationLevel) {\n return builder.setIsolationLevel(settings.isolationLevel).execute(cb);\n }\n\n return builder.execute(cb);\n}\n\nexport type DatabaseConnection<T> =\n | ControlledTransaction<T>\n | Kysely<T>\n | Transaction<T>;\n"],"mappings":";AAWA,SAAgB,gBACdA,IACAC,IACAC,UACY;AACZ,KAAI,GAAG,cACL,QAAO,GAAG,GAAsB;CAGlC,MAAM,UAAU,GAAG,aAAa;AAEhC,KAAI,UAAU,eACZ,QAAO,QAAQ,kBAAkB,SAAS,eAAe,CAAC,QAAQ,GAAG;AAGvE,QAAO,QAAQ,QAAQ,GAAG;AAC3B"}
package/dist/kysely.cjs CHANGED
@@ -1,9 +1,3 @@
1
+ const require_kysely = require('./kysely-8WPSKCZG.cjs');
1
2
 
2
- //#region src/kysely.ts
3
- function withTransaction(db, cb) {
4
- if (db.isTransaction) return cb(db);
5
- return db.transaction().execute(cb);
6
- }
7
-
8
- //#endregion
9
- exports.withTransaction = withTransaction;
3
+ exports.withTransaction = require_kysely.withTransaction;
package/dist/kysely.d.cts CHANGED
@@ -1,7 +1,2 @@
1
- import { ControlledTransaction, Kysely, Transaction } from "kysely";
2
-
3
- //#region src/kysely.d.ts
4
- declare function withTransaction<DB, T>(db: DatabaseConnection<DB>, cb: (trx: Transaction<DB>) => Promise<T>): Promise<T>;
5
- type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
6
- //#endregion
7
- export { DatabaseConnection, withTransaction };
1
+ import { DatabaseConnection, TransactionSettings, withTransaction } from "./kysely-0FOi6ZdO.cjs";
2
+ export { DatabaseConnection, TransactionSettings, withTransaction };
package/dist/kysely.d.mts CHANGED
@@ -1,7 +1,2 @@
1
- import { ControlledTransaction, Kysely, Transaction } from "kysely";
2
-
3
- //#region src/kysely.d.ts
4
- declare function withTransaction<DB, T>(db: DatabaseConnection<DB>, cb: (trx: Transaction<DB>) => Promise<T>): Promise<T>;
5
- type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
6
- //#endregion
7
- export { DatabaseConnection, withTransaction };
1
+ import { DatabaseConnection, TransactionSettings, withTransaction } from "./kysely-Di1LVvL2.mjs";
2
+ export { DatabaseConnection, TransactionSettings, withTransaction };
package/dist/kysely.mjs CHANGED
@@ -1,8 +1,3 @@
1
- //#region src/kysely.ts
2
- function withTransaction(db, cb) {
3
- if (db.isTransaction) return cb(db);
4
- return db.transaction().execute(cb);
5
- }
1
+ import { withTransaction } from "./kysely-DmfA94RY.mjs";
6
2
 
7
- //#endregion
8
3
  export { withTransaction };
package/dist/rls.cjs ADDED
@@ -0,0 +1,72 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const require_kysely = require('./kysely-8WPSKCZG.cjs');
25
+ const kysely = __toESM(require("kysely"));
26
+
27
+ //#region src/rls.ts
28
+ /**
29
+ * Execute a callback within a transaction with RLS context variables set.
30
+ *
31
+ * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the
32
+ * current transaction. Variables are automatically cleared when the transaction
33
+ * ends (commit or rollback).
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * await withRlsContext(
38
+ * db,
39
+ * { user_id: session.userId, tenant_id: session.tenantId },
40
+ * async (trx) => {
41
+ * // RLS policies can now use current_setting('app.user_id')
42
+ * return trx.selectFrom('orders').selectAll().execute();
43
+ * }
44
+ * );
45
+ * ```
46
+ *
47
+ * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)
48
+ * @param context - Key-value pairs to set as session variables
49
+ * @param callback - Function to execute within the RLS context
50
+ * @param options - Optional prefix and transaction settings
51
+ */
52
+ async function withRlsContext(db, context, callback, options) {
53
+ const prefix = options?.prefix ?? "app";
54
+ return require_kysely.withTransaction(db, async (trx) => {
55
+ for (const [key, value] of Object.entries(context)) {
56
+ if (value === null || value === void 0) continue;
57
+ const settingName = `${prefix}.${key}`;
58
+ const settingValue = String(value);
59
+ await kysely.sql`SELECT set_config(${settingName}, ${settingValue}, true)`.execute(trx);
60
+ }
61
+ return callback(trx);
62
+ }, options?.settings);
63
+ }
64
+ /**
65
+ * Bypass marker symbol for explicitly skipping RLS context.
66
+ */
67
+ const RLS_BYPASS = Symbol.for("geekmidas.rls.bypass");
68
+
69
+ //#endregion
70
+ exports.RLS_BYPASS = RLS_BYPASS;
71
+ exports.withRlsContext = withRlsContext;
72
+ //# sourceMappingURL=rls.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls.cjs","names":["db: DatabaseConnection<DB>","context: RlsContext","callback: (trx: Transaction<DB>) => Promise<T>","options?: WithRlsContextOptions"],"sources":["../src/rls.ts"],"sourcesContent":["import type { Transaction } from 'kysely';\nimport { sql } from 'kysely';\nimport {\n type DatabaseConnection,\n type TransactionSettings,\n withTransaction,\n} from './kysely';\n\n/**\n * RLS context - key-value pairs to set as PostgreSQL session variables.\n * Keys become `prefix.key` (e.g., `app.user_id`).\n */\nexport interface RlsContext {\n [key: string]: string | number | boolean | null | undefined;\n}\n\n/**\n * Options for withRlsContext function.\n */\nexport interface WithRlsContextOptions {\n /** Prefix for PostgreSQL session variables (default: 'app') */\n prefix?: string;\n /** Transaction settings (isolation level) */\n settings?: TransactionSettings;\n}\n\n/**\n * Execute a callback within a transaction with RLS context variables set.\n *\n * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the\n * current transaction. Variables are automatically cleared when the transaction\n * ends (commit or rollback).\n *\n * @example\n * ```ts\n * await withRlsContext(\n * db,\n * { user_id: session.userId, tenant_id: session.tenantId },\n * async (trx) => {\n * // RLS policies can now use current_setting('app.user_id')\n * return trx.selectFrom('orders').selectAll().execute();\n * }\n * );\n * ```\n *\n * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)\n * @param context - Key-value pairs to set as session variables\n * @param callback - Function to execute within the RLS context\n * @param options - Optional prefix and transaction settings\n */\nexport async function withRlsContext<DB, T>(\n db: DatabaseConnection<DB>,\n context: RlsContext,\n callback: (trx: Transaction<DB>) => Promise<T>,\n options?: WithRlsContextOptions,\n): Promise<T> {\n const prefix = options?.prefix ?? 'app';\n\n return withTransaction(\n db,\n async (trx) => {\n // Set each context variable using SET LOCAL (scoped to transaction)\n for (const [key, value] of Object.entries(context)) {\n if (value === null || value === undefined) continue;\n\n const settingName = `${prefix}.${key}`;\n const settingValue = String(value);\n\n // Use raw SQL for SET LOCAL with proper escaping\n // The setting name is an identifier, value is a string literal\n await sql`SELECT set_config(${settingName}, ${settingValue}, true)`.execute(\n trx,\n );\n }\n\n return callback(trx);\n },\n options?.settings,\n );\n}\n\n/**\n * Bypass marker symbol for explicitly skipping RLS context.\n */\nexport const RLS_BYPASS = Symbol.for('geekmidas.rls.bypass');\n\n/**\n * Type for RLS bypass marker.\n */\nexport type RlsBypass = typeof RLS_BYPASS;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eACpBA,IACAC,SACAC,UACAC,SACY;CACZ,MAAM,SAAS,SAAS,UAAU;AAElC,QAAO,+BACL,IACA,OAAO,QAAQ;AAEb,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,EAAE;AAClD,OAAI,UAAU,QAAQ,iBAAqB;GAE3C,MAAM,eAAe,EAAE,OAAO,GAAG,IAAI;GACrC,MAAM,eAAe,OAAO,MAAM;AAIlC,SAAM,WAAI,oBAAoB,YAAY,IAAI,aAAa,SAAS,QAClE,IACD;EACF;AAED,SAAO,SAAS,IAAI;CACrB,GACD,SAAS,SACV;AACF;;;;AAKD,MAAa,aAAa,OAAO,IAAI,uBAAuB"}
package/dist/rls.d.cts ADDED
@@ -0,0 +1,57 @@
1
+ import { DatabaseConnection, TransactionSettings } from "./kysely-0FOi6ZdO.cjs";
2
+ import { Transaction } from "kysely";
3
+
4
+ //#region src/rls.d.ts
5
+
6
+ /**
7
+ * RLS context - key-value pairs to set as PostgreSQL session variables.
8
+ * Keys become `prefix.key` (e.g., `app.user_id`).
9
+ */
10
+ interface RlsContext {
11
+ [key: string]: string | number | boolean | null | undefined;
12
+ }
13
+ /**
14
+ * Options for withRlsContext function.
15
+ */
16
+ interface WithRlsContextOptions {
17
+ /** Prefix for PostgreSQL session variables (default: 'app') */
18
+ prefix?: string;
19
+ /** Transaction settings (isolation level) */
20
+ settings?: TransactionSettings;
21
+ }
22
+ /**
23
+ * Execute a callback within a transaction with RLS context variables set.
24
+ *
25
+ * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the
26
+ * current transaction. Variables are automatically cleared when the transaction
27
+ * ends (commit or rollback).
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * await withRlsContext(
32
+ * db,
33
+ * { user_id: session.userId, tenant_id: session.tenantId },
34
+ * async (trx) => {
35
+ * // RLS policies can now use current_setting('app.user_id')
36
+ * return trx.selectFrom('orders').selectAll().execute();
37
+ * }
38
+ * );
39
+ * ```
40
+ *
41
+ * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)
42
+ * @param context - Key-value pairs to set as session variables
43
+ * @param callback - Function to execute within the RLS context
44
+ * @param options - Optional prefix and transaction settings
45
+ */
46
+ declare function withRlsContext<DB, T>(db: DatabaseConnection<DB>, context: RlsContext, callback: (trx: Transaction<DB>) => Promise<T>, options?: WithRlsContextOptions): Promise<T>;
47
+ /**
48
+ * Bypass marker symbol for explicitly skipping RLS context.
49
+ */
50
+ declare const RLS_BYPASS: unique symbol;
51
+ /**
52
+ * Type for RLS bypass marker.
53
+ */
54
+ type RlsBypass = typeof RLS_BYPASS;
55
+ //#endregion
56
+ export { RLS_BYPASS, RlsBypass, RlsContext, WithRlsContextOptions, withRlsContext };
57
+ //# sourceMappingURL=rls.d.cts.map
package/dist/rls.d.mts ADDED
@@ -0,0 +1,57 @@
1
+ import { DatabaseConnection, TransactionSettings } from "./kysely-Di1LVvL2.mjs";
2
+ import { Transaction } from "kysely";
3
+
4
+ //#region src/rls.d.ts
5
+
6
+ /**
7
+ * RLS context - key-value pairs to set as PostgreSQL session variables.
8
+ * Keys become `prefix.key` (e.g., `app.user_id`).
9
+ */
10
+ interface RlsContext {
11
+ [key: string]: string | number | boolean | null | undefined;
12
+ }
13
+ /**
14
+ * Options for withRlsContext function.
15
+ */
16
+ interface WithRlsContextOptions {
17
+ /** Prefix for PostgreSQL session variables (default: 'app') */
18
+ prefix?: string;
19
+ /** Transaction settings (isolation level) */
20
+ settings?: TransactionSettings;
21
+ }
22
+ /**
23
+ * Execute a callback within a transaction with RLS context variables set.
24
+ *
25
+ * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the
26
+ * current transaction. Variables are automatically cleared when the transaction
27
+ * ends (commit or rollback).
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * await withRlsContext(
32
+ * db,
33
+ * { user_id: session.userId, tenant_id: session.tenantId },
34
+ * async (trx) => {
35
+ * // RLS policies can now use current_setting('app.user_id')
36
+ * return trx.selectFrom('orders').selectAll().execute();
37
+ * }
38
+ * );
39
+ * ```
40
+ *
41
+ * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)
42
+ * @param context - Key-value pairs to set as session variables
43
+ * @param callback - Function to execute within the RLS context
44
+ * @param options - Optional prefix and transaction settings
45
+ */
46
+ declare function withRlsContext<DB, T>(db: DatabaseConnection<DB>, context: RlsContext, callback: (trx: Transaction<DB>) => Promise<T>, options?: WithRlsContextOptions): Promise<T>;
47
+ /**
48
+ * Bypass marker symbol for explicitly skipping RLS context.
49
+ */
50
+ declare const RLS_BYPASS: unique symbol;
51
+ /**
52
+ * Type for RLS bypass marker.
53
+ */
54
+ type RlsBypass = typeof RLS_BYPASS;
55
+ //#endregion
56
+ export { RLS_BYPASS, RlsBypass, RlsContext, WithRlsContextOptions, withRlsContext };
57
+ //# sourceMappingURL=rls.d.mts.map
package/dist/rls.mjs ADDED
@@ -0,0 +1,48 @@
1
+ import { withTransaction } from "./kysely-DmfA94RY.mjs";
2
+ import { sql } from "kysely";
3
+
4
+ //#region src/rls.ts
5
+ /**
6
+ * Execute a callback within a transaction with RLS context variables set.
7
+ *
8
+ * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the
9
+ * current transaction. Variables are automatically cleared when the transaction
10
+ * ends (commit or rollback).
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * await withRlsContext(
15
+ * db,
16
+ * { user_id: session.userId, tenant_id: session.tenantId },
17
+ * async (trx) => {
18
+ * // RLS policies can now use current_setting('app.user_id')
19
+ * return trx.selectFrom('orders').selectAll().execute();
20
+ * }
21
+ * );
22
+ * ```
23
+ *
24
+ * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)
25
+ * @param context - Key-value pairs to set as session variables
26
+ * @param callback - Function to execute within the RLS context
27
+ * @param options - Optional prefix and transaction settings
28
+ */
29
+ async function withRlsContext(db, context, callback, options) {
30
+ const prefix = options?.prefix ?? "app";
31
+ return withTransaction(db, async (trx) => {
32
+ for (const [key, value] of Object.entries(context)) {
33
+ if (value === null || value === void 0) continue;
34
+ const settingName = `${prefix}.${key}`;
35
+ const settingValue = String(value);
36
+ await sql`SELECT set_config(${settingName}, ${settingValue}, true)`.execute(trx);
37
+ }
38
+ return callback(trx);
39
+ }, options?.settings);
40
+ }
41
+ /**
42
+ * Bypass marker symbol for explicitly skipping RLS context.
43
+ */
44
+ const RLS_BYPASS = Symbol.for("geekmidas.rls.bypass");
45
+
46
+ //#endregion
47
+ export { RLS_BYPASS, withRlsContext };
48
+ //# sourceMappingURL=rls.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls.mjs","names":["db: DatabaseConnection<DB>","context: RlsContext","callback: (trx: Transaction<DB>) => Promise<T>","options?: WithRlsContextOptions"],"sources":["../src/rls.ts"],"sourcesContent":["import type { Transaction } from 'kysely';\nimport { sql } from 'kysely';\nimport {\n type DatabaseConnection,\n type TransactionSettings,\n withTransaction,\n} from './kysely';\n\n/**\n * RLS context - key-value pairs to set as PostgreSQL session variables.\n * Keys become `prefix.key` (e.g., `app.user_id`).\n */\nexport interface RlsContext {\n [key: string]: string | number | boolean | null | undefined;\n}\n\n/**\n * Options for withRlsContext function.\n */\nexport interface WithRlsContextOptions {\n /** Prefix for PostgreSQL session variables (default: 'app') */\n prefix?: string;\n /** Transaction settings (isolation level) */\n settings?: TransactionSettings;\n}\n\n/**\n * Execute a callback within a transaction with RLS context variables set.\n *\n * Sets PostgreSQL session variables using `SET LOCAL` which scopes them to the\n * current transaction. Variables are automatically cleared when the transaction\n * ends (commit or rollback).\n *\n * @example\n * ```ts\n * await withRlsContext(\n * db,\n * { user_id: session.userId, tenant_id: session.tenantId },\n * async (trx) => {\n * // RLS policies can now use current_setting('app.user_id')\n * return trx.selectFrom('orders').selectAll().execute();\n * }\n * );\n * ```\n *\n * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)\n * @param context - Key-value pairs to set as session variables\n * @param callback - Function to execute within the RLS context\n * @param options - Optional prefix and transaction settings\n */\nexport async function withRlsContext<DB, T>(\n db: DatabaseConnection<DB>,\n context: RlsContext,\n callback: (trx: Transaction<DB>) => Promise<T>,\n options?: WithRlsContextOptions,\n): Promise<T> {\n const prefix = options?.prefix ?? 'app';\n\n return withTransaction(\n db,\n async (trx) => {\n // Set each context variable using SET LOCAL (scoped to transaction)\n for (const [key, value] of Object.entries(context)) {\n if (value === null || value === undefined) continue;\n\n const settingName = `${prefix}.${key}`;\n const settingValue = String(value);\n\n // Use raw SQL for SET LOCAL with proper escaping\n // The setting name is an identifier, value is a string literal\n await sql`SELECT set_config(${settingName}, ${settingValue}, true)`.execute(\n trx,\n );\n }\n\n return callback(trx);\n },\n options?.settings,\n );\n}\n\n/**\n * Bypass marker symbol for explicitly skipping RLS context.\n */\nexport const RLS_BYPASS = Symbol.for('geekmidas.rls.bypass');\n\n/**\n * Type for RLS bypass marker.\n */\nexport type RlsBypass = typeof RLS_BYPASS;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eACpBA,IACAC,SACAC,UACAC,SACY;CACZ,MAAM,SAAS,SAAS,UAAU;AAElC,QAAO,gBACL,IACA,OAAO,QAAQ;AAEb,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,EAAE;AAClD,OAAI,UAAU,QAAQ,iBAAqB;GAE3C,MAAM,eAAe,EAAE,OAAO,GAAG,IAAI;GACrC,MAAM,eAAe,OAAO,MAAM;AAIlC,SAAM,IAAI,oBAAoB,YAAY,IAAI,aAAa,SAAS,QAClE,IACD;EACF;AAED,SAAO,SAAS,IAAI;CACrB,GACD,SAAS,SACV;AACF;;;;AAKD,MAAa,aAAa,OAAO,IAAI,uBAAuB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/db",
3
- "version": "0.0.4",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -8,6 +8,16 @@
8
8
  "types": "./dist/kysely.d.ts",
9
9
  "import": "./dist/kysely.mjs",
10
10
  "require": "./dist/kysely.cjs"
11
+ },
12
+ "./kysely/pagination": {
13
+ "types": "./dist/kysely/pagination.d.ts",
14
+ "import": "./dist/kysely/pagination.mjs",
15
+ "require": "./dist/kysely/pagination.cjs"
16
+ },
17
+ "./rls": {
18
+ "types": "./dist/rls.d.ts",
19
+ "import": "./dist/rls.mjs",
20
+ "require": "./dist/rls.cjs"
11
21
  }
12
22
  },
13
23
  "dependencies": {
@@ -16,6 +26,10 @@
16
26
  "devDependencies": {
17
27
  "@types/pg": "~8.15.4"
18
28
  },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/geekmidas/toolbox"
32
+ },
19
33
  "publishConfig": {
20
34
  "registry": "https://registry.npmjs.org/",
21
35
  "access": "public"
@@ -27,5 +41,8 @@
27
41
  "objection": "~3.1.5",
28
42
  "db-errors": "~0.2.3",
29
43
  "vitest": "~3.2.4"
44
+ },
45
+ "scripts": {
46
+ "ts": "tsc --noEmit --skipLibCheck src/**/*.ts"
30
47
  }
31
48
  }