@reprova/sdk 0.2.0 → 0.5.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.
- package/README.md +293 -11
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +18 -0
- package/dist/context.js.map +1 -1
- package/dist/drizzle.d.ts +33 -0
- package/dist/drizzle.d.ts.map +1 -0
- package/dist/drizzle.js +194 -0
- package/dist/drizzle.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/knex.d.ts +5 -0
- package/dist/knex.d.ts.map +1 -0
- package/dist/knex.js +83 -0
- package/dist/knex.js.map +1 -0
- package/dist/kysely.d.ts +22 -0
- package/dist/kysely.d.ts.map +1 -0
- package/dist/kysely.js +172 -0
- package/dist/kysely.js.map +1 -0
- package/dist/nest.d.ts +18 -0
- package/dist/nest.d.ts.map +1 -0
- package/dist/nest.js +94 -0
- package/dist/nest.js.map +1 -0
- package/dist/proto.gen.d.ts +75 -1
- package/dist/proto.gen.d.ts.map +1 -1
- package/dist/registry.d.ts +22 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +70 -0
- package/dist/registry.js.map +1 -0
- package/dist/sdk.d.ts +30 -4
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +482 -16
- package/dist/sdk.js.map +1 -1
- package/dist/sequelize.d.ts +5 -0
- package/dist/sequelize.d.ts.map +1 -0
- package/dist/sequelize.js +105 -0
- package/dist/sequelize.js.map +1 -0
- package/dist/typeorm.d.ts +2 -0
- package/dist/typeorm.d.ts.map +1 -0
- package/dist/typeorm.js +65 -0
- package/dist/typeorm.js.map +1 -0
- package/package.json +19 -1
package/README.md
CHANGED
|
@@ -1,28 +1,310 @@
|
|
|
1
1
|
# @reprova/sdk
|
|
2
2
|
|
|
3
|
-
Node.js/TypeScript SDK
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
Node.js/TypeScript SDK for **any Node app** — vanilla `node:http`, Koa, Fastify,
|
|
4
|
+
Express, background workers. `Reprova.init()` instruments Node's own HTTP servers,
|
|
5
|
+
so every request gets capture context (errors, silent 5xxs, headers, trace ids,
|
|
6
|
+
outbound HTTP calls) with zero framework code. Express and Prisma integrations are
|
|
7
|
+
optional enrichments: Express adds async-rejection forwarding and parsed request
|
|
8
|
+
bodies; Prisma adds automatic data footprints (which tables/rows each request
|
|
9
|
+
touched — what makes an error *reproducible*, not just visible). The batched,
|
|
10
|
+
fire-and-forget transport never adds meaningful latency, even when the control
|
|
11
|
+
plane is unreachable (`transport.test.ts`; `chaos.spec.ts` proves it against the
|
|
12
|
+
real running control plane).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
```bash
|
|
16
|
+
npm install @reprova/sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage — any Node app
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Reprova } from '@reprova/sdk';
|
|
23
|
+
|
|
24
|
+
Reprova.init({ dsn: process.env.REPROVA_DSN, release: gitSha });
|
|
25
|
+
// That's it. A plain http.createServer / Koa / Fastify app now captures
|
|
26
|
+
// errors, silent 5xx responses, request context, trace ids, outbound calls.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`init` instruments `http.Server`/`https.Server` directly and attaches
|
|
30
|
+
process-level error handlers (opt-outs: `instrumentHttp: false`,
|
|
31
|
+
`processHandlers: false`; `uncaughtException` flushes then exits 1). A missing
|
|
32
|
+
`dsn` puts the SDK in disabled mode: everything mounts, nothing records.
|
|
33
|
+
|
|
34
|
+
### Background jobs and scripts
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
await sdk.runJob('nightly-sync', async () => { ... }); // failures ingest as job_failure
|
|
38
|
+
await sdk.runWithContext({ name: 'csv-import' }, doImport); // generic wrapper: capture + rethrow
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Data footprints without Prisma (knex, raw SQL, any DAO)
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const rows = await knex('invoices').where({ id }).select();
|
|
45
|
+
sdk.recordFootprint({ model: 'Invoice', op: 'select', pks: rows.map(r => r.id), count: rows.length });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Reproductions need footprints. Six ORMs record them automatically (below);
|
|
49
|
+
anything else records them with one `recordFootprint` call per query site.
|
|
50
|
+
|
|
51
|
+
| ORM | Helper | Auto PKs | where_shape |
|
|
52
|
+
| --- | --- | --- | --- |
|
|
53
|
+
| Prisma | `instrumentPrisma` / `createPrismaExtension` | ✅ exact (DMMF) | ✅ |
|
|
54
|
+
| Kysely | `createKyselyPlugin` | ✅ (`primaryKey` opt for composite) | ✅ |
|
|
55
|
+
| Sequelize | `installSequelizeHooks` | ✅ exact (model metadata) | ✅ |
|
|
56
|
+
| TypeORM | `installTypeOrmSubscriber` | ✅ exact (entity metadata) | ➖ |
|
|
57
|
+
| Knex / Objection | `installKnexHooks` | ✅ SELECT; INSERT depends on driver RETURNING | ➖ |
|
|
58
|
+
| Drizzle | `createDrizzleFootprint` | ✅ SELECT (via Cache hook); ❌ writes (Drizzle exposes no result hook for them) | ✅ |
|
|
59
|
+
|
|
60
|
+
## Express (optional enrichment)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const sdk = Reprova.init({ dsn: process.env.REPROVA_DSN, release: gitSha });
|
|
64
|
+
sdk.setupExpress(app); // async-rejection forwarding + error-middleware capture + parsed bodies
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Auth claims (automatic for most apps, zero code changes)
|
|
68
|
+
|
|
69
|
+
`repro run`/`repro test` re-sign the captured request's auth claims into a
|
|
70
|
+
token the replayed app can validate — but only if the SDK captured claims in
|
|
71
|
+
the first place. Auto-detection covers the two dominant conventions, no
|
|
72
|
+
wiring needed:
|
|
73
|
+
|
|
74
|
+
- `req.auth` — `express-oauth2-jwt-bearer` / Auth0-style middleware
|
|
75
|
+
- `req.user` — Passport, and the overwhelming majority of hand-rolled
|
|
76
|
+
Express JWT middleware (`req.user = decoded` right after `jwt.verify`)
|
|
77
|
+
|
|
78
|
+
Only apps using neither — a custom property, a non-Express framework — need
|
|
79
|
+
one explicit call from inside their own auth middleware:
|
|
10
80
|
|
|
11
|
-
## Usage
|
|
12
81
|
```ts
|
|
13
|
-
import {
|
|
82
|
+
import { setAuthClaims } from '@reprova/sdk';
|
|
83
|
+
|
|
84
|
+
function verifyToken(req, res, next) {
|
|
85
|
+
const decoded = jwt.verify(token, secret);
|
|
86
|
+
req.identity = decoded; // some other property auto-detection doesn't check
|
|
87
|
+
setAuthClaims(decoded); // tells Reprova what to re-sign at replay time
|
|
88
|
+
next();
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Without it, replay has no claims to re-sign, sends no `Authorization`
|
|
93
|
+
header, and the app rejects the replayed request as unauthenticated before
|
|
94
|
+
it ever reaches the captured bug — `repro run` reports `DIFFERENT FAILURE`
|
|
95
|
+
instead of reproducing the original error. `setAuthClaims` is a no-op
|
|
96
|
+
outside a Reprova request context, so it's safe to call unconditionally.
|
|
97
|
+
|
|
98
|
+
## Prisma (optional enrichment — automatic footprints)
|
|
14
99
|
|
|
100
|
+
One call wires both automatic footprints and `migration_id`:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
15
103
|
const sdk = Reprova.init({ dsn: process.env.REPROVA_DSN, release: gitSha });
|
|
104
|
+
await sdk.instrumentPrisma(prisma, { dmmf: Prisma.dmmf });
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`instrumentPrisma` splices the footprint extension onto your existing client
|
|
108
|
+
in place (so the shared singleton keeps recording) and best-effort reads the
|
|
109
|
+
latest row of `_prisma_migrations` into `migration_id`. It's a no-op when the
|
|
110
|
+
SDK is disabled (no `dsn` / replay), so no `if (dsn)` guard is needed, and it
|
|
111
|
+
never imports `@prisma/client` — Prisma stays an optional peer. A missing or
|
|
112
|
+
unreadable migrations table is non-fatal: `migration_id` stays `'unknown'`.
|
|
113
|
+
Pass `{ readMigrationId: false }` to set it yourself via `sdk.setMigrationId`.
|
|
114
|
+
|
|
115
|
+
Prefer to own the wiring? The extension is still exported directly:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { createPrismaExtension } from '@reprova/sdk';
|
|
119
|
+
const prisma = new PrismaClient().$extends(createPrismaExtension(Prisma.dmmf));
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Kysely (optional enrichment — automatic footprints)
|
|
123
|
+
|
|
124
|
+
Add the plugin at construction; every query then records a footprint:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { createKyselyPlugin } from '@reprova/sdk';
|
|
128
|
+
const db = new Kysely<DB>({ dialect, plugins: [createKyselyPlugin()] });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The plugin reads the compiled AST for the table(s) and operation, the WHERE
|
|
132
|
+
clause's referenced **column names** (never values — PII-safe by
|
|
133
|
+
construction), and the returned rows for primary keys. Joined/subquery tables
|
|
134
|
+
are recorded as referenced (without PKs). It's a no-op outside a captured
|
|
135
|
+
request, never imports `kysely`, and can never break a query (every path is
|
|
136
|
+
guarded).
|
|
137
|
+
|
|
138
|
+
Kysely carries no schema metadata at runtime, so PK detection defaults to
|
|
139
|
+
`['id']`. Override for composite or non-`id` keys:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
createKyselyPlugin({ primaryKey: (table) => table === 'membership' ? ['tenantId', 'userId'] : ['id'] });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`migration_id` is independent of the ORM: read your migration tool's version
|
|
146
|
+
table (Kysely's default is `kysely_migration`) and call
|
|
147
|
+
`sdk.setMigrationId(latest)`, or leave it `'unknown'`.
|
|
148
|
+
|
|
149
|
+
## Sequelize (optional enrichment — automatic footprints)
|
|
150
|
+
|
|
151
|
+
Install once after your models are defined; every query records a footprint:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { installSequelizeHooks } from '@reprova/sdk';
|
|
155
|
+
installSequelizeHooks(sequelize);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Uses Sequelize's lifecycle hooks (`afterFind`, `afterCreate`, …) plus each
|
|
159
|
+
model's `tableName` / `primaryKeyAttributes`, so PKs are exact. Hooks are
|
|
160
|
+
registered per model (and on `afterDefine` for later ones), so the table is
|
|
161
|
+
known even when a query returns 0 rows. `where_shape` = the WHERE object's
|
|
162
|
+
top-level keys (names only).
|
|
163
|
+
|
|
164
|
+
## TypeORM (optional enrichment — automatic footprints)
|
|
165
|
+
|
|
166
|
+
Attach the subscriber after `dataSource.initialize()`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { installTypeOrmSubscriber } from '@reprova/sdk';
|
|
170
|
+
await dataSource.initialize();
|
|
171
|
+
installTypeOrmSubscriber(dataSource);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Uses entity lifecycle events (`afterLoad`, `afterInsert`, …) and each entity's
|
|
175
|
+
metadata for exact PKs. TypeORM fires these per entity with no WHERE context,
|
|
176
|
+
so entries are **merged by (table, op)** within a request (union of distinct
|
|
177
|
+
PKs), and there is no `where_shape`.
|
|
178
|
+
|
|
179
|
+
## Knex / Objection (optional enrichment — automatic footprints)
|
|
180
|
+
|
|
181
|
+
One listener covers raw Knex **and** Objection.js (which runs through the same
|
|
182
|
+
Knex instance):
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { installKnexHooks } from '@reprova/sdk';
|
|
186
|
+
installKnexHooks(knex);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Reads the table and operation from Knex's `query-response` event. SELECT rows
|
|
190
|
+
yield PKs (via `primaryKey(table)`, default `['id']`); UPDATE/DELETE record an
|
|
191
|
+
affected-row count; INSERT PK fidelity depends on the driver's RETURNING
|
|
192
|
+
support (full on Postgres, last-id only on sqlite/mysql). No `where_shape`
|
|
193
|
+
(Knex has no query AST).
|
|
194
|
+
|
|
195
|
+
## Drizzle (optional enrichment — exact PKs for SELECT, shape-only writes)
|
|
196
|
+
|
|
197
|
+
Drizzle has no single result-aware hook for every operation, but it has two
|
|
198
|
+
partial ones that together cover most of it: a `logger` (SQL text, no rows)
|
|
199
|
+
and a `Cache` (real result rows for SELECT, no rows for writes).
|
|
200
|
+
`createDrizzleFootprint()` wires both, merged into one footprint entry per
|
|
201
|
+
query — no double-counting:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { createDrizzleFootprint } from '@reprova/sdk';
|
|
205
|
+
const { logger, cache } = createDrizzleFootprint();
|
|
206
|
+
const db = drizzle(client, { logger, cache });
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
- **SELECT**: exact PKs and row count, extracted from the real rows Drizzle's
|
|
210
|
+
`Cache.put()` hook receives — `get()` always returns a miss, so this never
|
|
211
|
+
serves a cached (possibly stale) result back to the app, it only observes.
|
|
212
|
+
PK columns default to `['id']`; override for composite or non-`id` keys the
|
|
213
|
+
same way as Kysely: `createDrizzleFootprint({ primaryKey: (table) => [...] })`.
|
|
214
|
+
- **INSERT/UPDATE/DELETE**: shape-only (table + operation + WHERE column
|
|
215
|
+
names), same as before — Drizzle's mutation hook (`onMutate`) gets table
|
|
216
|
+
names only, never rows or even a count, and it races the real query
|
|
217
|
+
internally. For PK-level fidelity on writes, still call
|
|
218
|
+
`sdk.recordFootprint(...)` at the query sites that matter.
|
|
219
|
+
- Only works for **async drivers** (`node-postgres`, `postgres-js`, `mysql2`,
|
|
220
|
+
etc. — Reprova's two supported dialects for the customer's own database).
|
|
221
|
+
Sync drivers (`better-sqlite3`, `bun:sqlite`) bypass Drizzle's cache path
|
|
222
|
+
entirely; use `createDrizzleLogger()` alone there (shape-only, as before).
|
|
223
|
+
Drizzle ships no SQL Server driver at all, so this adapter never covers
|
|
224
|
+
that lane regardless of driver.
|
|
225
|
+
|
|
226
|
+
Prefer to build the pair yourself, or use just one half?
|
|
227
|
+
`createDrizzleLogger()` and `createDrizzleCache({ logger })` are still
|
|
228
|
+
exported individually — pass the SAME logger instance to `createDrizzleCache`
|
|
229
|
+
to get the merge behavior; omit it to run the cache standalone (exact
|
|
230
|
+
SELECT PKs, no `where_shape`, no write coverage).
|
|
231
|
+
|
|
232
|
+
## NestJS (optional enrichment — original stacks + replay parity)
|
|
233
|
+
|
|
234
|
+
Nest's exception layer catches thrown errors before Express error middleware
|
|
235
|
+
ever sees them and converts them to 500s — so with only `init()`, Nest errors
|
|
236
|
+
are captured via the silent-5xx hook: full request context and footprint, but
|
|
237
|
+
a synthesized error instead of the real one. The filter closes that gap, and
|
|
238
|
+
gives Nest apps the same `REPROVA_REPLAY` exact-stack contract as
|
|
239
|
+
`replayErrorHandler`:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const sdk = Reprova.init({ dsn: process.env.REPROVA_DSN, release: gitSha });
|
|
243
|
+
const app = await NestFactory.create(AppModule);
|
|
244
|
+
await sdk.setupNest(app, { prisma: app.get(PrismaService), dmmf: Prisma.dmmf });
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`setupNest` registers the exception filter and (when given a Prisma client)
|
|
248
|
+
wires automatic footprints + `migration_id` — the whole integration in one
|
|
249
|
+
call, like `setupExpress`. Using TypeORM/Knex/etc. instead? Call
|
|
250
|
+
`setupNest(app)` bare and add the usual one-liner
|
|
251
|
+
(`installTypeOrmSubscriber(dataSource)`, ...). Prefer manual control?
|
|
252
|
+
`app.useGlobalFilters(sdk.nestExceptionFilter())` is still exported.
|
|
253
|
+
|
|
254
|
+
No decorator needed — a filter without `@Catch` metadata is catch-all, and
|
|
255
|
+
the SDK never imports `@nestjs/*` (structural typing, like every adapter
|
|
256
|
+
here; proven against real Nest in `test/nest.integration.test.ts`). Behavior
|
|
257
|
+
mirrors Nest's own default filter: intentional `HttpException`s (4xx) pass
|
|
258
|
+
through untouched and uncaptured; non-HTTP exceptions and 5xx
|
|
259
|
+
`HttpException`s are captured with their original type/message/stack and
|
|
260
|
+
answered with the default body. Works on the Express and Fastify adapters.
|
|
261
|
+
If you register other filters, list this one first in `useGlobalFilters` so
|
|
262
|
+
more specific `@Catch(...)` filters keep winning.
|
|
263
|
+
|
|
264
|
+
## Express (continued)
|
|
265
|
+
|
|
266
|
+
`setupExpress(app)` can be called before or after your routes; it wires the
|
|
267
|
+
request-context middleware (repositioned to run first), error capture, the
|
|
268
|
+
HTTP-5xx hook, and async rejection forwarding in one call. A missing `dsn`
|
|
269
|
+
puts the SDK in disabled mode: nothing is recorded or sent, but Express-5
|
|
270
|
+
rejection semantics still apply — so apps can call `init` unconditionally.
|
|
271
|
+
|
|
272
|
+
Prefer manual control? `sdk.requestHandler()` / `sdk.errorHandler()` /
|
|
273
|
+
`wrapResponse` are still exported and behave exactly as before:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
16
276
|
app.use(sdk.requestHandler());
|
|
17
277
|
// ... your routes ...
|
|
18
278
|
app.use(sdk.errorHandler());
|
|
19
|
-
|
|
20
|
-
const prisma = new PrismaClient().$extends(createPrismaExtension(Prisma.dmmf));
|
|
21
279
|
```
|
|
22
280
|
|
|
23
281
|
Every capture carries a W3C `trace_id`, the git `release` sha, and the latest applied
|
|
24
282
|
Prisma `migration_id` — non-negotiable schema fields the rest of the pipeline depends on.
|
|
25
283
|
|
|
284
|
+
## Async handlers just work
|
|
285
|
+
|
|
286
|
+
Existing route code needs **no changes** — no `asyncHandler` wrapper, no
|
|
287
|
+
`try/catch → next(err)`:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
app.get('/invoices/:id', async (req, res) => {
|
|
291
|
+
const invoice = await db.invoice.find(req.params.id); // a rejection here is
|
|
292
|
+
res.json(invoice); // captured automatically
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Express 4 normally discards the promise an async handler returns, so a rejection
|
|
297
|
+
never reaches error middleware. On the first request, the SDK transparently patches
|
|
298
|
+
the router's dispatch so rejected handler promises are forwarded down the error
|
|
299
|
+
chain — the exact semantics Express 5 ships natively — with the full request
|
|
300
|
+
context (headers, body, data footprint, trace id) intact on the capture. Your own
|
|
301
|
+
error middleware still runs and the client still gets its response; nothing is
|
|
302
|
+
swallowed. On Express 5 the SDK detects native forwarding and does nothing.
|
|
303
|
+
|
|
304
|
+
One limitation: a router imported from a *different* physical copy of `express`
|
|
305
|
+
in `node_modules` (rare — npm normally dedupes to one) isn't covered by the outer
|
|
306
|
+
app's patch; mount `sdk.requestHandler()` inside that sub-app to cover its copy too.
|
|
307
|
+
|
|
26
308
|
## Commands
|
|
27
309
|
```bash
|
|
28
310
|
npm run build
|
package/dist/context.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface OutboundCall {
|
|
|
21
21
|
status: number;
|
|
22
22
|
response_body?: string;
|
|
23
23
|
recorded_for_replay: boolean;
|
|
24
|
+
duration_ms?: number;
|
|
25
|
+
outcome?: 'success' | 'timeout' | 'network_error';
|
|
24
26
|
}
|
|
25
27
|
export interface ReprovaContext {
|
|
26
28
|
traceId: string;
|
|
@@ -29,7 +31,22 @@ export interface ReprovaContext {
|
|
|
29
31
|
footprint: FootprintQuery[];
|
|
30
32
|
outboundCalls: OutboundCall[];
|
|
31
33
|
captured?: boolean;
|
|
34
|
+
rawRequest?: unknown;
|
|
32
35
|
}
|
|
33
36
|
export declare const contextStorage: AsyncLocalStorage<ReprovaContext>;
|
|
34
37
|
export declare function currentContext(): ReprovaContext | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Explicitly records the authenticated identity for the in-flight request.
|
|
40
|
+
* Auto-detection already covers `req.auth` (express-oauth2-jwt-bearer/Auth0)
|
|
41
|
+
* and `req.user` (Passport and most hand-rolled Express JWT middleware) —
|
|
42
|
+
* see detectAuthClaims in sdk.ts — so most apps need zero code changes. This
|
|
43
|
+
* is the escape hatch for anything else: a custom property, a non-Express
|
|
44
|
+
* framework. Call it from inside your own auth middleware once the token is
|
|
45
|
+
* verified, e.g. `setAuthClaims(decoded)` right after `jwt.verify`. Without
|
|
46
|
+
* captured claims one way or the other, replay has nothing to re-sign into a
|
|
47
|
+
* token and the app bounces the replayed request as unauthenticated before
|
|
48
|
+
* it ever reaches the captured bug. No-ops outside a Reprova request
|
|
49
|
+
* context.
|
|
50
|
+
*/
|
|
51
|
+
export declare function setAuthClaims(claims: Record<string, unknown>): void;
|
|
35
52
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAG7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,eAAe,CAAC;CACnD;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAKnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAEtE,wBAAgB,cAAc,IAAI,cAAc,GAAG,SAAS,CAE3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAGnE"}
|
package/dist/context.js
CHANGED
|
@@ -3,4 +3,22 @@ export const contextStorage = new AsyncLocalStorage();
|
|
|
3
3
|
export function currentContext() {
|
|
4
4
|
return contextStorage.getStore();
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Explicitly records the authenticated identity for the in-flight request.
|
|
8
|
+
* Auto-detection already covers `req.auth` (express-oauth2-jwt-bearer/Auth0)
|
|
9
|
+
* and `req.user` (Passport and most hand-rolled Express JWT middleware) —
|
|
10
|
+
* see detectAuthClaims in sdk.ts — so most apps need zero code changes. This
|
|
11
|
+
* is the escape hatch for anything else: a custom property, a non-Express
|
|
12
|
+
* framework. Call it from inside your own auth middleware once the token is
|
|
13
|
+
* verified, e.g. `setAuthClaims(decoded)` right after `jwt.verify`. Without
|
|
14
|
+
* captured claims one way or the other, replay has nothing to re-sign into a
|
|
15
|
+
* token and the app bounces the replayed request as unauthenticated before
|
|
16
|
+
* it ever reaches the captured bug. No-ops outside a Reprova request
|
|
17
|
+
* context.
|
|
18
|
+
*/
|
|
19
|
+
export function setAuthClaims(claims) {
|
|
20
|
+
const ctx = currentContext();
|
|
21
|
+
if (ctx)
|
|
22
|
+
ctx.request.authClaims = claims;
|
|
23
|
+
}
|
|
6
24
|
//# sourceMappingURL=context.js.map
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA8ChD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAEtE,MAAM,UAAU,cAAc;IAC5B,OAAO,cAAc,CAAC,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,MAA+B;IAC3D,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,IAAI,GAAG;QAAE,GAAG,CAAC,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface DrizzleLoggerOptions {
|
|
2
|
+
captureWhereShape?: boolean;
|
|
3
|
+
forward?: {
|
|
4
|
+
logQuery(query: string, params: unknown[]): void;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export interface DrizzleLogger {
|
|
8
|
+
logQuery(query: string, params: unknown[]): void;
|
|
9
|
+
}
|
|
10
|
+
export interface DrizzleCache {
|
|
11
|
+
strategy(): 'explicit' | 'all';
|
|
12
|
+
get(key: string, tables: string[], isTag: boolean, isAutoInvalidate?: boolean): Promise<unknown[] | undefined>;
|
|
13
|
+
put(hashedQuery: string, response: unknown, tables: string[], isTag?: boolean, config?: unknown): Promise<void>;
|
|
14
|
+
onMutate(params: {
|
|
15
|
+
tables?: unknown;
|
|
16
|
+
tags?: unknown;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface DrizzleCacheOptions {
|
|
20
|
+
primaryKey?: (table: string) => string[];
|
|
21
|
+
logger?: DrizzleLogger;
|
|
22
|
+
}
|
|
23
|
+
export interface DrizzleFootprintOptions {
|
|
24
|
+
captureWhereShape?: boolean;
|
|
25
|
+
primaryKey?: (table: string) => string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function createDrizzleLogger(opts?: DrizzleLoggerOptions): DrizzleLogger;
|
|
28
|
+
export declare function createDrizzleCache(opts?: DrizzleCacheOptions): DrizzleCache;
|
|
29
|
+
export declare function createDrizzleFootprint(opts?: DrizzleFootprintOptions): {
|
|
30
|
+
logger: DrizzleLogger;
|
|
31
|
+
cache: DrizzleCache;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=drizzle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,oBAAoB;IAEnC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KAAE,CAAC;CAChE;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD;AAKD,MAAM,WAAW,YAAY;IAC3B,QAAQ,IAAI,UAAU,GAAG,KAAK,CAAC;IAC/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,CAAC;IAC/G,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChH,QAAQ,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,mBAAmB;IAIlC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAMzC,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CAC1C;AAuED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAwClF;AAkBD,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,mBAAwB,GAAG,YAAY,CAwD/E;AAOD,wBAAgB,sBAAsB,CACpC,IAAI,GAAE,uBAA4B,GACjC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAIhD"}
|
package/dist/drizzle.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { contextStorage } from './context.js';
|
|
2
|
+
const OP_BY_KEYWORD = {
|
|
3
|
+
select: 'select',
|
|
4
|
+
insert: 'insert',
|
|
5
|
+
update: 'update',
|
|
6
|
+
delete: 'delete',
|
|
7
|
+
};
|
|
8
|
+
// First SQL keyword → operation.
|
|
9
|
+
function opOf(sql) {
|
|
10
|
+
const m = /^\s*(select|insert|update|delete)\b/i.exec(sql);
|
|
11
|
+
return m ? OP_BY_KEYWORD[m[1].toLowerCase()] : undefined;
|
|
12
|
+
}
|
|
13
|
+
// The principal table, from the op-appropriate clause.
|
|
14
|
+
function tableOf(sql, op) {
|
|
15
|
+
let m = null;
|
|
16
|
+
if (op === 'select' || op === 'delete')
|
|
17
|
+
m = /\bfrom\s+"?([A-Za-z0-9_]+)"?/i.exec(sql);
|
|
18
|
+
else if (op === 'insert')
|
|
19
|
+
m = /\binto\s+"?([A-Za-z0-9_]+)"?/i.exec(sql);
|
|
20
|
+
else if (op === 'update')
|
|
21
|
+
m = /\bupdate\s+"?([A-Za-z0-9_]+)"?/i.exec(sql);
|
|
22
|
+
return m ? m[1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
// Column names referenced in the WHERE clause — the right-hand identifier of
|
|
25
|
+
// each `"table"."col"` (or a bare `"col"`). Names only; values are `?`.
|
|
26
|
+
function whereColumns(sql) {
|
|
27
|
+
const wIdx = sql.search(/\bwhere\b/i);
|
|
28
|
+
if (wIdx < 0)
|
|
29
|
+
return [];
|
|
30
|
+
const tail = sql.slice(wIdx + 5);
|
|
31
|
+
const cols = new Set();
|
|
32
|
+
const re = /"([A-Za-z0-9_]+)"(?:\s*\.\s*"([A-Za-z0-9_]+)")?/g;
|
|
33
|
+
let m;
|
|
34
|
+
while ((m = re.exec(tail)) !== null)
|
|
35
|
+
cols.add(m[2] ?? m[1]);
|
|
36
|
+
return [...cols].sort();
|
|
37
|
+
}
|
|
38
|
+
function extractPks(rows, pkCols) {
|
|
39
|
+
if (pkCols.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
const pks = [];
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
const vals = pkCols.map((c) => row[c]);
|
|
45
|
+
if (vals.some((v) => v === undefined))
|
|
46
|
+
continue; // column not selected — can't form a PK
|
|
47
|
+
const pk = vals.map((v) => String(v)).join(':');
|
|
48
|
+
if (!seen.has(pk)) {
|
|
49
|
+
seen.add(pk);
|
|
50
|
+
pks.push(pk);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return pks;
|
|
54
|
+
}
|
|
55
|
+
// Drizzle's own cache key: SHA-256(sql + '-' + JSON.stringify(params)) — see
|
|
56
|
+
// drizzle-orm/cache/core/cache.js. Reimplemented (not imported) so a query's
|
|
57
|
+
// hash computed here from `logger.logQuery`'s (sql, params) matches the hash
|
|
58
|
+
// Drizzle later passes to `cache.put()` for the SAME query, letting the two
|
|
59
|
+
// hooks correlate without ever importing 'drizzle-orm'.
|
|
60
|
+
async function hashQuery(sql, params) {
|
|
61
|
+
const data = `${sql}-${JSON.stringify(params)}`;
|
|
62
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(data));
|
|
63
|
+
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
64
|
+
}
|
|
65
|
+
// A pending SELECT footprint entry, keyed by query hash, waiting to be
|
|
66
|
+
// upgraded by a paired cache's put(). Linked to its logger via WeakMap so the
|
|
67
|
+
// map is GC'd along with the logger if the app never pairs a cache to it.
|
|
68
|
+
const pendingByLogger = new WeakMap();
|
|
69
|
+
// Creates a Drizzle-compatible logger that records footprints.
|
|
70
|
+
// Usage: drizzle(client, { logger: createDrizzleLogger() })
|
|
71
|
+
export function createDrizzleLogger(opts = {}) {
|
|
72
|
+
const captureWhereShape = opts.captureWhereShape !== false;
|
|
73
|
+
const forward = opts.forward;
|
|
74
|
+
const pending = new Map();
|
|
75
|
+
const logger = {
|
|
76
|
+
logQuery(query, params) {
|
|
77
|
+
try {
|
|
78
|
+
const ctx = contextStorage.getStore();
|
|
79
|
+
if (ctx) {
|
|
80
|
+
const op = opOf(query);
|
|
81
|
+
const table = op ? tableOf(query, op) : undefined;
|
|
82
|
+
if (op && table) {
|
|
83
|
+
const where = captureWhereShape ? whereColumns(query).join(',') || undefined : undefined;
|
|
84
|
+
const entry = {
|
|
85
|
+
model: table,
|
|
86
|
+
op,
|
|
87
|
+
pks: [],
|
|
88
|
+
where_shape: where,
|
|
89
|
+
count: 0,
|
|
90
|
+
note: 'drizzle: shape-only (no result access — PKs/count unavailable)',
|
|
91
|
+
};
|
|
92
|
+
ctx.footprint.push(entry);
|
|
93
|
+
// A paired cache can upgrade this SAME entry once the real rows
|
|
94
|
+
// are known — see createDrizzleCache(). Mutations get nothing
|
|
95
|
+
// more from Drizzle's cache hook, so they stay as pushed above.
|
|
96
|
+
if (op === 'select') {
|
|
97
|
+
hashQuery(query, params)
|
|
98
|
+
.then((h) => pending.set(h, entry))
|
|
99
|
+
.catch(() => { });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { /* never break a query */ }
|
|
105
|
+
try {
|
|
106
|
+
forward?.logQuery(query, params);
|
|
107
|
+
}
|
|
108
|
+
catch { /* ignore */ }
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
pendingByLogger.set(logger, pending);
|
|
112
|
+
return logger;
|
|
113
|
+
}
|
|
114
|
+
// node-postgres (`pg`) QueryResult vs. mysql2's `[rows, fields]` tuple — the
|
|
115
|
+
// two response shapes `cache.put()` actually sees for Reprova's two real
|
|
116
|
+
// customer-database dialects (Postgres, MySQL; see agent/internal/dialect).
|
|
117
|
+
// Unrecognized shapes degrade to "no rows found" rather than guessing.
|
|
118
|
+
function rowsFromResponse(response) {
|
|
119
|
+
if (Array.isArray(response)) {
|
|
120
|
+
const [rows] = response; // mysql2: client.query() resolves [rows, fields]
|
|
121
|
+
return Array.isArray(rows) ? rows : undefined;
|
|
122
|
+
}
|
|
123
|
+
const rows = response?.rows;
|
|
124
|
+
return Array.isArray(rows) ? rows : undefined;
|
|
125
|
+
}
|
|
126
|
+
// Creates a Drizzle Cache that observes (never serves) query results to
|
|
127
|
+
// upgrade SELECT footprints with exact primary keys and row counts.
|
|
128
|
+
// Usage: drizzle(client, { cache: createDrizzleCache({ logger }) })
|
|
129
|
+
export function createDrizzleCache(opts = {}) {
|
|
130
|
+
const primaryKey = opts.primaryKey ?? (() => ['id']);
|
|
131
|
+
const pending = opts.logger ? pendingByLogger.get(opts.logger) : undefined;
|
|
132
|
+
return {
|
|
133
|
+
// 'all' — every SELECT is observed automatically; apps never need to
|
|
134
|
+
// call Drizzle's own `.$withCache()` for this to work.
|
|
135
|
+
strategy() {
|
|
136
|
+
return 'all';
|
|
137
|
+
},
|
|
138
|
+
// Always a miss: this cache exists to OBSERVE results, never to REPLACE a
|
|
139
|
+
// real query with a stale one.
|
|
140
|
+
async get() {
|
|
141
|
+
return undefined;
|
|
142
|
+
},
|
|
143
|
+
async put(hashedQuery, response, tables) {
|
|
144
|
+
try {
|
|
145
|
+
if (!tables || tables.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
const rows = rowsFromResponse(response);
|
|
148
|
+
if (!rows)
|
|
149
|
+
return;
|
|
150
|
+
const primary = tables[0];
|
|
151
|
+
const pks = extractPks(rows, primaryKey(primary));
|
|
152
|
+
const count = rows.length;
|
|
153
|
+
const note = count === 0 ? 'returned 0 rows' : undefined;
|
|
154
|
+
const linked = pending?.get(hashedQuery);
|
|
155
|
+
const ctx = contextStorage.getStore();
|
|
156
|
+
if (linked) {
|
|
157
|
+
pending.delete(hashedQuery);
|
|
158
|
+
linked.model = primary;
|
|
159
|
+
linked.pks = pks;
|
|
160
|
+
linked.count = count;
|
|
161
|
+
linked.note = note;
|
|
162
|
+
}
|
|
163
|
+
else if (ctx) {
|
|
164
|
+
ctx.footprint.push({ model: primary, op: 'select', pks, count, note });
|
|
165
|
+
}
|
|
166
|
+
// Joined/subquery tables: recorded as referenced, without PKs — same
|
|
167
|
+
// convention as the Kysely adapter (result columns can't be reliably
|
|
168
|
+
// attributed to a joined table's own primary key).
|
|
169
|
+
if (ctx) {
|
|
170
|
+
for (const t of tables.slice(1)) {
|
|
171
|
+
ctx.footprint.push({ model: t, op: 'select', pks: [], count: 0, note: 'referenced (join/subquery)' });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch { /* never break a query */ }
|
|
176
|
+
},
|
|
177
|
+
// Drizzle's mutation hook gets table names only — no rows, no count, and
|
|
178
|
+
// it races the real query (Promise.all internally). Nothing usable to
|
|
179
|
+
// record here; mutations are already captured (shape-only) by the paired
|
|
180
|
+
// logger.
|
|
181
|
+
async onMutate() { },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Convenience: builds a linked logger+cache pair in one call, the
|
|
185
|
+
// recommended way to get exact SELECT pks/count without double-counting.
|
|
186
|
+
// Usage:
|
|
187
|
+
// const { logger, cache } = createDrizzleFootprint();
|
|
188
|
+
// const db = drizzle(client, { logger, cache });
|
|
189
|
+
export function createDrizzleFootprint(opts = {}) {
|
|
190
|
+
const logger = createDrizzleLogger({ captureWhereShape: opts.captureWhereShape });
|
|
191
|
+
const cache = createDrizzleCache({ primaryKey: opts.primaryKey, logger });
|
|
192
|
+
return { logger, cache };
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=drizzle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
export { Reprova, wrapResponse } from './sdk.js';
|
|
2
|
-
export { contextStorage, currentContext } from './context.js';
|
|
1
|
+
export { Reprova, wrapResponse, forwardAsyncErrors, replayErrorHandler } from './sdk.js';
|
|
2
|
+
export { contextStorage, currentContext, setAuthClaims } from './context.js';
|
|
3
3
|
export { createPrismaExtension } from './prisma.js';
|
|
4
|
+
export { createKyselyPlugin } from './kysely.js';
|
|
5
|
+
export type { KyselyPluginOptions, KyselyFootprintPlugin } from './kysely.js';
|
|
6
|
+
export { installSequelizeHooks } from './sequelize.js';
|
|
7
|
+
export type { SequelizeAdapterOptions } from './sequelize.js';
|
|
8
|
+
export { installTypeOrmSubscriber } from './typeorm.js';
|
|
9
|
+
export { installKnexHooks } from './knex.js';
|
|
10
|
+
export type { KnexAdapterOptions } from './knex.js';
|
|
11
|
+
export { createNestExceptionFilter } from './nest.js';
|
|
12
|
+
export type { ReprovaNestFilter, NestExceptionFilterOptions, ArgumentsHostLike, NestAppLike } from './nest.js';
|
|
13
|
+
export { createDrizzleLogger, createDrizzleCache, createDrizzleFootprint } from './drizzle.js';
|
|
14
|
+
export type { DrizzleLoggerOptions, DrizzleLogger, DrizzleCache, DrizzleCacheOptions, DrizzleFootprintOptions, } from './drizzle.js';
|
|
4
15
|
export type { ReprovaOptions } from './sdk.js';
|
|
5
16
|
export type { ReprovaContext, FootprintQuery, OutboundCall, RequestSnapshot } from './context.js';
|
|
6
17
|
export * from './proto.gen.js';
|