@reprova/sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/context.d.ts +35 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/prisma.d.ts +33 -0
- package/dist/prisma.d.ts.map +1 -0
- package/dist/prisma.js +131 -0
- package/dist/prisma.js.map +1 -0
- package/dist/proto.gen.d.ts +117 -0
- package/dist/proto.gen.d.ts.map +1 -0
- package/dist/proto.gen.js +4 -0
- package/dist/proto.gen.js.map +1 -0
- package/dist/sdk.d.ts +40 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +184 -0
- package/dist/sdk.js.map +1 -0
- package/dist/shape.d.ts +3 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +42 -0
- package/dist/shape.js.map +1 -0
- package/dist/stack.d.ts +5 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/stack.js +43 -0
- package/dist/stack.js.map +1 -0
- package/dist/transport.d.ts +25 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +67 -0
- package/dist/transport.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denis Ssamba
|
|
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/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @reprova/sdk
|
|
2
|
+
|
|
3
|
+
Node.js/TypeScript SDK: Express error hook, HTTP 5xx hook, a Prisma client extension
|
|
4
|
+
that records each request's data footprint (which tables/rows it touched) via
|
|
5
|
+
`AsyncLocalStorage`, outbound HTTP call recording, and a batched, fire-and-forget
|
|
6
|
+
transport to the control plane — capturing never adds meaningful latency to your
|
|
7
|
+
requests, even when the control plane is unreachable (`transport.test.ts` proves this;
|
|
8
|
+
`chaos.spec.ts` in the integration suite proves it against the real, running control
|
|
9
|
+
plane too).
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```ts
|
|
13
|
+
import { Reprova, createPrismaExtension } from '@reprova/sdk';
|
|
14
|
+
|
|
15
|
+
const sdk = Reprova.init({ dsn: process.env.REPROVA_DSN, release: gitSha });
|
|
16
|
+
app.use(sdk.requestHandler());
|
|
17
|
+
// ... your routes ...
|
|
18
|
+
app.use(sdk.errorHandler());
|
|
19
|
+
|
|
20
|
+
const prisma = new PrismaClient().$extends(createPrismaExtension(Prisma.dmmf));
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Every capture carries a W3C `trace_id`, the git `release` sha, and the latest applied
|
|
24
|
+
Prisma `migration_id` — non-negotiable schema fields the rest of the pipeline depends on.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
```bash
|
|
28
|
+
npm run build
|
|
29
|
+
npm run lint
|
|
30
|
+
npm test
|
|
31
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
export interface RequestSnapshot {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
body: string;
|
|
7
|
+
authClaims?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface FootprintQuery {
|
|
10
|
+
model: string;
|
|
11
|
+
op: string;
|
|
12
|
+
pks: string[];
|
|
13
|
+
where_shape?: string;
|
|
14
|
+
count: number;
|
|
15
|
+
note?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface OutboundCall {
|
|
18
|
+
host: string;
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
status: number;
|
|
22
|
+
response_body?: string;
|
|
23
|
+
recorded_for_replay: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface ReprovaContext {
|
|
26
|
+
traceId: string;
|
|
27
|
+
capturedAt: Date;
|
|
28
|
+
request: RequestSnapshot;
|
|
29
|
+
footprint: FootprintQuery[];
|
|
30
|
+
outboundCalls: OutboundCall[];
|
|
31
|
+
captured?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare const contextStorage: AsyncLocalStorage<ReprovaContext>;
|
|
34
|
+
export declare function currentContext(): ReprovaContext | undefined;
|
|
35
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +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;CAC9B;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;CACpB;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAEtE,wBAAgB,cAAc,IAAI,cAAc,GAAG,SAAS,CAE3D"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqChD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAEtE,MAAM,UAAU,cAAc;IAC5B,OAAO,cAAc,CAAC,QAAQ,EAAE,CAAC;AACnC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Reprova, wrapResponse } from './sdk.js';
|
|
2
|
+
export { contextStorage, currentContext } from './context.js';
|
|
3
|
+
export { createPrismaExtension } from './prisma.js';
|
|
4
|
+
export type { ReprovaOptions } from './sdk.js';
|
|
5
|
+
export type { ReprovaContext, FootprintQuery, OutboundCall, RequestSnapshot } from './context.js';
|
|
6
|
+
export * from './proto.gen.js';
|
|
7
|
+
export declare const SDK_VERSION = "0.1.0";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClG,cAAc,gBAAgB,CAAC;AAE/B,eAAO,MAAM,WAAW,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Reprova, wrapResponse } from './sdk.js';
|
|
2
|
+
export { contextStorage, currentContext } from './context.js';
|
|
3
|
+
export { createPrismaExtension } from './prisma.js';
|
|
4
|
+
export * from './proto.gen.js';
|
|
5
|
+
export const SDK_VERSION = '0.1.0';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpD,cAAc,gBAAgB,CAAC;AAE/B,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC"}
|
package/dist/prisma.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface DMMFField {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: string;
|
|
4
|
+
type: string;
|
|
5
|
+
isId: boolean;
|
|
6
|
+
isList: boolean;
|
|
7
|
+
relationName?: string;
|
|
8
|
+
}
|
|
9
|
+
interface DMMFModel {
|
|
10
|
+
name: string;
|
|
11
|
+
fields: DMMFField[];
|
|
12
|
+
primaryKey?: {
|
|
13
|
+
fields: string[];
|
|
14
|
+
} | null;
|
|
15
|
+
}
|
|
16
|
+
interface DMMF {
|
|
17
|
+
datamodel: {
|
|
18
|
+
models: DMMFModel[];
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function createPrismaExtension(dmmf: DMMF): {
|
|
22
|
+
name: string;
|
|
23
|
+
query: {
|
|
24
|
+
$allOperations({ model, operation, args, query }: {
|
|
25
|
+
model: string | undefined;
|
|
26
|
+
operation: string;
|
|
27
|
+
args: Record<string, unknown>;
|
|
28
|
+
query: (args: Record<string, unknown>) => Promise<unknown>;
|
|
29
|
+
}): Promise<unknown>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"AAKA,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AACD,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CAC1C;AACD,UAAU,IAAI;IACZ,SAAS,EAAE;QAAE,MAAM,EAAE,SAAS,EAAE,CAAA;KAAE,CAAC;CACpC;AAoFD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI;;;0DAIQ;YAChD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;YAC1B,SAAS,EAAE,MAAM,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC9B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SAC5D;;EA+CN"}
|
package/dist/prisma.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { contextStorage } from './context.js';
|
|
2
|
+
import { shapeArgs } from './shape.js';
|
|
3
|
+
function getPkFields(dmmf, modelName) {
|
|
4
|
+
const model = dmmf.datamodel.models.find(m => m.name === modelName);
|
|
5
|
+
if (!model)
|
|
6
|
+
return ['id'];
|
|
7
|
+
// Composite PK
|
|
8
|
+
if (model.primaryKey?.fields?.length)
|
|
9
|
+
return model.primaryKey.fields;
|
|
10
|
+
// Single @id field
|
|
11
|
+
const idField = model.fields.find(f => f.isId);
|
|
12
|
+
return idField ? [idField.name] : ['id'];
|
|
13
|
+
}
|
|
14
|
+
function getRelationFields(dmmf, modelName) {
|
|
15
|
+
const model = dmmf.datamodel.models.find(m => m.name === modelName);
|
|
16
|
+
if (!model)
|
|
17
|
+
return [];
|
|
18
|
+
return model.fields.filter(f => f.kind === 'object' && !!f.relationName);
|
|
19
|
+
}
|
|
20
|
+
function extractFromRow(row, pkFields) {
|
|
21
|
+
const vals = pkFields.map(f => row[f]);
|
|
22
|
+
if (vals.some(v => v === undefined))
|
|
23
|
+
return null;
|
|
24
|
+
return vals.map(v => String(v)).join(':');
|
|
25
|
+
}
|
|
26
|
+
function extractPks(result, pkFields) {
|
|
27
|
+
if (result === null || result === undefined)
|
|
28
|
+
return [];
|
|
29
|
+
if (Array.isArray(result)) {
|
|
30
|
+
const pks = [];
|
|
31
|
+
for (const item of result) {
|
|
32
|
+
if (item && typeof item === 'object') {
|
|
33
|
+
const pk = extractFromRow(item, pkFields);
|
|
34
|
+
if (pk !== null)
|
|
35
|
+
pks.push(pk);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return pks;
|
|
39
|
+
}
|
|
40
|
+
if (typeof result === 'object') {
|
|
41
|
+
const pk = extractFromRow(result, pkFields);
|
|
42
|
+
return pk !== null ? [pk] : [];
|
|
43
|
+
}
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
// Walk one level of included relations on the result (single object or array of
|
|
47
|
+
// objects) and produce one footprint entry per relation field that was actually
|
|
48
|
+
// present in the result (i.e. included in the query).
|
|
49
|
+
function extractNestedFootprints(dmmf, modelName, result) {
|
|
50
|
+
if (!dmmf)
|
|
51
|
+
return [];
|
|
52
|
+
const rows = Array.isArray(result)
|
|
53
|
+
? result.filter(r => r && typeof r === 'object')
|
|
54
|
+
: result && typeof result === 'object'
|
|
55
|
+
? [result]
|
|
56
|
+
: [];
|
|
57
|
+
if (rows.length === 0)
|
|
58
|
+
return [];
|
|
59
|
+
const entries = [];
|
|
60
|
+
for (const field of getRelationFields(dmmf, modelName)) {
|
|
61
|
+
if (!rows.some(row => field.name in row))
|
|
62
|
+
continue; // relation wasn't included
|
|
63
|
+
const nestedPkFields = getPkFields(dmmf, field.type);
|
|
64
|
+
const nestedItems = [];
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
const val = row[field.name];
|
|
67
|
+
if (Array.isArray(val))
|
|
68
|
+
nestedItems.push(...val);
|
|
69
|
+
else if (val !== null && val !== undefined)
|
|
70
|
+
nestedItems.push(val);
|
|
71
|
+
}
|
|
72
|
+
const pks = extractPks(field.isList ? nestedItems : nestedItems[0] ?? null, nestedPkFields);
|
|
73
|
+
entries.push({
|
|
74
|
+
model: field.type,
|
|
75
|
+
op: field.isList ? 'findMany' : 'findUnique',
|
|
76
|
+
pks,
|
|
77
|
+
count: nestedItems.length,
|
|
78
|
+
note: nestedItems.length === 0 ? 'returned 0 rows' : undefined,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return entries;
|
|
82
|
+
}
|
|
83
|
+
// Creates the Reprova Prisma client extension.
|
|
84
|
+
// Usage: const prisma = new PrismaClient().$extends(reprova.prismaExtension())
|
|
85
|
+
export function createPrismaExtension(dmmf) {
|
|
86
|
+
return {
|
|
87
|
+
name: 'reprova-footprint',
|
|
88
|
+
query: {
|
|
89
|
+
$allOperations({ model, operation, args, query }) {
|
|
90
|
+
const ctx = contextStorage.getStore();
|
|
91
|
+
const start = performance.now();
|
|
92
|
+
return query(args).then((result) => {
|
|
93
|
+
if (!ctx)
|
|
94
|
+
return result;
|
|
95
|
+
const elapsed = performance.now() - start;
|
|
96
|
+
void elapsed; // used for bench
|
|
97
|
+
// Handle raw queries
|
|
98
|
+
if (model === undefined || operation === '$queryRaw' || operation === '$executeRaw') {
|
|
99
|
+
const sqlText = Array.isArray(args) ? String(args[0]) : '[raw]';
|
|
100
|
+
ctx.footprint.push({
|
|
101
|
+
model: '$raw',
|
|
102
|
+
op: operation,
|
|
103
|
+
pks: [],
|
|
104
|
+
count: 0,
|
|
105
|
+
note: 'raw query — partial coverage',
|
|
106
|
+
where_shape: sqlText.replace(/\$\d+/g, '?'),
|
|
107
|
+
});
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
const pkFields = dmmf ? getPkFields(dmmf, model) : ['id'];
|
|
111
|
+
const pks = extractPks(result, pkFields);
|
|
112
|
+
const count = Array.isArray(result) ? result.length : (result !== null && result !== undefined ? 1 : 0);
|
|
113
|
+
const whereShape = args.where ? shapeArgs(args.where) : undefined;
|
|
114
|
+
ctx.footprint.push({
|
|
115
|
+
model,
|
|
116
|
+
op: operation,
|
|
117
|
+
pks,
|
|
118
|
+
where_shape: whereShape,
|
|
119
|
+
count,
|
|
120
|
+
note: count === 0 ? 'returned 0 rows' : undefined,
|
|
121
|
+
});
|
|
122
|
+
for (const nested of extractNestedFootprints(dmmf, model, result)) {
|
|
123
|
+
ctx.footprint.push(nested);
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export interface CapturePayload {
|
|
2
|
+
trigger: 'unhandled_exception' | 'http_5xx' | 'manual_capture' | 'orm_error' | 'job_failure';
|
|
3
|
+
trace_id: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
release: string;
|
|
6
|
+
migration_id: string;
|
|
7
|
+
sdk_version: string;
|
|
8
|
+
error: {
|
|
9
|
+
type: string;
|
|
10
|
+
message: string;
|
|
11
|
+
stack: Array<string>;
|
|
12
|
+
};
|
|
13
|
+
request: {
|
|
14
|
+
method: string;
|
|
15
|
+
path: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
body: string;
|
|
18
|
+
auth_claims?: {};
|
|
19
|
+
};
|
|
20
|
+
response?: {
|
|
21
|
+
status: number;
|
|
22
|
+
body: string;
|
|
23
|
+
};
|
|
24
|
+
data_footprint: {
|
|
25
|
+
queries: Array<{
|
|
26
|
+
model: string;
|
|
27
|
+
op: string;
|
|
28
|
+
pks: Array<string>;
|
|
29
|
+
where_shape?: string;
|
|
30
|
+
count: number;
|
|
31
|
+
note?: string;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
outbound_calls: Array<{
|
|
35
|
+
host: string;
|
|
36
|
+
method: string;
|
|
37
|
+
path: string;
|
|
38
|
+
status: number;
|
|
39
|
+
response_body?: string;
|
|
40
|
+
recorded_for_replay: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export interface Issue {
|
|
44
|
+
id: string;
|
|
45
|
+
fingerprint: string;
|
|
46
|
+
title: string;
|
|
47
|
+
status: 'unresolved' | 'resolved' | 'ignored' | 'regressed';
|
|
48
|
+
first_seen: string;
|
|
49
|
+
last_seen: string;
|
|
50
|
+
occurrence_count: number;
|
|
51
|
+
users_affected: number;
|
|
52
|
+
first_release: string;
|
|
53
|
+
}
|
|
54
|
+
export interface JobPlan {
|
|
55
|
+
job_id: string;
|
|
56
|
+
issue_id: string;
|
|
57
|
+
created_at: string;
|
|
58
|
+
expires_at: string;
|
|
59
|
+
seed_rows: Record<string, Array<string>>;
|
|
60
|
+
walk_rules: {
|
|
61
|
+
max_rows: number;
|
|
62
|
+
max_depth: number;
|
|
63
|
+
exclude_tables: Array<string>;
|
|
64
|
+
always_include_tables: Array<string>;
|
|
65
|
+
};
|
|
66
|
+
anonymization_ruleset_id: string;
|
|
67
|
+
migration_id: string;
|
|
68
|
+
recipient_age_pubkey: string;
|
|
69
|
+
}
|
|
70
|
+
export interface Manifest {
|
|
71
|
+
package_id: string;
|
|
72
|
+
job_id: string;
|
|
73
|
+
issue_id: string;
|
|
74
|
+
migration_id: string;
|
|
75
|
+
created_at: string;
|
|
76
|
+
anonymization_ruleset_id: string;
|
|
77
|
+
table_order: Array<string>;
|
|
78
|
+
row_counts: Record<string, number>;
|
|
79
|
+
row_count: number;
|
|
80
|
+
tables_count: number;
|
|
81
|
+
masked_columns_count: number;
|
|
82
|
+
deferred_constraints: Array<{
|
|
83
|
+
table: string;
|
|
84
|
+
constraint_name: string;
|
|
85
|
+
columns: Array<string>;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
export interface PackageReceipt {
|
|
89
|
+
job_id: string;
|
|
90
|
+
package_id: string;
|
|
91
|
+
sha256: string;
|
|
92
|
+
size_bytes: number;
|
|
93
|
+
row_count: number;
|
|
94
|
+
tables_count: number;
|
|
95
|
+
masked_columns_count: number;
|
|
96
|
+
staleness_seconds: number;
|
|
97
|
+
download_path: string;
|
|
98
|
+
created_at: string;
|
|
99
|
+
}
|
|
100
|
+
export interface ReplayVerdict {
|
|
101
|
+
issue_id: string;
|
|
102
|
+
result: 'exact_match' | 'different_failure' | 'no_reproduction';
|
|
103
|
+
replay_error?: {
|
|
104
|
+
type: string;
|
|
105
|
+
message: string;
|
|
106
|
+
top_frame: string;
|
|
107
|
+
};
|
|
108
|
+
local_release: string;
|
|
109
|
+
ran_by: string;
|
|
110
|
+
ran_at: string;
|
|
111
|
+
}
|
|
112
|
+
export interface SignedJobPlan {
|
|
113
|
+
plan: string;
|
|
114
|
+
ed25519_signature: string;
|
|
115
|
+
control_plane_key_id: string;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=proto.gen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proto.gen.d.ts","sourceRoot":"","sources":["../src/proto.gen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proto.gen.js","sourceRoot":"","sources":["../src/proto.gen.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,+BAA+B"}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Response, RequestHandler, ErrorRequestHandler } from 'express';
|
|
2
|
+
import { type ReprovaContext } from './context.js';
|
|
3
|
+
export interface ReprovaOptions {
|
|
4
|
+
dsn: string;
|
|
5
|
+
release: string;
|
|
6
|
+
environment?: string;
|
|
7
|
+
recordOutbound?: {
|
|
8
|
+
allowHosts?: string[];
|
|
9
|
+
denyHosts?: string[];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare class Reprova {
|
|
13
|
+
private readonly opts;
|
|
14
|
+
private readonly transport;
|
|
15
|
+
readonly release: string;
|
|
16
|
+
readonly sdkVersion = "0.1.0";
|
|
17
|
+
private migrationId;
|
|
18
|
+
private readonly outboundOpts;
|
|
19
|
+
constructor(opts: ReprovaOptions);
|
|
20
|
+
static init(opts: ReprovaOptions): Reprova;
|
|
21
|
+
static getInstance(): Reprova | null;
|
|
22
|
+
setMigrationId(id: string): void;
|
|
23
|
+
requestHandler(): RequestHandler;
|
|
24
|
+
errorHandler(): ErrorRequestHandler;
|
|
25
|
+
captureError(err: Error, opts?: {
|
|
26
|
+
trigger?: string;
|
|
27
|
+
}): void;
|
|
28
|
+
captureHttp5xx(err: Error | null, statusCode: number, responseBody: string): void;
|
|
29
|
+
capture(trigger: string, err: Error, ctx: ReprovaContext | undefined, extra?: {
|
|
30
|
+
response?: {
|
|
31
|
+
status: number;
|
|
32
|
+
body: string;
|
|
33
|
+
};
|
|
34
|
+
}): void;
|
|
35
|
+
attachProcessHandlers(): void;
|
|
36
|
+
private patchOutbound;
|
|
37
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export declare function wrapResponse(res: Response, sdk: Reprova): void;
|
|
40
|
+
//# sourceMappingURL=sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAgB,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAKnE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE;QACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACH;AAID,qBAAa,OAAO;IAON,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,WAAW;IAC9B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;gBAEnC,IAAI,EAAE,cAAc;IAWjD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO;IAM1C,MAAM,CAAC,WAAW,IAAI,OAAO,GAAG,IAAI;IAEpC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAGhC,cAAc,IAAI,cAAc;IAqBhC,YAAY,IAAI,mBAAmB;IAWnC,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAM3D,cAAc,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAajF,OAAO,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,KAAK,EACV,GAAG,EAAE,cAAc,GAAG,SAAS,EAC/B,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GACtD,IAAI;IAgCP,qBAAqB,IAAI,IAAI;IAe7B,OAAO,CAAC,aAAa;IA6Cf,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7C;AAGD,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAS9D"}
|
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { contextStorage } from './context.js';
|
|
2
|
+
import { BatchTransport } from './transport.js';
|
|
3
|
+
import { parseStack, generateTraceId, stripHeaders, captureHeaders } from './stack.js';
|
|
4
|
+
let instance = null;
|
|
5
|
+
export class Reprova {
|
|
6
|
+
opts;
|
|
7
|
+
transport;
|
|
8
|
+
release;
|
|
9
|
+
sdkVersion = '0.1.0';
|
|
10
|
+
migrationId = 'unknown';
|
|
11
|
+
outboundOpts;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.opts = opts;
|
|
14
|
+
const url = new URL(opts.dsn);
|
|
15
|
+
const apiKey = url.pathname.slice(1); // strip leading slash
|
|
16
|
+
const endpoint = `${url.protocol}//${url.host}/v1/ingest`;
|
|
17
|
+
this.transport = new BatchTransport({ endpoint, apiKey });
|
|
18
|
+
this.transport.start();
|
|
19
|
+
this.release = opts.release;
|
|
20
|
+
this.outboundOpts = opts.recordOutbound;
|
|
21
|
+
}
|
|
22
|
+
static init(opts) {
|
|
23
|
+
instance = new Reprova(opts);
|
|
24
|
+
instance.patchOutbound();
|
|
25
|
+
return instance;
|
|
26
|
+
}
|
|
27
|
+
static getInstance() { return instance; }
|
|
28
|
+
setMigrationId(id) { this.migrationId = id; }
|
|
29
|
+
// Express middleware: opens per-request context.
|
|
30
|
+
requestHandler() {
|
|
31
|
+
return (req, _res, next) => {
|
|
32
|
+
const traceId = req.headers['traceparent'] ?? generateTraceId();
|
|
33
|
+
const ctx = {
|
|
34
|
+
traceId,
|
|
35
|
+
capturedAt: new Date(),
|
|
36
|
+
request: {
|
|
37
|
+
method: req.method,
|
|
38
|
+
path: req.path,
|
|
39
|
+
headers: captureHeaders(req.headers),
|
|
40
|
+
body: typeof req.body === 'string' ? req.body : JSON.stringify(req.body ?? ''),
|
|
41
|
+
authClaims: req['auth'],
|
|
42
|
+
},
|
|
43
|
+
footprint: [],
|
|
44
|
+
outboundCalls: [],
|
|
45
|
+
};
|
|
46
|
+
contextStorage.run(ctx, next);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Express error middleware: captures unhandled_exception.
|
|
50
|
+
errorHandler() {
|
|
51
|
+
return (err, req, res, next) => {
|
|
52
|
+
const ctx = contextStorage.getStore();
|
|
53
|
+
if (ctx && err instanceof Error) {
|
|
54
|
+
this.capture('unhandled_exception', err, ctx);
|
|
55
|
+
}
|
|
56
|
+
next(err);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Manual capture.
|
|
60
|
+
captureError(err, opts) {
|
|
61
|
+
const ctx = contextStorage.getStore();
|
|
62
|
+
this.capture(opts?.trigger ?? 'manual_capture', err, ctx);
|
|
63
|
+
}
|
|
64
|
+
// Called by response hook for http_5xx (no thrown error).
|
|
65
|
+
captureHttp5xx(err, statusCode, responseBody) {
|
|
66
|
+
const ctx = contextStorage.getStore();
|
|
67
|
+
if (!ctx)
|
|
68
|
+
return;
|
|
69
|
+
// Skip: an error was already thrown and captured for this request (e.g. via
|
|
70
|
+
// errorHandler/captureError) — this 5xx response is just that error surfacing,
|
|
71
|
+
// not an independent silent failure. Reporting both would double-count the issue.
|
|
72
|
+
if (ctx.captured)
|
|
73
|
+
return;
|
|
74
|
+
const syntheticErr = err ?? new Error(`HTTP ${statusCode} response`);
|
|
75
|
+
this.capture('http_5xx', syntheticErr, ctx, {
|
|
76
|
+
response: { status: statusCode, body: responseBody.slice(0, 16 * 1024) },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
capture(trigger, err, ctx, extra) {
|
|
80
|
+
if (ctx)
|
|
81
|
+
ctx.captured = true;
|
|
82
|
+
const payload = {
|
|
83
|
+
trigger: trigger,
|
|
84
|
+
trace_id: ctx?.traceId ?? generateTraceId(),
|
|
85
|
+
timestamp: (ctx?.capturedAt ?? new Date()).toISOString(),
|
|
86
|
+
release: this.release,
|
|
87
|
+
migration_id: this.migrationId,
|
|
88
|
+
sdk_version: this.sdkVersion,
|
|
89
|
+
error: {
|
|
90
|
+
type: err.constructor?.name ?? err.name ?? 'Error',
|
|
91
|
+
message: err.message,
|
|
92
|
+
stack: parseStack(err),
|
|
93
|
+
},
|
|
94
|
+
request: ctx
|
|
95
|
+
? {
|
|
96
|
+
method: ctx.request.method,
|
|
97
|
+
path: ctx.request.path,
|
|
98
|
+
headers: stripHeaders(ctx.request.headers),
|
|
99
|
+
body: ctx.request.body,
|
|
100
|
+
auth_claims: ctx.request.authClaims,
|
|
101
|
+
}
|
|
102
|
+
: { method: '', path: '', headers: {}, body: '' },
|
|
103
|
+
response: extra?.response,
|
|
104
|
+
data_footprint: { queries: ctx?.footprint ?? [] },
|
|
105
|
+
outbound_calls: ctx?.outboundCalls ?? [],
|
|
106
|
+
};
|
|
107
|
+
this.transport.enqueue(payload);
|
|
108
|
+
}
|
|
109
|
+
// Attach process-level handlers.
|
|
110
|
+
attachProcessHandlers() {
|
|
111
|
+
process.on('uncaughtException', (err) => {
|
|
112
|
+
const ctx = contextStorage.getStore();
|
|
113
|
+
this.capture('unhandled_exception', err, ctx);
|
|
114
|
+
// Allow flush before exit.
|
|
115
|
+
void this.transport.flush().finally(() => process.exit(1));
|
|
116
|
+
});
|
|
117
|
+
process.on('unhandledRejection', (reason) => {
|
|
118
|
+
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
119
|
+
const ctx = contextStorage.getStore();
|
|
120
|
+
this.capture('unhandled_exception', err, ctx);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
patchOutbound() {
|
|
124
|
+
const sdk = this;
|
|
125
|
+
const allowHosts = this.outboundOpts?.allowHosts;
|
|
126
|
+
const denyHosts = this.outboundOpts?.denyHosts;
|
|
127
|
+
function shouldRecord(host) {
|
|
128
|
+
if (denyHosts?.some(h => host.includes(h)))
|
|
129
|
+
return false;
|
|
130
|
+
if (allowHosts && !allowHosts.some(h => host.includes(h)))
|
|
131
|
+
return false;
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
// Patch global fetch.
|
|
135
|
+
const originalFetch = globalThis.fetch;
|
|
136
|
+
if (typeof originalFetch === 'function') {
|
|
137
|
+
globalThis.fetch = async function patchedFetch(input, init) {
|
|
138
|
+
const res = await originalFetch(input, init);
|
|
139
|
+
const ctx = contextStorage.getStore();
|
|
140
|
+
if (ctx) {
|
|
141
|
+
const url = typeof input === 'string' ? new URL(input) : input instanceof URL ? input : new URL(input.url);
|
|
142
|
+
const host = url.host;
|
|
143
|
+
if (shouldRecord(host)) {
|
|
144
|
+
const ct = res.headers.get('content-type') ?? '';
|
|
145
|
+
let responseBody;
|
|
146
|
+
if (ct.includes('json') || ct.includes('text')) {
|
|
147
|
+
try {
|
|
148
|
+
const clone = res.clone();
|
|
149
|
+
const text = await clone.text();
|
|
150
|
+
responseBody = text.slice(0, 64 * 1024);
|
|
151
|
+
}
|
|
152
|
+
catch { /* ignore */ }
|
|
153
|
+
}
|
|
154
|
+
ctx.outboundCalls.push({
|
|
155
|
+
host,
|
|
156
|
+
method: (init?.method ?? 'GET').toUpperCase(),
|
|
157
|
+
path: url.pathname + url.search,
|
|
158
|
+
status: res.status,
|
|
159
|
+
response_body: responseBody,
|
|
160
|
+
recorded_for_replay: true,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return res;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async [Symbol.asyncDispose]() {
|
|
169
|
+
this.transport.stop();
|
|
170
|
+
await this.transport.flush();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Auto-setup http_5xx hook for Express responses.
|
|
174
|
+
export function wrapResponse(res, sdk) {
|
|
175
|
+
const originalJson = res.json.bind(res);
|
|
176
|
+
res.json = function (body) {
|
|
177
|
+
if (res.statusCode >= 500) {
|
|
178
|
+
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
|
|
179
|
+
sdk.captureHttp5xx(null, res.statusCode, bodyStr.slice(0, 16 * 1024));
|
|
180
|
+
}
|
|
181
|
+
return originalJson(body);
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=sdk.js.map
|
package/dist/sdk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAuB,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAavF,IAAI,QAAQ,GAAmB,IAAI,CAAC;AAEpC,MAAM,OAAO,OAAO;IAOW;IANZ,SAAS,CAAiB;IAClC,OAAO,CAAS;IAChB,UAAU,GAAG,OAAO,CAAC;IACtB,WAAW,GAAG,SAAS,CAAC;IACf,YAAY,CAAmC;IAEhE,YAA6B,IAAoB;QAApB,SAAI,GAAJ,IAAI,CAAgB;QAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB;QAC5D,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,YAAY,CAAC;QAE1D,IAAI,CAAC,SAAS,GAAG,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC;IAC1C,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,IAAoB;QAC9B,QAAQ,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,WAAW,KAAqB,OAAO,QAAQ,CAAC,CAAC,CAAC;IAEzD,cAAc,CAAC,EAAU,IAAU,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC,CAAC;IAE3D,iDAAiD;IACjD,cAAc;QACZ,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;YAC1D,MAAM,OAAO,GAAI,GAAG,CAAC,OAAO,CAAC,aAAa,CAAwB,IAAI,eAAe,EAAE,CAAC;YACxF,MAAM,GAAG,GAAmB;gBAC1B,OAAO;gBACP,UAAU,EAAE,IAAI,IAAI,EAAE;gBACtB,OAAO,EAAE;oBACP,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,OAAwD,CAAC;oBACrF,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;oBAC9E,UAAU,EAAG,GAA0C,CAAC,MAAM,CAAwC;iBACvG;gBACD,SAAS,EAAE,EAAE;gBACb,aAAa,EAAE,EAAE;aAClB,CAAC;YACF,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC,CAAC;IACJ,CAAC;IAED,0DAA0D;IAC1D,YAAY;QACV,OAAO,CAAC,GAAU,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;YACrE,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,GAAG,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;gBAChC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAChD,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC,CAAC;IACJ,CAAC;IAED,kBAAkB;IAClB,YAAY,CAAC,GAAU,EAAE,IAA2B;QAClD,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,gBAAgB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,0DAA0D;IAC1D,cAAc,CAAC,GAAiB,EAAE,UAAkB,EAAE,YAAoB;QACxE,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,4EAA4E;QAC5E,+EAA+E;QAC/E,kFAAkF;QAClF,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO;QACzB,MAAM,YAAY,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,QAAQ,UAAU,WAAW,CAAC,CAAC;QACrE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,EAAE;YAC1C,QAAQ,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE;SACzE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CACL,OAAe,EACf,GAAU,EACV,GAA+B,EAC/B,KAAuD;QAEvD,IAAI,GAAG;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC7B,MAAM,OAAO,GAAmB;YAC9B,OAAO,EAAE,OAAoC;YAC7C,QAAQ,EAAE,GAAG,EAAE,OAAO,IAAI,eAAe,EAAE;YAC3C,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;YACxD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,YAAY,EAAE,IAAI,CAAC,WAAW;YAC9B,WAAW,EAAE,IAAI,CAAC,UAAU;YAC5B,KAAK,EAAE;gBACL,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,OAAO;gBAClD,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC;aACvB;YACD,OAAO,EAAE,GAAG;gBACV,CAAC,CAAC;oBACE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM;oBAC1B,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI;oBACtB,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;oBAC1C,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI;oBACtB,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU;iBACpC;gBACH,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,QAAQ,EAAE,KAAK,EAAE,QAAQ;YACzB,cAAc,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,IAAI,EAAE,EAAE;YACjD,cAAc,EAAE,GAAG,EAAE,aAAa,IAAI,EAAE;SACzC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,iCAAiC;IACjC,qBAAqB;QACnB,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAU,EAAE,EAAE;YAC7C,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC9C,2BAA2B;YAC3B,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAe,EAAE,EAAE;YACnD,MAAM,GAAG,GAAG,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACzE,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,aAAa;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC;QACjB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC;QAE/C,SAAS,YAAY,CAAC,IAAY;YAChC,IAAI,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACzD,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sBAAsB;QACtB,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;QACvC,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE,CAAC;YACxC,UAAU,CAAC,KAAK,GAAG,KAAK,UAAU,YAAY,CAAC,KAAwB,EAAE,IAAkB;gBACzF,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBAC7C,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAE,KAAyB,CAAC,GAAG,CAAC,CAAC;oBAChI,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;oBACtB,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvB,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;wBACjD,IAAI,YAAgC,CAAC;wBACrC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC/C,IAAI,CAAC;gCACH,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC;gCAC1B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;gCAChC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;4BAC1C,CAAC;4BAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;wBAC1B,CAAC;wBACD,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC;4BACrB,IAAI;4BACJ,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE;4BAC7C,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;4BAC/B,MAAM,EAAE,GAAG,CAAC,MAAM;4BAClB,aAAa,EAAE,YAAY;4BAC3B,mBAAmB,EAAE,IAAI;yBAC1B,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBACD,OAAO,GAAG,CAAC;YACb,CAAiB,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QACzB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,GAAa,EAAE,GAAY;IACtD,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,GAAG,CAAC,IAAI,GAAG,UAAU,IAAa;QAChC,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACvE,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/shape.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,MAAM,CAwB1D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAS/C"}
|
package/dist/shape.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Shapes a value by replacing every scalar leaf with "[type:length]".
|
|
2
|
+
// This prevents PII from leaking into where_shape / args_shape fields.
|
|
3
|
+
export function shapeValue(val, depth = 0) {
|
|
4
|
+
if (val === null || val === undefined)
|
|
5
|
+
return 'null';
|
|
6
|
+
if (depth > 3)
|
|
7
|
+
return '[...]';
|
|
8
|
+
const t = typeof val;
|
|
9
|
+
if (t === 'string')
|
|
10
|
+
return `[string:${val.length}]`;
|
|
11
|
+
if (t === 'number')
|
|
12
|
+
return `[number:${String(val).length}]`;
|
|
13
|
+
if (t === 'boolean')
|
|
14
|
+
return `[boolean:1]`;
|
|
15
|
+
if (t === 'bigint')
|
|
16
|
+
return `[bigint:${String(val).length}]`;
|
|
17
|
+
if (Array.isArray(val)) {
|
|
18
|
+
if (val.length === 0)
|
|
19
|
+
return '[]';
|
|
20
|
+
return `[array:${val.length}]`;
|
|
21
|
+
}
|
|
22
|
+
if (t === 'object') {
|
|
23
|
+
const shaped = {};
|
|
24
|
+
for (const [k, v] of Object.entries(val)) {
|
|
25
|
+
shaped[k] = shapeValue(v, depth + 1);
|
|
26
|
+
}
|
|
27
|
+
return JSON.stringify(shaped);
|
|
28
|
+
}
|
|
29
|
+
return `[${t}]`;
|
|
30
|
+
}
|
|
31
|
+
export function shapeArgs(args) {
|
|
32
|
+
if (args === null || args === undefined)
|
|
33
|
+
return 'null';
|
|
34
|
+
if (typeof args !== 'object')
|
|
35
|
+
return shapeValue(args);
|
|
36
|
+
const shaped = {};
|
|
37
|
+
for (const [k, v] of Object.entries(args)) {
|
|
38
|
+
shaped[k] = shapeValue(v);
|
|
39
|
+
}
|
|
40
|
+
return JSON.stringify(shaped);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AAEvE,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,KAAK,GAAG,CAAC;IAChD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACrD,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAE9B,MAAM,CAAC,GAAG,OAAO,GAAG,CAAC;IACrB,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,WAAY,GAAc,CAAC,MAAM,GAAG,CAAC;IAChE,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,WAAW,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;IAC5D,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IAC1C,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,WAAW,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;IAE5D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC;IACjC,CAAC;IAED,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAA8B,CAAC,EAAE,CAAC;YACpE,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACvD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IAEtD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;QACrE,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/stack.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function parseStack(err: Error): string[];
|
|
2
|
+
export declare function generateTraceId(): string;
|
|
3
|
+
export declare function stripHeaders(headers: Record<string, string>): Record<string, string>;
|
|
4
|
+
export declare function captureHeaders(raw: Record<string, string | string[] | undefined>): Record<string, string>;
|
|
5
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AACA,wBAAgB,UAAU,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,EAAE,CAQ/C;AAGD,wBAAgB,eAAe,IAAI,MAAM,CAIxC;AAYD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMpF;AAGD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOzG"}
|
package/dist/stack.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Converts an Error stack trace into an array of frame strings.
|
|
2
|
+
export function parseStack(err) {
|
|
3
|
+
if (!err.stack)
|
|
4
|
+
return [];
|
|
5
|
+
const lines = err.stack.split('\n');
|
|
6
|
+
// Skip first line (error message), return the rest trimmed.
|
|
7
|
+
return lines
|
|
8
|
+
.slice(1)
|
|
9
|
+
.map(l => l.trim())
|
|
10
|
+
.filter(l => l.startsWith('at '));
|
|
11
|
+
}
|
|
12
|
+
// Generates a W3C traceparent header value.
|
|
13
|
+
export function generateTraceId() {
|
|
14
|
+
const traceId = randomHex(32);
|
|
15
|
+
const spanId = randomHex(16);
|
|
16
|
+
return `00-${traceId}-${spanId}-01`;
|
|
17
|
+
}
|
|
18
|
+
function randomHex(chars) {
|
|
19
|
+
const bytes = Math.ceil(chars / 2);
|
|
20
|
+
const arr = new Uint8Array(bytes);
|
|
21
|
+
crypto.getRandomValues(arr);
|
|
22
|
+
return Array.from(arr, b => b.toString(16).padStart(2, '0')).join('').slice(0, chars);
|
|
23
|
+
}
|
|
24
|
+
// Strips sensitive HTTP headers, replacing values with '[stripped]'.
|
|
25
|
+
const SENSITIVE = /^(authorization|cookie|token|secret)/i;
|
|
26
|
+
export function stripHeaders(headers) {
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
29
|
+
out[k] = SENSITIVE.test(k) ? '[stripped]' : v;
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
// Captures HTTP headers from Express-style IncomingMessage header object.
|
|
34
|
+
export function captureHeaders(raw) {
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
37
|
+
if (v === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
out[k] = Array.isArray(v) ? v.join(', ') : v;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,GAAU;IACnC,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,4DAA4D;IAC5D,OAAO,KAAK;SACT,KAAK,CAAC,CAAC,CAAC;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC7B,OAAO,MAAM,OAAO,IAAI,MAAM,KAAK,CAAC;AACtC,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACxF,CAAC;AAED,qEAAqE;AACrE,MAAM,SAAS,GAAG,uCAAuC,CAAC;AAE1D,MAAM,UAAU,YAAY,CAAC,OAA+B;IAC1D,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,GAAkD;IAC/E,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS;QAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CapturePayload } from './proto.gen.js';
|
|
2
|
+
interface TransportOptions {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
maxBatch?: number;
|
|
6
|
+
flushIntervalMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class BatchTransport {
|
|
9
|
+
private queue;
|
|
10
|
+
private timer;
|
|
11
|
+
private lastWarnAt;
|
|
12
|
+
private readonly endpoint;
|
|
13
|
+
private readonly apiKey;
|
|
14
|
+
private readonly maxBatch;
|
|
15
|
+
private readonly flushIntervalMs;
|
|
16
|
+
constructor(opts: TransportOptions);
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): void;
|
|
19
|
+
enqueue(payload: CapturePayload): void;
|
|
20
|
+
flush(): Promise<void>;
|
|
21
|
+
private sendOne;
|
|
22
|
+
private warnOnce;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,IAAI,EAAE,gBAAgB;IAOlC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAOZ,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAOhC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAQd,OAAO;IAkBrB,OAAO,CAAC,QAAQ;CAOjB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const WARN_INTERVAL_MS = 60_000;
|
|
2
|
+
export class BatchTransport {
|
|
3
|
+
queue = [];
|
|
4
|
+
timer = null;
|
|
5
|
+
lastWarnAt = 0;
|
|
6
|
+
endpoint;
|
|
7
|
+
apiKey;
|
|
8
|
+
maxBatch;
|
|
9
|
+
flushIntervalMs;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.endpoint = opts.endpoint;
|
|
12
|
+
this.apiKey = opts.apiKey;
|
|
13
|
+
this.maxBatch = opts.maxBatch ?? 10;
|
|
14
|
+
this.flushIntervalMs = opts.flushIntervalMs ?? 2_000;
|
|
15
|
+
}
|
|
16
|
+
start() {
|
|
17
|
+
this.timer = setInterval(() => void this.flush(), this.flushIntervalMs);
|
|
18
|
+
if (this.timer.unref)
|
|
19
|
+
this.timer.unref(); // don't block process exit
|
|
20
|
+
}
|
|
21
|
+
stop() {
|
|
22
|
+
if (this.timer) {
|
|
23
|
+
clearInterval(this.timer);
|
|
24
|
+
this.timer = null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
enqueue(payload) {
|
|
28
|
+
this.queue.push(payload);
|
|
29
|
+
if (this.queue.length >= this.maxBatch) {
|
|
30
|
+
void this.flush();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async flush() {
|
|
34
|
+
if (this.queue.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const batch = this.queue.splice(0, this.maxBatch);
|
|
37
|
+
for (const payload of batch) {
|
|
38
|
+
await this.sendOne(payload);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async sendOne(payload) {
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(this.endpoint, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'X-Reprova-Key': this.apiKey,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(payload),
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
this.warnOnce(`reprova: ingest returned ${res.status}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
this.warnOnce(`reprova: transport error: ${err.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
warnOnce(msg) {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
if (now - this.lastWarnAt > WARN_INTERVAL_MS) {
|
|
62
|
+
this.lastWarnAt = now;
|
|
63
|
+
console.warn(msg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AASA,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC,MAAM,OAAO,cAAc;IACjB,KAAK,GAAqB,EAAE,CAAC;IAC7B,KAAK,GAA0C,IAAI,CAAC;IACpD,UAAU,GAAG,CAAC,CAAC;IACN,QAAQ,CAAS;IACjB,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,eAAe,CAAS;IAEzC,YAAY,IAAsB;QAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC;IACvD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACxE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,2BAA2B;IACvE,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAuB;QAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClD,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,OAAuB;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACrC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,IAAI,CAAC,MAAM;iBAC7B;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAC9B,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,CAAC,QAAQ,CAAC,4BAA4B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,6BAA8B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,GAAW;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,GAAG,gBAAgB,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reprova/sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Reprova SDK for Node.js: Express error capture, Prisma data-footprint recording, and outbound HTTP call recording for locally runnable reproductions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/denisssamba-12/reprova.git",
|
|
9
|
+
"directory": "sdk-node"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/denisssamba-12/reprova#readme",
|
|
12
|
+
"bugs": "https://github.com/denisssamba-12/reprova/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"error-tracking",
|
|
15
|
+
"reproduction",
|
|
16
|
+
"debugging",
|
|
17
|
+
"express",
|
|
18
|
+
"prisma"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
43
|
+
"bench": "vitest bench",
|
|
44
|
+
"lint": "tsc --noEmit"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/express": "^4.17.25",
|
|
48
|
+
"@types/node": "^22.20.0",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vitest": "^1.6.1"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"express": "^4.22.2"
|
|
54
|
+
}
|
|
55
|
+
}
|