@usebetterdev/audit-prisma 0.4.0-beta.4 → 0.5.0-beta.1
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/LICENSE +21 -0
- package/dist/index.cjs +40 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -6
- package/dist/index.d.ts +22 -6
- package/dist/index.js +39 -8
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 usebetter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
prismaAuditAdapter: () => prismaAuditAdapter,
|
|
24
|
+
prismaModelMap: () => prismaModelMap,
|
|
24
25
|
withAuditExtension: () => withAuditExtension
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -93,12 +94,44 @@ function getAuditOperation(action) {
|
|
|
93
94
|
return void 0;
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
// src/model-map.ts
|
|
98
|
+
function hasPrismaRuntimeDataModel(value) {
|
|
99
|
+
if (!("_runtimeDataModel" in value)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const { _runtimeDataModel: rdm } = value;
|
|
103
|
+
if (rdm === null || typeof rdm !== "object") {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return "models" in rdm;
|
|
107
|
+
}
|
|
108
|
+
function prismaModelMap(prisma) {
|
|
109
|
+
if (!hasPrismaRuntimeDataModel(prisma)) {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
const result = {};
|
|
113
|
+
for (const [modelName, meta] of Object.entries(prisma._runtimeDataModel.models)) {
|
|
114
|
+
result[modelName] = meta.dbName ?? modelName;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function buildTableNameResolver(prisma, transform) {
|
|
119
|
+
if (transform !== void 0) {
|
|
120
|
+
return transform;
|
|
121
|
+
}
|
|
122
|
+
const map = prismaModelMap(prisma);
|
|
123
|
+
if (map !== void 0) {
|
|
124
|
+
return (modelName) => map[modelName] ?? modelName;
|
|
125
|
+
}
|
|
126
|
+
return (modelName) => modelName;
|
|
127
|
+
}
|
|
128
|
+
|
|
96
129
|
// src/extension.ts
|
|
97
130
|
function withAuditExtension(prisma, captureLog, options = {}) {
|
|
98
131
|
const bulkMode = options.bulkMode ?? "per-row";
|
|
99
132
|
const handleError = buildErrorHandler(options.onError);
|
|
100
133
|
const metadata = options.metadata;
|
|
101
|
-
const
|
|
134
|
+
const resolveTableName = buildTableNameResolver(prisma, options.tableNameTransform);
|
|
102
135
|
const extended = prisma.$extends({
|
|
103
136
|
query: {
|
|
104
137
|
$allModels: {
|
|
@@ -113,17 +146,17 @@ function withAuditExtension(prisma, captureLog, options = {}) {
|
|
|
113
146
|
if (auditOp === void 0) {
|
|
114
147
|
return result;
|
|
115
148
|
}
|
|
149
|
+
const tableName = resolveTableName(model);
|
|
116
150
|
try {
|
|
117
151
|
await fireCaptureLog({
|
|
118
|
-
|
|
152
|
+
tableName,
|
|
119
153
|
operation,
|
|
120
154
|
auditOp,
|
|
121
155
|
args,
|
|
122
156
|
result,
|
|
123
157
|
captureLog,
|
|
124
158
|
bulkMode,
|
|
125
|
-
metadata
|
|
126
|
-
tableNameTransform
|
|
159
|
+
metadata
|
|
127
160
|
});
|
|
128
161
|
} catch (err) {
|
|
129
162
|
handleError(err);
|
|
@@ -136,17 +169,15 @@ function withAuditExtension(prisma, captureLog, options = {}) {
|
|
|
136
169
|
return extended;
|
|
137
170
|
}
|
|
138
171
|
async function fireCaptureLog({
|
|
139
|
-
|
|
172
|
+
tableName,
|
|
140
173
|
operation,
|
|
141
174
|
auditOp,
|
|
142
175
|
args,
|
|
143
176
|
result,
|
|
144
177
|
captureLog,
|
|
145
178
|
bulkMode,
|
|
146
|
-
metadata
|
|
147
|
-
tableNameTransform
|
|
179
|
+
metadata
|
|
148
180
|
}) {
|
|
149
|
-
const tableName = tableNameTransform !== void 0 ? tableNameTransform(model) : model;
|
|
150
181
|
const actorId = (0, import_audit_core.getAuditContext)()?.actorId;
|
|
151
182
|
if (operation === "createMany") {
|
|
152
183
|
await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });
|
|
@@ -327,6 +358,7 @@ function getArgsData(args) {
|
|
|
327
358
|
// Annotate the CommonJS export names for ESM import in node:
|
|
328
359
|
0 && (module.exports = {
|
|
329
360
|
prismaAuditAdapter,
|
|
361
|
+
prismaModelMap,
|
|
330
362
|
withAuditExtension
|
|
331
363
|
});
|
|
332
364
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/extension.ts","../src/action-map.ts"],"sourcesContent":["export { prismaAuditAdapter } from \"./adapter.js\";\nexport type { PrismaClientWithRaw } from \"./adapter.js\";\nexport { withAuditExtension } from \"./extension.js\";\nexport type {\n PrismaClientLike,\n WithAuditExtensionOptions,\n BulkMode,\n} from \"./extension.js\";\n","import type { AuditDatabaseAdapter, AuditLog } from \"@usebetterdev/audit-core\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports raw query execution.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientWithRaw {\n $executeRawUnsafe(query: string, ...params: unknown[]): Promise<number>;\n}\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Prisma client.\n *\n * Implements `writeLog` via `$executeRawUnsafe` for precise control over\n * PostgreSQL type casts (::uuid, ::timestamptz, ::jsonb) and null handling.\n *\n * Only `writeLog` is implemented — querying audit logs via Prisma is out of scope.\n * Use `drizzleAuditAdapter` if you also need `queryLogs`.\n */\nexport function prismaAuditAdapter(db: PrismaClientWithRaw): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n await db.$executeRawUnsafe(\n `INSERT INTO audit_logs (\n id, timestamp, table_name, operation, record_id,\n actor_id, before_data, after_data, diff,\n label, description, severity, compliance,\n notify, reason, metadata, redacted_fields\n ) VALUES (\n $1::uuid, $2::timestamptz, $3, $4, $5,\n $6, $7::jsonb, $8::jsonb, $9::jsonb,\n $10, $11, $12, $13::jsonb,\n $14, $15, $16::jsonb, $17::jsonb\n )`,\n log.id,\n log.timestamp.toISOString(),\n log.tableName,\n log.operation,\n log.recordId,\n log.actorId ?? null,\n jsonOrNull(log.beforeData),\n jsonOrNull(log.afterData),\n jsonOrNull(log.diff),\n log.label ?? null,\n log.description ?? null,\n log.severity ?? null,\n jsonOrNull(log.compliance),\n log.notify ?? null,\n log.reason ?? null,\n jsonOrNull(log.metadata),\n jsonOrNull(log.redactedFields),\n );\n },\n };\n}\n\nfunction jsonOrNull(value: unknown): string | null {\n if (value === undefined || value === null) {\n return null;\n }\n return JSON.stringify(value);\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getAuditContext } from \"@usebetterdev/audit-core\";\nimport { getAuditOperation } from \"./action-map.js\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports `$extends`.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientLike {\n $extends(extension: PrismaExtensionDefinition): unknown;\n}\n\n/**\n * Shape of the Prisma `$extends` query extension definition.\n * We only type what we need — the full shape lives in `@prisma/client/extension`.\n */\ninterface PrismaExtensionDefinition {\n query: {\n $allModels: {\n $allOperations: (params: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) => Promise<unknown>;\n };\n };\n}\n\nexport type BulkMode = \"per-row\" | \"bulk\";\n\nexport interface WithAuditExtensionOptions {\n /**\n * How to handle bulk write operations (`createMany`, `updateMany`, `deleteMany`).\n *\n * - `\"per-row\"` (default): for `createMany` and `createManyAndReturn`, fires\n * one audit entry per item in `args.data`. For `updateMany` and `deleteMany`,\n * individual record IDs are unavailable so one entry with `recordId: \"unknown\"`\n * is fired regardless.\n * - `\"bulk\"`: fires a single audit entry for the entire operation.\n */\n bulkMode?: BulkMode;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n * Errors are always swallowed — audit failures never propagate to callers.\n */\n onError?: (error: unknown) => void;\n /**\n * Extra structured data merged into every audit log entry produced by this\n * extension. Useful for tagging logs with the adapter name or environment.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, audit.captureLog, { metadata: { adapter: \"prisma\" } })\n * ```\n */\n metadata?: Record<string, unknown>;\n /**\n * Maps a Prisma model name to the `tableName` stored in audit logs.\n * Defaults to the model name as-is (PascalCase, e.g. `\"Project\"`).\n *\n * Use `(name) => name.toLowerCase()` to normalise to SQL table naming so\n * Prisma audit entries align with Drizzle and other SQL-based adapters.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, captureLog, {\n * tableNameTransform: (name) => name.toLowerCase(), // \"Project\" → \"projects\"\n * })\n * ```\n */\n tableNameTransform?: (modelName: string) => string;\n}\n\n/**\n * Wraps a Prisma client with a `$extends` query extension that calls\n * `captureLog` after each successful mutation.\n *\n * Returns a new extended client of the same type `T`. Use this extended\n * client in place of the original — all queries behave identically.\n *\n * Actor identity is read automatically from the current `AuditContext`\n * (set via `audit.withContext` or `runWithAuditContext` from audit-core).\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`.\n *\n * @example\n * ```ts\n * const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: [\"User\"] });\n * const auditedPrisma = withAuditExtension(prisma, audit.captureLog);\n * // Use auditedPrisma everywhere — mutations are transparently logged\n * ```\n */\nexport function withAuditExtension<T extends PrismaClientLike>(\n prisma: T,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options: WithAuditExtensionOptions = {},\n): T {\n const bulkMode = options.bulkMode ?? \"per-row\";\n const handleError = buildErrorHandler(options.onError);\n const metadata = options.metadata;\n const tableNameTransform = options.tableNameTransform;\n\n const extended = prisma.$extends({\n query: {\n $allModels: {\n async $allOperations({\n model,\n operation,\n args,\n query,\n }: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) {\n const result = await query(args);\n\n const auditOp = getAuditOperation(operation);\n if (auditOp === undefined) {\n return result;\n }\n\n try {\n await fireCaptureLog({\n model,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n tableNameTransform,\n });\n } catch (err) {\n handleError(err);\n }\n\n return result;\n },\n },\n },\n });\n\n // The $extends return type is `unknown` — casting back to T is safe because\n // the extension only adds transparent interception; it does not change the query API.\n return extended as unknown as T;\n}\n\ninterface FireCaptureLogParams {\n model: string;\n operation: string;\n auditOp: AuditOperation;\n args: unknown;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n tableNameTransform: ((modelName: string) => string) | undefined;\n}\n\nasync function fireCaptureLog({\n model,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n tableNameTransform,\n}: FireCaptureLogParams): Promise<void> {\n const tableName = tableNameTransform !== undefined ? tableNameTransform(model) : model;\n const actorId = getAuditContext()?.actorId;\n\n if (operation === \"createMany\") {\n await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"createManyAndReturn\") {\n await handleCreateManyAndReturn({ tableName, auditOp, result, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"updateMany\" || operation === \"deleteMany\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // Single-record operations: create, update, upsert, delete\n const row = toRecord(result);\n const recordId = (row !== undefined ? extractId(row) : undefined) ?? \"unknown\";\n\n if (auditOp === \"INSERT\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n if (auditOp === \"UPDATE\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // DELETE: result is the deleted record\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { before: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n}\n\ninterface HandleCreateManyParams {\n tableName: string;\n auditOp: AuditOperation;\n args: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateMany({\n tableName,\n auditOp,\n args,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // per-row: fire one entry per item in args.data\n const rows = getArgsData(args);\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\ninterface HandleCreateManyAndReturnParams {\n tableName: string;\n auditOp: AuditOperation;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateManyAndReturn({\n tableName,\n auditOp,\n result,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyAndReturnParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n const rows = Array.isArray(result) ? result : [];\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\nfunction buildErrorHandler(\n onError: ((error: unknown) => void) | undefined,\n): (error: unknown) => void {\n return (error: unknown) => {\n try {\n if (onError !== undefined) {\n onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`audit-prisma: capture failed — ${msg}`);\n }\n } catch {\n // onError itself threw — swallow to never break mutations.\n }\n };\n}\n\nfunction extractId(record: Record<string, unknown>): string | undefined {\n const id = record[\"id\"];\n if (id !== undefined && id !== null) {\n return String(id);\n }\n return undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && value !== undefined && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction getArgsData(args: unknown): unknown[] {\n if (args !== null && typeof args === \"object\" && \"data\" in args) {\n const data = (args as Record<string, unknown>)[\"data\"];\n if (Array.isArray(data)) {\n return data;\n }\n }\n return [];\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\n/**\n * Maps Prisma query action names to AuditOperation values.\n * Actions not in this map are read-only queries and are not audited.\n */\nexport const ACTION_MAP = {\n create: \"INSERT\",\n createMany: \"INSERT\",\n createManyAndReturn: \"INSERT\",\n update: \"UPDATE\",\n updateMany: \"UPDATE\",\n upsert: \"UPDATE\",\n delete: \"DELETE\",\n deleteMany: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type MutableAction = keyof typeof ACTION_MAP;\n\nfunction isMutableAction(action: string): action is MutableAction {\n return action in ACTION_MAP;\n}\n\n/**\n * Returns the AuditOperation for a Prisma action name, or `undefined` for\n * read-only operations (findMany, findUnique, count, aggregate, etc.).\n */\nexport function getAuditOperation(action: string): AuditOperation | undefined {\n if (isMutableAction(action)) {\n return ACTION_MAP[action];\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/extension.ts","../src/action-map.ts","../src/model-map.ts"],"sourcesContent":["export { prismaAuditAdapter } from \"./adapter.js\";\nexport type { PrismaClientWithRaw } from \"./adapter.js\";\nexport { withAuditExtension } from \"./extension.js\";\nexport type {\n PrismaClientLike,\n WithAuditExtensionOptions,\n BulkMode,\n} from \"./extension.js\";\nexport { prismaModelMap } from \"./model-map.js\";\n","import type { AuditDatabaseAdapter, AuditLog } from \"@usebetterdev/audit-core\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports raw query execution.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientWithRaw {\n $executeRawUnsafe(query: string, ...params: unknown[]): Promise<number>;\n}\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Prisma client.\n *\n * Implements `writeLog` via `$executeRawUnsafe` for precise control over\n * PostgreSQL type casts (::uuid, ::timestamptz, ::jsonb) and null handling.\n *\n * Only `writeLog` is implemented — querying audit logs via Prisma is out of scope.\n * Use `drizzleAuditAdapter` if you also need `queryLogs`.\n */\nexport function prismaAuditAdapter(db: PrismaClientWithRaw): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n await db.$executeRawUnsafe(\n `INSERT INTO audit_logs (\n id, timestamp, table_name, operation, record_id,\n actor_id, before_data, after_data, diff,\n label, description, severity, compliance,\n notify, reason, metadata, redacted_fields\n ) VALUES (\n $1::uuid, $2::timestamptz, $3, $4, $5,\n $6, $7::jsonb, $8::jsonb, $9::jsonb,\n $10, $11, $12, $13::jsonb,\n $14, $15, $16::jsonb, $17::jsonb\n )`,\n log.id,\n log.timestamp.toISOString(),\n log.tableName,\n log.operation,\n log.recordId,\n log.actorId ?? null,\n jsonOrNull(log.beforeData),\n jsonOrNull(log.afterData),\n jsonOrNull(log.diff),\n log.label ?? null,\n log.description ?? null,\n log.severity ?? null,\n jsonOrNull(log.compliance),\n log.notify ?? null,\n log.reason ?? null,\n jsonOrNull(log.metadata),\n jsonOrNull(log.redactedFields),\n );\n },\n };\n}\n\nfunction jsonOrNull(value: unknown): string | null {\n if (value === undefined || value === null) {\n return null;\n }\n return JSON.stringify(value);\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getAuditContext } from \"@usebetterdev/audit-core\";\nimport { getAuditOperation } from \"./action-map.js\";\nimport { buildTableNameResolver } from \"./model-map.js\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports `$extends`.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientLike {\n $extends(extension: PrismaExtensionDefinition): unknown;\n}\n\n/**\n * Shape of the Prisma `$extends` query extension definition.\n * We only type what we need — the full shape lives in `@prisma/client/extension`.\n */\ninterface PrismaExtensionDefinition {\n query: {\n $allModels: {\n $allOperations: (params: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) => Promise<unknown>;\n };\n };\n}\n\nexport type BulkMode = \"per-row\" | \"bulk\";\n\nexport interface WithAuditExtensionOptions {\n /**\n * How to handle bulk write operations (`createMany`, `updateMany`, `deleteMany`).\n *\n * - `\"per-row\"` (default): for `createMany` and `createManyAndReturn`, fires\n * one audit entry per item in `args.data`. For `updateMany` and `deleteMany`,\n * individual record IDs are unavailable so one entry with `recordId: \"unknown\"`\n * is fired regardless.\n * - `\"bulk\"`: fires a single audit entry for the entire operation.\n */\n bulkMode?: BulkMode;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n * Errors are always swallowed — audit failures never propagate to callers.\n */\n onError?: (error: unknown) => void;\n /**\n * Extra structured data merged into every audit log entry produced by this\n * extension. Useful for tagging logs with the adapter name or environment.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, audit.captureLog, { metadata: { adapter: \"prisma\" } })\n * ```\n */\n metadata?: Record<string, unknown>;\n /**\n * Maps a Prisma model name to the `tableName` stored in audit logs.\n *\n * By default the extension auto-detects the SQL table name from Prisma's\n * `_runtimeDataModel` (populated by `@@map` directives). If the runtime\n * metadata is unavailable, the model name is used as-is.\n *\n * When provided, this function takes full precedence over auto-detection.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, captureLog, {\n * tableNameTransform: (name) => name.toLowerCase(),\n * })\n * ```\n */\n tableNameTransform?: (modelName: string) => string;\n}\n\n/**\n * Wraps a Prisma client with a `$extends` query extension that calls\n * `captureLog` after each successful mutation.\n *\n * Returns a new extended client of the same type `T`. Use this extended\n * client in place of the original — all queries behave identically.\n *\n * Actor identity is read automatically from the current `AuditContext`\n * (set via `audit.withContext` or `runWithAuditContext` from audit-core).\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`.\n *\n * @example\n * ```ts\n * // auditTables must use SQL table names (auto-detected from @@map directives)\n * const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: [\"users\"] });\n * const auditedPrisma = withAuditExtension(prisma, audit.captureLog);\n * // Use auditedPrisma everywhere — mutations are transparently logged\n * ```\n */\nexport function withAuditExtension<T extends PrismaClientLike>(\n prisma: T,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options: WithAuditExtensionOptions = {},\n): T {\n const bulkMode = options.bulkMode ?? \"per-row\";\n const handleError = buildErrorHandler(options.onError);\n const metadata = options.metadata;\n const resolveTableName = buildTableNameResolver(prisma, options.tableNameTransform);\n\n const extended = prisma.$extends({\n query: {\n $allModels: {\n async $allOperations({\n model,\n operation,\n args,\n query,\n }: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) {\n const result = await query(args);\n\n const auditOp = getAuditOperation(operation);\n if (auditOp === undefined) {\n return result;\n }\n\n const tableName = resolveTableName(model);\n\n try {\n await fireCaptureLog({\n tableName,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n });\n } catch (err) {\n handleError(err);\n }\n\n return result;\n },\n },\n },\n });\n\n // The $extends return type is `unknown` — casting back to T is safe because\n // the extension only adds transparent interception; it does not change the query API.\n return extended as unknown as T;\n}\n\ninterface FireCaptureLogParams {\n tableName: string;\n operation: string;\n auditOp: AuditOperation;\n args: unknown;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n}\n\nasync function fireCaptureLog({\n tableName,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n}: FireCaptureLogParams): Promise<void> {\n const actorId = getAuditContext()?.actorId;\n\n if (operation === \"createMany\") {\n await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"createManyAndReturn\") {\n await handleCreateManyAndReturn({ tableName, auditOp, result, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"updateMany\" || operation === \"deleteMany\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // Single-record operations: create, update, upsert, delete\n const row = toRecord(result);\n const recordId = (row !== undefined ? extractId(row) : undefined) ?? \"unknown\";\n\n if (auditOp === \"INSERT\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n if (auditOp === \"UPDATE\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // DELETE: result is the deleted record\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { before: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n}\n\ninterface HandleCreateManyParams {\n tableName: string;\n auditOp: AuditOperation;\n args: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateMany({\n tableName,\n auditOp,\n args,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // per-row: fire one entry per item in args.data\n const rows = getArgsData(args);\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\ninterface HandleCreateManyAndReturnParams {\n tableName: string;\n auditOp: AuditOperation;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateManyAndReturn({\n tableName,\n auditOp,\n result,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyAndReturnParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n const rows = Array.isArray(result) ? result : [];\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\nfunction buildErrorHandler(\n onError: ((error: unknown) => void) | undefined,\n): (error: unknown) => void {\n return (error: unknown) => {\n try {\n if (onError !== undefined) {\n onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`audit-prisma: capture failed — ${msg}`);\n }\n } catch {\n // onError itself threw — swallow to never break mutations.\n }\n };\n}\n\nfunction extractId(record: Record<string, unknown>): string | undefined {\n const id = record[\"id\"];\n if (id !== undefined && id !== null) {\n return String(id);\n }\n return undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && value !== undefined && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction getArgsData(args: unknown): unknown[] {\n if (args !== null && typeof args === \"object\" && \"data\" in args) {\n const data = (args as Record<string, unknown>)[\"data\"];\n if (Array.isArray(data)) {\n return data;\n }\n }\n return [];\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\n/**\n * Maps Prisma query action names to AuditOperation values.\n * Actions not in this map are read-only queries and are not audited.\n */\nexport const ACTION_MAP = {\n create: \"INSERT\",\n createMany: \"INSERT\",\n createManyAndReturn: \"INSERT\",\n update: \"UPDATE\",\n updateMany: \"UPDATE\",\n upsert: \"UPDATE\",\n delete: \"DELETE\",\n deleteMany: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type MutableAction = keyof typeof ACTION_MAP;\n\nfunction isMutableAction(action: string): action is MutableAction {\n return action in ACTION_MAP;\n}\n\n/**\n * Returns the AuditOperation for a Prisma action name, or `undefined` for\n * read-only operations (findMany, findUnique, count, aggregate, etc.).\n */\nexport function getAuditOperation(action: string): AuditOperation | undefined {\n if (isMutableAction(action)) {\n return ACTION_MAP[action];\n }\n return undefined;\n}\n","/**\n * Runtime model-to-table mapping for Prisma clients that expose\n * `_runtimeDataModel.models`. This lets the audit extension auto-detect\n * the SQL table name (from `@@map`) without user configuration.\n */\n\ninterface RuntimeModel {\n dbName: string | null;\n}\n\ninterface RuntimeDataModel {\n models: Record<string, RuntimeModel>;\n}\n\ninterface PrismaWithRuntimeDataModel {\n _runtimeDataModel: RuntimeDataModel;\n}\n\n/**\n * Type guard: checks whether `value` exposes Prisma's `_runtimeDataModel.models`.\n */\nfunction hasPrismaRuntimeDataModel(value: object): value is PrismaWithRuntimeDataModel {\n if (!(\"_runtimeDataModel\" in value)) {\n return false;\n }\n\n const { _runtimeDataModel: rdm } = value;\n if (rdm === null || typeof rdm !== \"object\") {\n return false;\n }\n\n return \"models\" in rdm;\n}\n\n/**\n * Returns a `Record<string, string>` mapping Prisma model names to their\n * SQL table names (from `@@map`). Models without `@@map` map to themselves.\n *\n * Returns `undefined` if the client does not expose `_runtimeDataModel`.\n */\nexport function prismaModelMap(prisma: object): Record<string, string> | undefined {\n if (!hasPrismaRuntimeDataModel(prisma)) {\n return undefined;\n }\n\n const result: Record<string, string> = {};\n for (const [modelName, meta] of Object.entries(prisma._runtimeDataModel.models)) {\n result[modelName] = meta.dbName ?? modelName;\n }\n return result;\n}\n\n/**\n * Builds a function that resolves a Prisma model name to a table name.\n *\n * Resolution order:\n * 1. If `transform` is provided, use it (full user control).\n * 2. Else if the client exposes `_runtimeDataModel`, use `dbName ?? modelName`.\n * 3. Else return the model name as-is (safe fallback).\n */\nexport function buildTableNameResolver(\n prisma: object,\n transform: ((modelName: string) => string) | undefined,\n): (modelName: string) => string {\n if (transform !== undefined) {\n return transform;\n }\n\n const map = prismaModelMap(prisma);\n if (map !== undefined) {\n return (modelName: string) => map[modelName] ?? modelName;\n }\n\n return (modelName: string) => modelName;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -70,15 +70,17 @@ interface WithAuditExtensionOptions {
|
|
|
70
70
|
metadata?: Record<string, unknown>;
|
|
71
71
|
/**
|
|
72
72
|
* Maps a Prisma model name to the `tableName` stored in audit logs.
|
|
73
|
-
* Defaults to the model name as-is (PascalCase, e.g. `"Project"`).
|
|
74
73
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* By default the extension auto-detects the SQL table name from Prisma's
|
|
75
|
+
* `_runtimeDataModel` (populated by `@@map` directives). If the runtime
|
|
76
|
+
* metadata is unavailable, the model name is used as-is.
|
|
77
|
+
*
|
|
78
|
+
* When provided, this function takes full precedence over auto-detection.
|
|
77
79
|
*
|
|
78
80
|
* @example
|
|
79
81
|
* ```ts
|
|
80
82
|
* withAuditExtension(prisma, captureLog, {
|
|
81
|
-
* tableNameTransform: (name) => name.toLowerCase(),
|
|
83
|
+
* tableNameTransform: (name) => name.toLowerCase(),
|
|
82
84
|
* })
|
|
83
85
|
* ```
|
|
84
86
|
*/
|
|
@@ -99,11 +101,25 @@ interface WithAuditExtensionOptions {
|
|
|
99
101
|
*
|
|
100
102
|
* @example
|
|
101
103
|
* ```ts
|
|
102
|
-
*
|
|
104
|
+
* // auditTables must use SQL table names (auto-detected from @@map directives)
|
|
105
|
+
* const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: ["users"] });
|
|
103
106
|
* const auditedPrisma = withAuditExtension(prisma, audit.captureLog);
|
|
104
107
|
* // Use auditedPrisma everywhere — mutations are transparently logged
|
|
105
108
|
* ```
|
|
106
109
|
*/
|
|
107
110
|
declare function withAuditExtension<T extends PrismaClientLike>(prisma: T, captureLog: (input: CaptureLogInput) => Promise<void>, options?: WithAuditExtensionOptions): T;
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Runtime model-to-table mapping for Prisma clients that expose
|
|
114
|
+
* `_runtimeDataModel.models`. This lets the audit extension auto-detect
|
|
115
|
+
* the SQL table name (from `@@map`) without user configuration.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Returns a `Record<string, string>` mapping Prisma model names to their
|
|
119
|
+
* SQL table names (from `@@map`). Models without `@@map` map to themselves.
|
|
120
|
+
*
|
|
121
|
+
* Returns `undefined` if the client does not expose `_runtimeDataModel`.
|
|
122
|
+
*/
|
|
123
|
+
declare function prismaModelMap(prisma: object): Record<string, string> | undefined;
|
|
124
|
+
|
|
125
|
+
export { type BulkMode, type PrismaClientLike, type PrismaClientWithRaw, type WithAuditExtensionOptions, prismaAuditAdapter, prismaModelMap, withAuditExtension };
|
package/dist/index.d.ts
CHANGED
|
@@ -70,15 +70,17 @@ interface WithAuditExtensionOptions {
|
|
|
70
70
|
metadata?: Record<string, unknown>;
|
|
71
71
|
/**
|
|
72
72
|
* Maps a Prisma model name to the `tableName` stored in audit logs.
|
|
73
|
-
* Defaults to the model name as-is (PascalCase, e.g. `"Project"`).
|
|
74
73
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* By default the extension auto-detects the SQL table name from Prisma's
|
|
75
|
+
* `_runtimeDataModel` (populated by `@@map` directives). If the runtime
|
|
76
|
+
* metadata is unavailable, the model name is used as-is.
|
|
77
|
+
*
|
|
78
|
+
* When provided, this function takes full precedence over auto-detection.
|
|
77
79
|
*
|
|
78
80
|
* @example
|
|
79
81
|
* ```ts
|
|
80
82
|
* withAuditExtension(prisma, captureLog, {
|
|
81
|
-
* tableNameTransform: (name) => name.toLowerCase(),
|
|
83
|
+
* tableNameTransform: (name) => name.toLowerCase(),
|
|
82
84
|
* })
|
|
83
85
|
* ```
|
|
84
86
|
*/
|
|
@@ -99,11 +101,25 @@ interface WithAuditExtensionOptions {
|
|
|
99
101
|
*
|
|
100
102
|
* @example
|
|
101
103
|
* ```ts
|
|
102
|
-
*
|
|
104
|
+
* // auditTables must use SQL table names (auto-detected from @@map directives)
|
|
105
|
+
* const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: ["users"] });
|
|
103
106
|
* const auditedPrisma = withAuditExtension(prisma, audit.captureLog);
|
|
104
107
|
* // Use auditedPrisma everywhere — mutations are transparently logged
|
|
105
108
|
* ```
|
|
106
109
|
*/
|
|
107
110
|
declare function withAuditExtension<T extends PrismaClientLike>(prisma: T, captureLog: (input: CaptureLogInput) => Promise<void>, options?: WithAuditExtensionOptions): T;
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Runtime model-to-table mapping for Prisma clients that expose
|
|
114
|
+
* `_runtimeDataModel.models`. This lets the audit extension auto-detect
|
|
115
|
+
* the SQL table name (from `@@map`) without user configuration.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Returns a `Record<string, string>` mapping Prisma model names to their
|
|
119
|
+
* SQL table names (from `@@map`). Models without `@@map` map to themselves.
|
|
120
|
+
*
|
|
121
|
+
* Returns `undefined` if the client does not expose `_runtimeDataModel`.
|
|
122
|
+
*/
|
|
123
|
+
declare function prismaModelMap(prisma: object): Record<string, string> | undefined;
|
|
124
|
+
|
|
125
|
+
export { type BulkMode, type PrismaClientLike, type PrismaClientWithRaw, type WithAuditExtensionOptions, prismaAuditAdapter, prismaModelMap, withAuditExtension };
|
package/dist/index.js
CHANGED
|
@@ -66,12 +66,44 @@ function getAuditOperation(action) {
|
|
|
66
66
|
return void 0;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// src/model-map.ts
|
|
70
|
+
function hasPrismaRuntimeDataModel(value) {
|
|
71
|
+
if (!("_runtimeDataModel" in value)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const { _runtimeDataModel: rdm } = value;
|
|
75
|
+
if (rdm === null || typeof rdm !== "object") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return "models" in rdm;
|
|
79
|
+
}
|
|
80
|
+
function prismaModelMap(prisma) {
|
|
81
|
+
if (!hasPrismaRuntimeDataModel(prisma)) {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const [modelName, meta] of Object.entries(prisma._runtimeDataModel.models)) {
|
|
86
|
+
result[modelName] = meta.dbName ?? modelName;
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
function buildTableNameResolver(prisma, transform) {
|
|
91
|
+
if (transform !== void 0) {
|
|
92
|
+
return transform;
|
|
93
|
+
}
|
|
94
|
+
const map = prismaModelMap(prisma);
|
|
95
|
+
if (map !== void 0) {
|
|
96
|
+
return (modelName) => map[modelName] ?? modelName;
|
|
97
|
+
}
|
|
98
|
+
return (modelName) => modelName;
|
|
99
|
+
}
|
|
100
|
+
|
|
69
101
|
// src/extension.ts
|
|
70
102
|
function withAuditExtension(prisma, captureLog, options = {}) {
|
|
71
103
|
const bulkMode = options.bulkMode ?? "per-row";
|
|
72
104
|
const handleError = buildErrorHandler(options.onError);
|
|
73
105
|
const metadata = options.metadata;
|
|
74
|
-
const
|
|
106
|
+
const resolveTableName = buildTableNameResolver(prisma, options.tableNameTransform);
|
|
75
107
|
const extended = prisma.$extends({
|
|
76
108
|
query: {
|
|
77
109
|
$allModels: {
|
|
@@ -86,17 +118,17 @@ function withAuditExtension(prisma, captureLog, options = {}) {
|
|
|
86
118
|
if (auditOp === void 0) {
|
|
87
119
|
return result;
|
|
88
120
|
}
|
|
121
|
+
const tableName = resolveTableName(model);
|
|
89
122
|
try {
|
|
90
123
|
await fireCaptureLog({
|
|
91
|
-
|
|
124
|
+
tableName,
|
|
92
125
|
operation,
|
|
93
126
|
auditOp,
|
|
94
127
|
args,
|
|
95
128
|
result,
|
|
96
129
|
captureLog,
|
|
97
130
|
bulkMode,
|
|
98
|
-
metadata
|
|
99
|
-
tableNameTransform
|
|
131
|
+
metadata
|
|
100
132
|
});
|
|
101
133
|
} catch (err) {
|
|
102
134
|
handleError(err);
|
|
@@ -109,17 +141,15 @@ function withAuditExtension(prisma, captureLog, options = {}) {
|
|
|
109
141
|
return extended;
|
|
110
142
|
}
|
|
111
143
|
async function fireCaptureLog({
|
|
112
|
-
|
|
144
|
+
tableName,
|
|
113
145
|
operation,
|
|
114
146
|
auditOp,
|
|
115
147
|
args,
|
|
116
148
|
result,
|
|
117
149
|
captureLog,
|
|
118
150
|
bulkMode,
|
|
119
|
-
metadata
|
|
120
|
-
tableNameTransform
|
|
151
|
+
metadata
|
|
121
152
|
}) {
|
|
122
|
-
const tableName = tableNameTransform !== void 0 ? tableNameTransform(model) : model;
|
|
123
153
|
const actorId = getAuditContext()?.actorId;
|
|
124
154
|
if (operation === "createMany") {
|
|
125
155
|
await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });
|
|
@@ -299,6 +329,7 @@ function getArgsData(args) {
|
|
|
299
329
|
}
|
|
300
330
|
export {
|
|
301
331
|
prismaAuditAdapter,
|
|
332
|
+
prismaModelMap,
|
|
302
333
|
withAuditExtension
|
|
303
334
|
};
|
|
304
335
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/extension.ts","../src/action-map.ts"],"sourcesContent":["import type { AuditDatabaseAdapter, AuditLog } from \"@usebetterdev/audit-core\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports raw query execution.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientWithRaw {\n $executeRawUnsafe(query: string, ...params: unknown[]): Promise<number>;\n}\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Prisma client.\n *\n * Implements `writeLog` via `$executeRawUnsafe` for precise control over\n * PostgreSQL type casts (::uuid, ::timestamptz, ::jsonb) and null handling.\n *\n * Only `writeLog` is implemented — querying audit logs via Prisma is out of scope.\n * Use `drizzleAuditAdapter` if you also need `queryLogs`.\n */\nexport function prismaAuditAdapter(db: PrismaClientWithRaw): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n await db.$executeRawUnsafe(\n `INSERT INTO audit_logs (\n id, timestamp, table_name, operation, record_id,\n actor_id, before_data, after_data, diff,\n label, description, severity, compliance,\n notify, reason, metadata, redacted_fields\n ) VALUES (\n $1::uuid, $2::timestamptz, $3, $4, $5,\n $6, $7::jsonb, $8::jsonb, $9::jsonb,\n $10, $11, $12, $13::jsonb,\n $14, $15, $16::jsonb, $17::jsonb\n )`,\n log.id,\n log.timestamp.toISOString(),\n log.tableName,\n log.operation,\n log.recordId,\n log.actorId ?? null,\n jsonOrNull(log.beforeData),\n jsonOrNull(log.afterData),\n jsonOrNull(log.diff),\n log.label ?? null,\n log.description ?? null,\n log.severity ?? null,\n jsonOrNull(log.compliance),\n log.notify ?? null,\n log.reason ?? null,\n jsonOrNull(log.metadata),\n jsonOrNull(log.redactedFields),\n );\n },\n };\n}\n\nfunction jsonOrNull(value: unknown): string | null {\n if (value === undefined || value === null) {\n return null;\n }\n return JSON.stringify(value);\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getAuditContext } from \"@usebetterdev/audit-core\";\nimport { getAuditOperation } from \"./action-map.js\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports `$extends`.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientLike {\n $extends(extension: PrismaExtensionDefinition): unknown;\n}\n\n/**\n * Shape of the Prisma `$extends` query extension definition.\n * We only type what we need — the full shape lives in `@prisma/client/extension`.\n */\ninterface PrismaExtensionDefinition {\n query: {\n $allModels: {\n $allOperations: (params: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) => Promise<unknown>;\n };\n };\n}\n\nexport type BulkMode = \"per-row\" | \"bulk\";\n\nexport interface WithAuditExtensionOptions {\n /**\n * How to handle bulk write operations (`createMany`, `updateMany`, `deleteMany`).\n *\n * - `\"per-row\"` (default): for `createMany` and `createManyAndReturn`, fires\n * one audit entry per item in `args.data`. For `updateMany` and `deleteMany`,\n * individual record IDs are unavailable so one entry with `recordId: \"unknown\"`\n * is fired regardless.\n * - `\"bulk\"`: fires a single audit entry for the entire operation.\n */\n bulkMode?: BulkMode;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n * Errors are always swallowed — audit failures never propagate to callers.\n */\n onError?: (error: unknown) => void;\n /**\n * Extra structured data merged into every audit log entry produced by this\n * extension. Useful for tagging logs with the adapter name or environment.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, audit.captureLog, { metadata: { adapter: \"prisma\" } })\n * ```\n */\n metadata?: Record<string, unknown>;\n /**\n * Maps a Prisma model name to the `tableName` stored in audit logs.\n * Defaults to the model name as-is (PascalCase, e.g. `\"Project\"`).\n *\n * Use `(name) => name.toLowerCase()` to normalise to SQL table naming so\n * Prisma audit entries align with Drizzle and other SQL-based adapters.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, captureLog, {\n * tableNameTransform: (name) => name.toLowerCase(), // \"Project\" → \"projects\"\n * })\n * ```\n */\n tableNameTransform?: (modelName: string) => string;\n}\n\n/**\n * Wraps a Prisma client with a `$extends` query extension that calls\n * `captureLog` after each successful mutation.\n *\n * Returns a new extended client of the same type `T`. Use this extended\n * client in place of the original — all queries behave identically.\n *\n * Actor identity is read automatically from the current `AuditContext`\n * (set via `audit.withContext` or `runWithAuditContext` from audit-core).\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`.\n *\n * @example\n * ```ts\n * const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: [\"User\"] });\n * const auditedPrisma = withAuditExtension(prisma, audit.captureLog);\n * // Use auditedPrisma everywhere — mutations are transparently logged\n * ```\n */\nexport function withAuditExtension<T extends PrismaClientLike>(\n prisma: T,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options: WithAuditExtensionOptions = {},\n): T {\n const bulkMode = options.bulkMode ?? \"per-row\";\n const handleError = buildErrorHandler(options.onError);\n const metadata = options.metadata;\n const tableNameTransform = options.tableNameTransform;\n\n const extended = prisma.$extends({\n query: {\n $allModels: {\n async $allOperations({\n model,\n operation,\n args,\n query,\n }: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) {\n const result = await query(args);\n\n const auditOp = getAuditOperation(operation);\n if (auditOp === undefined) {\n return result;\n }\n\n try {\n await fireCaptureLog({\n model,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n tableNameTransform,\n });\n } catch (err) {\n handleError(err);\n }\n\n return result;\n },\n },\n },\n });\n\n // The $extends return type is `unknown` — casting back to T is safe because\n // the extension only adds transparent interception; it does not change the query API.\n return extended as unknown as T;\n}\n\ninterface FireCaptureLogParams {\n model: string;\n operation: string;\n auditOp: AuditOperation;\n args: unknown;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n tableNameTransform: ((modelName: string) => string) | undefined;\n}\n\nasync function fireCaptureLog({\n model,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n tableNameTransform,\n}: FireCaptureLogParams): Promise<void> {\n const tableName = tableNameTransform !== undefined ? tableNameTransform(model) : model;\n const actorId = getAuditContext()?.actorId;\n\n if (operation === \"createMany\") {\n await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"createManyAndReturn\") {\n await handleCreateManyAndReturn({ tableName, auditOp, result, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"updateMany\" || operation === \"deleteMany\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // Single-record operations: create, update, upsert, delete\n const row = toRecord(result);\n const recordId = (row !== undefined ? extractId(row) : undefined) ?? \"unknown\";\n\n if (auditOp === \"INSERT\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n if (auditOp === \"UPDATE\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // DELETE: result is the deleted record\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { before: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n}\n\ninterface HandleCreateManyParams {\n tableName: string;\n auditOp: AuditOperation;\n args: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateMany({\n tableName,\n auditOp,\n args,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // per-row: fire one entry per item in args.data\n const rows = getArgsData(args);\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\ninterface HandleCreateManyAndReturnParams {\n tableName: string;\n auditOp: AuditOperation;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateManyAndReturn({\n tableName,\n auditOp,\n result,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyAndReturnParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n const rows = Array.isArray(result) ? result : [];\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\nfunction buildErrorHandler(\n onError: ((error: unknown) => void) | undefined,\n): (error: unknown) => void {\n return (error: unknown) => {\n try {\n if (onError !== undefined) {\n onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`audit-prisma: capture failed — ${msg}`);\n }\n } catch {\n // onError itself threw — swallow to never break mutations.\n }\n };\n}\n\nfunction extractId(record: Record<string, unknown>): string | undefined {\n const id = record[\"id\"];\n if (id !== undefined && id !== null) {\n return String(id);\n }\n return undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && value !== undefined && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction getArgsData(args: unknown): unknown[] {\n if (args !== null && typeof args === \"object\" && \"data\" in args) {\n const data = (args as Record<string, unknown>)[\"data\"];\n if (Array.isArray(data)) {\n return data;\n }\n }\n return [];\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\n/**\n * Maps Prisma query action names to AuditOperation values.\n * Actions not in this map are read-only queries and are not audited.\n */\nexport const ACTION_MAP = {\n create: \"INSERT\",\n createMany: \"INSERT\",\n createManyAndReturn: \"INSERT\",\n update: \"UPDATE\",\n updateMany: \"UPDATE\",\n upsert: \"UPDATE\",\n delete: \"DELETE\",\n deleteMany: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type MutableAction = keyof typeof ACTION_MAP;\n\nfunction isMutableAction(action: string): action is MutableAction {\n return action in ACTION_MAP;\n}\n\n/**\n * Returns the AuditOperation for a Prisma action name, or `undefined` for\n * read-only operations (findMany, findUnique, count, aggregate, etc.).\n */\nexport function getAuditOperation(action: string): AuditOperation | undefined {\n if (isMutableAction(action)) {\n return ACTION_MAP[action];\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/extension.ts","../src/action-map.ts","../src/model-map.ts"],"sourcesContent":["import type { AuditDatabaseAdapter, AuditLog } from \"@usebetterdev/audit-core\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports raw query execution.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientWithRaw {\n $executeRawUnsafe(query: string, ...params: unknown[]): Promise<number>;\n}\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Prisma client.\n *\n * Implements `writeLog` via `$executeRawUnsafe` for precise control over\n * PostgreSQL type casts (::uuid, ::timestamptz, ::jsonb) and null handling.\n *\n * Only `writeLog` is implemented — querying audit logs via Prisma is out of scope.\n * Use `drizzleAuditAdapter` if you also need `queryLogs`.\n */\nexport function prismaAuditAdapter(db: PrismaClientWithRaw): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n await db.$executeRawUnsafe(\n `INSERT INTO audit_logs (\n id, timestamp, table_name, operation, record_id,\n actor_id, before_data, after_data, diff,\n label, description, severity, compliance,\n notify, reason, metadata, redacted_fields\n ) VALUES (\n $1::uuid, $2::timestamptz, $3, $4, $5,\n $6, $7::jsonb, $8::jsonb, $9::jsonb,\n $10, $11, $12, $13::jsonb,\n $14, $15, $16::jsonb, $17::jsonb\n )`,\n log.id,\n log.timestamp.toISOString(),\n log.tableName,\n log.operation,\n log.recordId,\n log.actorId ?? null,\n jsonOrNull(log.beforeData),\n jsonOrNull(log.afterData),\n jsonOrNull(log.diff),\n log.label ?? null,\n log.description ?? null,\n log.severity ?? null,\n jsonOrNull(log.compliance),\n log.notify ?? null,\n log.reason ?? null,\n jsonOrNull(log.metadata),\n jsonOrNull(log.redactedFields),\n );\n },\n };\n}\n\nfunction jsonOrNull(value: unknown): string | null {\n if (value === undefined || value === null) {\n return null;\n }\n return JSON.stringify(value);\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getAuditContext } from \"@usebetterdev/audit-core\";\nimport { getAuditOperation } from \"./action-map.js\";\nimport { buildTableNameResolver } from \"./model-map.js\";\n\n/**\n * Minimal duck-typed interface for a Prisma client that supports `$extends`.\n * Avoids a hard dependency on the generated `@prisma/client`.\n */\nexport interface PrismaClientLike {\n $extends(extension: PrismaExtensionDefinition): unknown;\n}\n\n/**\n * Shape of the Prisma `$extends` query extension definition.\n * We only type what we need — the full shape lives in `@prisma/client/extension`.\n */\ninterface PrismaExtensionDefinition {\n query: {\n $allModels: {\n $allOperations: (params: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) => Promise<unknown>;\n };\n };\n}\n\nexport type BulkMode = \"per-row\" | \"bulk\";\n\nexport interface WithAuditExtensionOptions {\n /**\n * How to handle bulk write operations (`createMany`, `updateMany`, `deleteMany`).\n *\n * - `\"per-row\"` (default): for `createMany` and `createManyAndReturn`, fires\n * one audit entry per item in `args.data`. For `updateMany` and `deleteMany`,\n * individual record IDs are unavailable so one entry with `recordId: \"unknown\"`\n * is fired regardless.\n * - `\"bulk\"`: fires a single audit entry for the entire operation.\n */\n bulkMode?: BulkMode;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n * Errors are always swallowed — audit failures never propagate to callers.\n */\n onError?: (error: unknown) => void;\n /**\n * Extra structured data merged into every audit log entry produced by this\n * extension. Useful for tagging logs with the adapter name or environment.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, audit.captureLog, { metadata: { adapter: \"prisma\" } })\n * ```\n */\n metadata?: Record<string, unknown>;\n /**\n * Maps a Prisma model name to the `tableName` stored in audit logs.\n *\n * By default the extension auto-detects the SQL table name from Prisma's\n * `_runtimeDataModel` (populated by `@@map` directives). If the runtime\n * metadata is unavailable, the model name is used as-is.\n *\n * When provided, this function takes full precedence over auto-detection.\n *\n * @example\n * ```ts\n * withAuditExtension(prisma, captureLog, {\n * tableNameTransform: (name) => name.toLowerCase(),\n * })\n * ```\n */\n tableNameTransform?: (modelName: string) => string;\n}\n\n/**\n * Wraps a Prisma client with a `$extends` query extension that calls\n * `captureLog` after each successful mutation.\n *\n * Returns a new extended client of the same type `T`. Use this extended\n * client in place of the original — all queries behave identically.\n *\n * Actor identity is read automatically from the current `AuditContext`\n * (set via `audit.withContext` or `runWithAuditContext` from audit-core).\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`.\n *\n * @example\n * ```ts\n * // auditTables must use SQL table names (auto-detected from @@map directives)\n * const audit = betterAudit({ database: prismaAuditAdapter(prisma), auditTables: [\"users\"] });\n * const auditedPrisma = withAuditExtension(prisma, audit.captureLog);\n * // Use auditedPrisma everywhere — mutations are transparently logged\n * ```\n */\nexport function withAuditExtension<T extends PrismaClientLike>(\n prisma: T,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options: WithAuditExtensionOptions = {},\n): T {\n const bulkMode = options.bulkMode ?? \"per-row\";\n const handleError = buildErrorHandler(options.onError);\n const metadata = options.metadata;\n const resolveTableName = buildTableNameResolver(prisma, options.tableNameTransform);\n\n const extended = prisma.$extends({\n query: {\n $allModels: {\n async $allOperations({\n model,\n operation,\n args,\n query,\n }: {\n model: string;\n operation: string;\n args: unknown;\n query: (args: unknown) => Promise<unknown>;\n }) {\n const result = await query(args);\n\n const auditOp = getAuditOperation(operation);\n if (auditOp === undefined) {\n return result;\n }\n\n const tableName = resolveTableName(model);\n\n try {\n await fireCaptureLog({\n tableName,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n });\n } catch (err) {\n handleError(err);\n }\n\n return result;\n },\n },\n },\n });\n\n // The $extends return type is `unknown` — casting back to T is safe because\n // the extension only adds transparent interception; it does not change the query API.\n return extended as unknown as T;\n}\n\ninterface FireCaptureLogParams {\n tableName: string;\n operation: string;\n auditOp: AuditOperation;\n args: unknown;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n}\n\nasync function fireCaptureLog({\n tableName,\n operation,\n auditOp,\n args,\n result,\n captureLog,\n bulkMode,\n metadata,\n}: FireCaptureLogParams): Promise<void> {\n const actorId = getAuditContext()?.actorId;\n\n if (operation === \"createMany\") {\n await handleCreateMany({ tableName, auditOp, args, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"createManyAndReturn\") {\n await handleCreateManyAndReturn({ tableName, auditOp, result, captureLog, bulkMode, metadata, actorId });\n return;\n }\n\n if (operation === \"updateMany\" || operation === \"deleteMany\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // Single-record operations: create, update, upsert, delete\n const row = toRecord(result);\n const recordId = (row !== undefined ? extractId(row) : undefined) ?? \"unknown\";\n\n if (auditOp === \"INSERT\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n if (auditOp === \"UPDATE\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { after: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // DELETE: result is the deleted record\n await captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(row !== undefined && { before: row }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n}\n\ninterface HandleCreateManyParams {\n tableName: string;\n auditOp: AuditOperation;\n args: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateMany({\n tableName,\n auditOp,\n args,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n // per-row: fire one entry per item in args.data\n const rows = getArgsData(args);\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\ninterface HandleCreateManyAndReturnParams {\n tableName: string;\n auditOp: AuditOperation;\n result: unknown;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n bulkMode: BulkMode;\n metadata: Record<string, unknown> | undefined;\n actorId: string | undefined;\n}\n\nasync function handleCreateManyAndReturn({\n tableName,\n auditOp,\n result,\n captureLog,\n bulkMode,\n metadata,\n actorId,\n}: HandleCreateManyAndReturnParams): Promise<void> {\n if (bulkMode === \"bulk\") {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n const rows = Array.isArray(result) ? result : [];\n if (rows.length === 0) {\n await captureLog({\n tableName,\n operation: auditOp,\n recordId: \"unknown\",\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n return;\n }\n\n await Promise.all(\n rows.map((row) => {\n const record = toRecord(row);\n const recordId = (record !== undefined ? extractId(record) : undefined) ?? \"unknown\";\n return captureLog({\n tableName,\n operation: auditOp,\n recordId,\n ...(record !== undefined && { after: record }),\n ...(metadata !== undefined && { metadata }),\n ...(actorId !== undefined && { actorId }),\n });\n }),\n );\n}\n\nfunction buildErrorHandler(\n onError: ((error: unknown) => void) | undefined,\n): (error: unknown) => void {\n return (error: unknown) => {\n try {\n if (onError !== undefined) {\n onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`audit-prisma: capture failed — ${msg}`);\n }\n } catch {\n // onError itself threw — swallow to never break mutations.\n }\n };\n}\n\nfunction extractId(record: Record<string, unknown>): string | undefined {\n const id = record[\"id\"];\n if (id !== undefined && id !== null) {\n return String(id);\n }\n return undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && value !== undefined && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction getArgsData(args: unknown): unknown[] {\n if (args !== null && typeof args === \"object\" && \"data\" in args) {\n const data = (args as Record<string, unknown>)[\"data\"];\n if (Array.isArray(data)) {\n return data;\n }\n }\n return [];\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\n/**\n * Maps Prisma query action names to AuditOperation values.\n * Actions not in this map are read-only queries and are not audited.\n */\nexport const ACTION_MAP = {\n create: \"INSERT\",\n createMany: \"INSERT\",\n createManyAndReturn: \"INSERT\",\n update: \"UPDATE\",\n updateMany: \"UPDATE\",\n upsert: \"UPDATE\",\n delete: \"DELETE\",\n deleteMany: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type MutableAction = keyof typeof ACTION_MAP;\n\nfunction isMutableAction(action: string): action is MutableAction {\n return action in ACTION_MAP;\n}\n\n/**\n * Returns the AuditOperation for a Prisma action name, or `undefined` for\n * read-only operations (findMany, findUnique, count, aggregate, etc.).\n */\nexport function getAuditOperation(action: string): AuditOperation | undefined {\n if (isMutableAction(action)) {\n return ACTION_MAP[action];\n }\n return undefined;\n}\n","/**\n * Runtime model-to-table mapping for Prisma clients that expose\n * `_runtimeDataModel.models`. This lets the audit extension auto-detect\n * the SQL table name (from `@@map`) without user configuration.\n */\n\ninterface RuntimeModel {\n dbName: string | null;\n}\n\ninterface RuntimeDataModel {\n models: Record<string, RuntimeModel>;\n}\n\ninterface PrismaWithRuntimeDataModel {\n _runtimeDataModel: RuntimeDataModel;\n}\n\n/**\n * Type guard: checks whether `value` exposes Prisma's `_runtimeDataModel.models`.\n */\nfunction hasPrismaRuntimeDataModel(value: object): value is PrismaWithRuntimeDataModel {\n if (!(\"_runtimeDataModel\" in value)) {\n return false;\n }\n\n const { _runtimeDataModel: rdm } = value;\n if (rdm === null || typeof rdm !== \"object\") {\n return false;\n }\n\n return \"models\" in rdm;\n}\n\n/**\n * Returns a `Record<string, string>` mapping Prisma model names to their\n * SQL table names (from `@@map`). Models without `@@map` map to themselves.\n *\n * Returns `undefined` if the client does not expose `_runtimeDataModel`.\n */\nexport function prismaModelMap(prisma: object): Record<string, string> | undefined {\n if (!hasPrismaRuntimeDataModel(prisma)) {\n return undefined;\n }\n\n const result: Record<string, string> = {};\n for (const [modelName, meta] of Object.entries(prisma._runtimeDataModel.models)) {\n result[modelName] = meta.dbName ?? modelName;\n }\n return result;\n}\n\n/**\n * Builds a function that resolves a Prisma model name to a table name.\n *\n * Resolution order:\n * 1. If `transform` is provided, use it (full user control).\n * 2. Else if the client exposes `_runtimeDataModel`, use `dbName ?? modelName`.\n * 3. Else return the model name as-is (safe fallback).\n */\nexport function buildTableNameResolver(\n prisma: object,\n transform: ((modelName: string) => string) | undefined,\n): (modelName: string) => string {\n if (transform !== undefined) {\n return transform;\n }\n\n const map = prismaModelMap(prisma);\n if (map !== undefined) {\n return (modelName: string) => map[modelName] ?? modelName;\n }\n\n return (modelName: string) => modelName;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebetterdev/audit-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-beta.1",
|
|
4
4
|
"repository": "github:usebetter-dev/usebetter",
|
|
5
5
|
"bugs": "https://github.com/usebetter-dev/usebetter/issues",
|
|
6
6
|
"homepage": "https://github.com/usebetter-dev/usebetter#readme",
|
|
@@ -24,16 +24,8 @@
|
|
|
24
24
|
"dist",
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsup",
|
|
29
|
-
"lint": "oxlint",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"test:integration": "vitest run -c vitest.integration.config.ts",
|
|
32
|
-
"typecheck": "tsc --noEmit",
|
|
33
|
-
"prisma:generate": "prisma generate --config prisma.config.ts"
|
|
34
|
-
},
|
|
35
27
|
"dependencies": {
|
|
36
|
-
"@usebetterdev/audit-core": "
|
|
28
|
+
"@usebetterdev/audit-core": "0.5.0-beta.1"
|
|
37
29
|
},
|
|
38
30
|
"peerDependencies": {
|
|
39
31
|
"@prisma/client": ">=5.0.0"
|
|
@@ -49,14 +41,22 @@
|
|
|
49
41
|
"@testcontainers/postgresql": "^11.11.0",
|
|
50
42
|
"@types/node": "^22.10.0",
|
|
51
43
|
"@types/pg": "^8.11.0",
|
|
52
|
-
"@usebetterdev/test-utils": "workspace:*",
|
|
53
44
|
"pg": "^8.13.0",
|
|
54
45
|
"prisma": "^7.0.0",
|
|
55
46
|
"tsup": "^8.3.5",
|
|
56
47
|
"typescript": "~5.7.2",
|
|
57
|
-
"vitest": "^2.1.6"
|
|
48
|
+
"vitest": "^2.1.6",
|
|
49
|
+
"@usebetterdev/test-utils": "0.5.0-beta.1"
|
|
58
50
|
},
|
|
59
51
|
"engines": {
|
|
60
52
|
"node": ">=22"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"lint": "oxlint",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:integration": "vitest run -c vitest.integration.config.ts",
|
|
59
|
+
"typecheck": "tsc --noEmit",
|
|
60
|
+
"prisma:generate": "prisma generate --config prisma.config.ts"
|
|
61
61
|
}
|
|
62
|
-
}
|
|
62
|
+
}
|