@reprova/sdk 0.5.0 → 0.6.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 +75 -0
- package/dist/cjs/context.js +28 -0
- package/dist/cjs/distributionBaselinePoller.js +87 -0
- package/dist/cjs/distributionDrift.js +170 -0
- package/dist/cjs/drizzle.js +204 -0
- package/dist/cjs/frameworkAdapter.js +2 -0
- package/dist/cjs/httpFetch.js +59 -0
- package/dist/cjs/idempotency.js +132 -0
- package/dist/cjs/idempotencySettingsPoller.js +71 -0
- package/dist/cjs/index.js +96 -0
- package/dist/cjs/invariantInference.js +90 -0
- package/dist/cjs/invariantRules.js +142 -0
- package/dist/cjs/knex.js +86 -0
- package/dist/cjs/kysely.js +175 -0
- package/dist/cjs/legacyPrisma.js +130 -0
- package/dist/cjs/mikroorm.js +74 -0
- package/dist/cjs/mongoSchemaReporter.js +85 -0
- package/dist/cjs/mongoose.js +335 -0
- package/dist/cjs/nPlusOne.js +0 -0
- package/dist/cjs/nest.js +136 -0
- package/dist/cjs/nextjs.js +180 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/prisma.js +363 -0
- package/dist/cjs/proto.gen.js +4 -0
- package/dist/cjs/registry.js +100 -0
- package/dist/cjs/retryStorm.js +70 -0
- package/dist/cjs/rulesPoller.js +77 -0
- package/dist/cjs/sdk.js +1231 -0
- package/dist/cjs/sequelize.js +108 -0
- package/dist/cjs/shape.js +45 -0
- package/dist/cjs/stack.js +51 -0
- package/dist/cjs/transport.js +71 -0
- package/dist/cjs/typeorm.js +200 -0
- package/dist/context.d.ts +18 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/distributionBaselinePoller.d.ts +36 -0
- package/dist/distributionBaselinePoller.d.ts.map +1 -0
- package/dist/distributionBaselinePoller.js +84 -0
- package/dist/distributionBaselinePoller.js.map +1 -0
- package/dist/distributionDrift.d.ts +41 -0
- package/dist/distributionDrift.d.ts.map +1 -0
- package/dist/distributionDrift.js +161 -0
- package/dist/distributionDrift.js.map +1 -0
- package/dist/drizzle.d.ts.map +1 -1
- package/dist/drizzle.js +10 -4
- package/dist/drizzle.js.map +1 -1
- package/dist/frameworkAdapter.d.ts +5 -0
- package/dist/frameworkAdapter.d.ts.map +1 -0
- package/dist/frameworkAdapter.js +2 -0
- package/dist/frameworkAdapter.js.map +1 -0
- package/dist/httpFetch.d.ts +13 -0
- package/dist/httpFetch.d.ts.map +1 -0
- package/dist/httpFetch.js +54 -0
- package/dist/httpFetch.js.map +1 -0
- package/dist/idempotency.d.ts +26 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +124 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/idempotencySettingsPoller.d.ts +23 -0
- package/dist/idempotencySettingsPoller.d.ts.map +1 -0
- package/dist/idempotencySettingsPoller.js +68 -0
- package/dist/idempotencySettingsPoller.js.map +1 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/invariantInference.d.ts +28 -0
- package/dist/invariantInference.d.ts.map +1 -0
- package/dist/invariantInference.js +87 -0
- package/dist/invariantInference.js.map +1 -0
- package/dist/invariantRules.d.ts +16 -0
- package/dist/invariantRules.d.ts.map +1 -0
- package/dist/invariantRules.js +104 -0
- package/dist/invariantRules.js.map +1 -0
- package/dist/knex.d.ts.map +1 -1
- package/dist/knex.js +1 -0
- package/dist/knex.js.map +1 -1
- package/dist/kysely.d.ts.map +1 -1
- package/dist/kysely.js +2 -1
- package/dist/kysely.js.map +1 -1
- package/dist/legacyPrisma.d.ts +6 -0
- package/dist/legacyPrisma.d.ts.map +1 -0
- package/dist/legacyPrisma.js +128 -0
- package/dist/legacyPrisma.js.map +1 -0
- package/dist/mikroorm.d.ts +2 -0
- package/dist/mikroorm.d.ts.map +1 -0
- package/dist/mikroorm.js +72 -0
- package/dist/mikroorm.js.map +1 -0
- package/dist/mongoSchemaReporter.d.ts +23 -0
- package/dist/mongoSchemaReporter.d.ts.map +1 -0
- package/dist/mongoSchemaReporter.js +82 -0
- package/dist/mongoSchemaReporter.js.map +1 -0
- package/dist/mongoose.d.ts +48 -0
- package/dist/mongoose.d.ts.map +1 -0
- package/dist/mongoose.js +331 -0
- package/dist/mongoose.js.map +1 -0
- package/dist/nPlusOne.d.ts +52 -0
- package/dist/nPlusOne.d.ts.map +1 -0
- package/dist/nPlusOne.js +0 -0
- package/dist/nPlusOne.js.map +1 -0
- package/dist/nest.d.ts +13 -0
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +39 -0
- package/dist/nest.js.map +1 -1
- package/dist/nextjs.d.ts +27 -0
- package/dist/nextjs.d.ts.map +1 -0
- package/dist/nextjs.js +178 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/prisma.d.ts +21 -5
- package/dist/prisma.d.ts.map +1 -1
- package/dist/prisma.js +247 -22
- package/dist/prisma.js.map +1 -1
- package/dist/proto.gen.d.ts +10 -2
- package/dist/proto.gen.d.ts.map +1 -1
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +24 -0
- package/dist/registry.js.map +1 -1
- package/dist/retryStorm.d.ts +42 -0
- package/dist/retryStorm.d.ts.map +1 -0
- package/dist/retryStorm.js +65 -0
- package/dist/retryStorm.js.map +1 -0
- package/dist/rulesPoller.d.ts +26 -0
- package/dist/rulesPoller.d.ts.map +1 -0
- package/dist/rulesPoller.js +74 -0
- package/dist/rulesPoller.js.map +1 -0
- package/dist/sdk.d.ts +48 -7
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +619 -47
- package/dist/sdk.js.map +1 -1
- package/dist/sequelize.d.ts.map +1 -1
- package/dist/sequelize.js +4 -3
- package/dist/sequelize.js.map +1 -1
- package/dist/stack.d.ts.map +1 -1
- package/dist/stack.js +6 -3
- package/dist/stack.js.map +1 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +2 -1
- package/dist/transport.js.map +1 -1
- package/dist/typeorm.d.ts +1 -0
- package/dist/typeorm.d.ts.map +1 -1
- package/dist/typeorm.js +133 -1
- package/dist/typeorm.js.map +1 -1
- package/package.json +14 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare class ValueDistributionDrift extends Error {
|
|
2
|
+
}
|
|
3
|
+
export declare class WriteCountDrift extends Error {
|
|
4
|
+
}
|
|
5
|
+
export interface WelfordState {
|
|
6
|
+
count: number;
|
|
7
|
+
mean: number;
|
|
8
|
+
m2: number;
|
|
9
|
+
min: number;
|
|
10
|
+
max: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function newWelfordState(): WelfordState;
|
|
13
|
+
export declare function updateWelfordState(state: WelfordState, x: number): void;
|
|
14
|
+
export interface DistributionBaseline {
|
|
15
|
+
sampleCount: number;
|
|
16
|
+
mean: number;
|
|
17
|
+
stddev: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function isDrift(value: number, baseline: DistributionBaseline): boolean;
|
|
20
|
+
export declare function extractTopLevelNumericFields(body: unknown): Record<string, number>;
|
|
21
|
+
interface ReporterOptions {
|
|
22
|
+
endpoint: string;
|
|
23
|
+
apiKey: string;
|
|
24
|
+
flushIntervalMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare class DistributionDriftReporter {
|
|
27
|
+
private states;
|
|
28
|
+
private timer;
|
|
29
|
+
private lastWarnAt;
|
|
30
|
+
private readonly endpoint;
|
|
31
|
+
private readonly apiKey;
|
|
32
|
+
private readonly flushIntervalMs;
|
|
33
|
+
constructor(opts: ReporterOptions);
|
|
34
|
+
start(): void;
|
|
35
|
+
stop(): void;
|
|
36
|
+
record(route: string, field: string, value: number): void;
|
|
37
|
+
flush(): Promise<void>;
|
|
38
|
+
private warnOnce;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=distributionDrift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distributionDrift.d.ts","sourceRoot":"","sources":["../src/distributionDrift.ts"],"names":[],"mappings":"AA2BA,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AAcpD,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAKD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAQvE;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAI9E;AAMD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOlF;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAYD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAA4E;IAC1F,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,eAAe,CAAS;gBAE7B,IAAI,EAAE,eAAe;IAMjC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAOZ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAUnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5B,OAAO,CAAC,QAAQ;CAOjB"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Distributional drift, numeric-only v1 slice: baseline the typical range
|
|
2
|
+
// of a numeric response field per (route, field) using Welford's online
|
|
3
|
+
// mean/variance algorithm, and flag a value that's implausibly far from
|
|
4
|
+
// that baseline — a `total` field that's normally 10-500 suddenly
|
|
5
|
+
// returning -40, with no rule authored and no equality asserted anywhere.
|
|
6
|
+
// Unlike business-invariant-rules and idempotency-key matching, this is a
|
|
7
|
+
// statistical guess with real false-positive risk (there's no
|
|
8
|
+
// "asymptotically safe" case the way an app-asserted equality is), so it's
|
|
9
|
+
// off by default and the thresholds below are fixed, not yet
|
|
10
|
+
// tenant-configurable.
|
|
11
|
+
//
|
|
12
|
+
// Deliberately scoped to numeric fields only for this slice — categorical
|
|
13
|
+
// novelty (a status enum emitting a never-seen value) is a structurally
|
|
14
|
+
// different mechanism (tracking a bounded set of distinct values, not a
|
|
15
|
+
// running mean/variance) and is left for a later slice.
|
|
16
|
+
//
|
|
17
|
+
// Observes the top-level fields of a JSON object response body only — not
|
|
18
|
+
// nested objects/arrays, and not array-shaped top-level responses (a list
|
|
19
|
+
// endpoint's items each have their own values; there's no single baseline
|
|
20
|
+
// for "the array"). That keeps the walk trivial and the key space bounded
|
|
21
|
+
// to (route, field) instead of an unbounded set of JSON paths.
|
|
22
|
+
import { httpFetch } from './httpFetch.js';
|
|
23
|
+
const MIN_SAMPLES = 200;
|
|
24
|
+
const SIGMA_MULTIPLIER = 6;
|
|
25
|
+
export class ValueDistributionDrift extends Error {
|
|
26
|
+
}
|
|
27
|
+
// Same mean/variance mechanism, same MIN_SAMPLES/SIGMA_MULTIPLIER thresholds,
|
|
28
|
+
// entirely different observation site: prisma.ts's observeWriteCountDrift
|
|
29
|
+
// baselines a Prisma updateMany/deleteMany/createMany's affected-row count
|
|
30
|
+
// per (route, operation:model) instead of an HTTP response field's value. Shares
|
|
31
|
+
// WelfordState/updateWelfordState/isDrift directly — the math doesn't care
|
|
32
|
+
// what number it's baselining — and DistributionDriftReporter/
|
|
33
|
+
// DistributionBaselinePoller are already fully generic (record/getBaseline
|
|
34
|
+
// take arbitrary string keys), so a second instance of each, pointed at a
|
|
35
|
+
// separate control-plane endpoint, is all that's needed there too. Kept as
|
|
36
|
+
// a distinct Error class/trigger since it's a conceptually separate
|
|
37
|
+
// question ("did this write touch a weird number of rows" vs. "is this
|
|
38
|
+
// response value weird") with its own independent dashboard toggle.
|
|
39
|
+
export class WriteCountDrift extends Error {
|
|
40
|
+
}
|
|
41
|
+
export function newWelfordState() {
|
|
42
|
+
return { count: 0, mean: 0, m2: 0, min: Infinity, max: -Infinity };
|
|
43
|
+
}
|
|
44
|
+
// Standard single-pass Welford update — mean and M2 (sum of squared
|
|
45
|
+
// deviations from the running mean) stay numerically stable across an
|
|
46
|
+
// unbounded stream, unlike naively accumulating sum/sum-of-squares.
|
|
47
|
+
export function updateWelfordState(state, x) {
|
|
48
|
+
state.count++;
|
|
49
|
+
const delta = x - state.mean;
|
|
50
|
+
state.mean += delta / state.count;
|
|
51
|
+
const delta2 = x - state.mean;
|
|
52
|
+
state.m2 += delta * delta2;
|
|
53
|
+
if (x < state.min)
|
|
54
|
+
state.min = x;
|
|
55
|
+
if (x > state.max)
|
|
56
|
+
state.max = x;
|
|
57
|
+
}
|
|
58
|
+
// A value is drift when the baseline has enough samples to trust AND the
|
|
59
|
+
// value is more than SIGMA_MULTIPLIER standard deviations from the mean.
|
|
60
|
+
// A zero-variance baseline (every sample identical so far) treats ANY
|
|
61
|
+
// different value as infinite deviation — flagged, since after MIN_SAMPLES
|
|
62
|
+
// consistent observations a field that never varied suddenly doing so is
|
|
63
|
+
// exactly the kind of thing worth surfacing, not a numerical edge case to
|
|
64
|
+
// special-case away.
|
|
65
|
+
export function isDrift(value, baseline) {
|
|
66
|
+
if (baseline.sampleCount < MIN_SAMPLES)
|
|
67
|
+
return false;
|
|
68
|
+
const deviation = baseline.stddev === 0 ? (value !== baseline.mean ? Infinity : 0) : Math.abs(value - baseline.mean) / baseline.stddev;
|
|
69
|
+
return deviation > SIGMA_MULTIPLIER;
|
|
70
|
+
}
|
|
71
|
+
// Extracts finite numeric values from the response body's OWN top-level
|
|
72
|
+
// keys — null/NaN/Infinity are skipped (not a "value", nothing to
|
|
73
|
+
// baseline), and a non-object or array body yields nothing (see module doc
|
|
74
|
+
// for why array-shaped top-level responses are out of scope for this slice).
|
|
75
|
+
export function extractTopLevelNumericFields(body) {
|
|
76
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
77
|
+
return {};
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [k, v] of Object.entries(body)) {
|
|
80
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
81
|
+
out[k] = v;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
const WARN_INTERVAL_MS = 60_000;
|
|
86
|
+
// Accumulates local Welford state per (route, field) since the last flush
|
|
87
|
+
// and periodically reports it as a delta — same start/stop/periodic-flush
|
|
88
|
+
// shape as BatchTransport/InferenceReporter. The control plane merges each
|
|
89
|
+
// report into the tenant's cumulative baseline via the parallel-variance
|
|
90
|
+
// formula; a dropped/failed flush only ever loses that batch's
|
|
91
|
+
// contribution (undercounts), never double-reports on retry, since there
|
|
92
|
+
// is no retry — same trade-off every other reporter in this SDK already
|
|
93
|
+
// makes.
|
|
94
|
+
export class DistributionDriftReporter {
|
|
95
|
+
states = new Map();
|
|
96
|
+
timer = null;
|
|
97
|
+
lastWarnAt = 0;
|
|
98
|
+
endpoint;
|
|
99
|
+
apiKey;
|
|
100
|
+
flushIntervalMs;
|
|
101
|
+
constructor(opts) {
|
|
102
|
+
this.endpoint = opts.endpoint;
|
|
103
|
+
this.apiKey = opts.apiKey;
|
|
104
|
+
this.flushIntervalMs = opts.flushIntervalMs ?? 30_000;
|
|
105
|
+
}
|
|
106
|
+
start() {
|
|
107
|
+
this.timer = setInterval(() => void this.flush(), this.flushIntervalMs);
|
|
108
|
+
if (this.timer.unref)
|
|
109
|
+
this.timer.unref();
|
|
110
|
+
}
|
|
111
|
+
stop() {
|
|
112
|
+
if (this.timer) {
|
|
113
|
+
clearInterval(this.timer);
|
|
114
|
+
this.timer = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
record(route, field, value) {
|
|
118
|
+
const key = `${route}\x00${field}`;
|
|
119
|
+
let entry = this.states.get(key);
|
|
120
|
+
if (!entry) {
|
|
121
|
+
entry = { route, field, state: newWelfordState() };
|
|
122
|
+
this.states.set(key, entry);
|
|
123
|
+
}
|
|
124
|
+
updateWelfordState(entry.state, value);
|
|
125
|
+
}
|
|
126
|
+
async flush() {
|
|
127
|
+
if (this.states.size === 0)
|
|
128
|
+
return;
|
|
129
|
+
const observations = [...this.states.values()].map(({ route, field, state }) => ({
|
|
130
|
+
route,
|
|
131
|
+
field,
|
|
132
|
+
count: state.count,
|
|
133
|
+
mean: state.mean,
|
|
134
|
+
m2: state.m2,
|
|
135
|
+
min: state.min,
|
|
136
|
+
max: state.max,
|
|
137
|
+
}));
|
|
138
|
+
this.states.clear();
|
|
139
|
+
try {
|
|
140
|
+
const res = await httpFetch(this.endpoint, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json', 'X-Reprova-Key': this.apiKey },
|
|
143
|
+
body: JSON.stringify({ observations }),
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
this.warnOnce(`reprova: distribution baseline report returned ${res.status}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
this.warnOnce(`reprova: distribution baseline report error: ${err.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
warnOnce(msg) {
|
|
154
|
+
const now = Date.now();
|
|
155
|
+
if (now - this.lastWarnAt > WARN_INTERVAL_MS) {
|
|
156
|
+
this.lastWarnAt = now;
|
|
157
|
+
console.warn(msg);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=distributionDrift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distributionDrift.js","sourceRoot":"","sources":["../src/distributionDrift.ts"],"names":[],"mappings":"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"}
|
package/dist/drizzle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAsCA,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;AA2ED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAyClF;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
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { contextStorage } from './context.js';
|
|
2
3
|
const OP_BY_KEYWORD = {
|
|
3
4
|
select: 'select',
|
|
@@ -57,10 +58,14 @@ function extractPks(rows, pkCols) {
|
|
|
57
58
|
// hash computed here from `logger.logQuery`'s (sql, params) matches the hash
|
|
58
59
|
// Drizzle later passes to `cache.put()` for the SAME query, letting the two
|
|
59
60
|
// hooks correlate without ever importing 'drizzle-orm'.
|
|
61
|
+
// Node's own crypto module (not WebCrypto's `crypto.subtle`, which only
|
|
62
|
+
// became a stable global in Node 19+) — createHash has existed since
|
|
63
|
+
// Node's earliest crypto module, so this works on any Node version this
|
|
64
|
+
// SDK targets. Stays async (a plain value from an async function resolves
|
|
65
|
+
// immediately) so callers don't need to change.
|
|
60
66
|
async function hashQuery(sql, params) {
|
|
61
67
|
const data = `${sql}-${JSON.stringify(params)}`;
|
|
62
|
-
|
|
63
|
-
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
68
|
+
return createHash('sha256').update(data).digest('hex');
|
|
64
69
|
}
|
|
65
70
|
// A pending SELECT footprint entry, keyed by query hash, waiting to be
|
|
66
71
|
// upgraded by a paired cache's put(). Linked to its logger via WeakMap so the
|
|
@@ -88,6 +93,7 @@ export function createDrizzleLogger(opts = {}) {
|
|
|
88
93
|
where_shape: where,
|
|
89
94
|
count: 0,
|
|
90
95
|
note: 'drizzle: shape-only (no result access — PKs/count unavailable)',
|
|
96
|
+
resolved: true,
|
|
91
97
|
};
|
|
92
98
|
ctx.footprint.push(entry);
|
|
93
99
|
// A paired cache can upgrade this SAME entry once the real rows
|
|
@@ -161,14 +167,14 @@ export function createDrizzleCache(opts = {}) {
|
|
|
161
167
|
linked.note = note;
|
|
162
168
|
}
|
|
163
169
|
else if (ctx) {
|
|
164
|
-
ctx.footprint.push({ model: primary, op: 'select', pks, count, note });
|
|
170
|
+
ctx.footprint.push({ model: primary, op: 'select', pks, count, note, resolved: true });
|
|
165
171
|
}
|
|
166
172
|
// Joined/subquery tables: recorded as referenced, without PKs — same
|
|
167
173
|
// convention as the Kysely adapter (result columns can't be reliably
|
|
168
174
|
// attributed to a joined table's own primary key).
|
|
169
175
|
if (ctx) {
|
|
170
176
|
for (const t of tables.slice(1)) {
|
|
171
|
-
ctx.footprint.push({ model: t, op: 'select', pks: [], count: 0, note: 'referenced (join/subquery)' });
|
|
177
|
+
ctx.footprint.push({ model: t, op: 'select', pks: [], count: 0, note: 'referenced (join/subquery)', resolved: true });
|
|
172
178
|
}
|
|
173
179
|
}
|
|
174
180
|
}
|
package/dist/drizzle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameworkAdapter.d.ts","sourceRoot":"","sources":["../src/frameworkAdapter.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,gBAAgB;IAK/B,iBAAiB,CAAC,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAM7E,kBAAkB,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameworkAdapter.js","sourceRoot":"","sources":["../src/frameworkAdapter.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface HttpFetchResponse {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
status: number;
|
|
4
|
+
json(): Promise<unknown>;
|
|
5
|
+
text(): Promise<string>;
|
|
6
|
+
}
|
|
7
|
+
export interface HttpFetchInit {
|
|
8
|
+
method?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
body?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function httpFetch(url: string, init?: HttpFetchInit): Promise<HttpFetchResponse>;
|
|
13
|
+
//# sourceMappingURL=httpFetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpFetch.d.ts","sourceRoot":"","sources":["../src/httpFetch.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAYD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyCvF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
// Node 18+ ships a global fetch; older Node (found live: Node 14, still a
|
|
4
|
+
// real LTS for legacy apps) has none at all. node-fetch isn't a safe
|
|
5
|
+
// replacement here — v3+ is ESM-only, the exact same require(ESM) trap
|
|
6
|
+
// @marcbachmann/cel-js caused (see invariantRules.ts's own doc comment on
|
|
7
|
+
// loadEnv). Every internal caller in this SDK only ever needs a small,
|
|
8
|
+
// stable subset of the Fetch API (GET/POST, headers, a string body,
|
|
9
|
+
// .ok/.status/.json()/.text()), so this implements just that on top of
|
|
10
|
+
// Node's own always-available http/https modules, and delegates to a real
|
|
11
|
+
// global fetch when one exists (the common case — zero overhead, zero
|
|
12
|
+
// behavior change on any Node version that already has one).
|
|
13
|
+
export function httpFetch(url, init) {
|
|
14
|
+
const globalFetch = globalThis.fetch;
|
|
15
|
+
if (typeof globalFetch === 'function') {
|
|
16
|
+
return globalFetch(url, init);
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = new URL(url);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
reject(err);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
28
|
+
const req = lib.request({
|
|
29
|
+
hostname: parsed.hostname,
|
|
30
|
+
port: parsed.port || undefined,
|
|
31
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
32
|
+
method: init?.method ?? 'GET',
|
|
33
|
+
headers: init?.headers,
|
|
34
|
+
}, (res) => {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
37
|
+
res.on('end', () => {
|
|
38
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
39
|
+
const status = res.statusCode ?? 0;
|
|
40
|
+
resolve({
|
|
41
|
+
ok: status >= 200 && status < 300,
|
|
42
|
+
status,
|
|
43
|
+
json: async () => JSON.parse(text),
|
|
44
|
+
text: async () => text,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
req.on('error', reject);
|
|
49
|
+
if (init?.body)
|
|
50
|
+
req.write(init.body);
|
|
51
|
+
req.end();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=httpFetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpFetch.js","sourceRoot":"","sources":["../src/httpFetch.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;AAe/B,0EAA0E;AAC1E,qEAAqE;AACrE,uEAAuE;AACvE,0EAA0E;AAC1E,uEAAuE;AACvE,oEAAoE;AACpE,uEAAuE;AACvE,0EAA0E;AAC1E,sEAAsE;AACtE,6DAA6D;AAC7D,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,IAAoB;IACzD,MAAM,WAAW,GAAI,UAAuF,CAAC,KAAK,CAAC;IACnH,IAAI,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACxD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CACrB;YACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,SAAS;YAC9B,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE;YAC1C,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK;YAC7B,OAAO,EAAE,IAAI,EAAE,OAAO;SACvB,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACpD,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;gBACnC,OAAO,CAAC;oBACN,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;oBACjC,MAAM;oBACN,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;oBAClC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;iBACvB,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,IAAI,IAAI,EAAE,IAAI;YAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface IdempotencySettings {
|
|
2
|
+
key_check_enabled: boolean;
|
|
3
|
+
header_names: string[];
|
|
4
|
+
ttl_ms: number;
|
|
5
|
+
max_keys: number;
|
|
6
|
+
duplicate_read_enabled: boolean;
|
|
7
|
+
duplicate_read_window_ms: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_IDEMPOTENCY_SETTINGS: IdempotencySettings;
|
|
10
|
+
export declare class IdempotencyKeyMismatch extends Error {
|
|
11
|
+
}
|
|
12
|
+
export declare class DuplicateReadMismatch extends Error {
|
|
13
|
+
}
|
|
14
|
+
export declare function hashResponseBody(body: unknown): string;
|
|
15
|
+
export declare class HashMismatchCache {
|
|
16
|
+
private maxKeys;
|
|
17
|
+
private ttlMs;
|
|
18
|
+
private readonly map;
|
|
19
|
+
constructor(maxKeys: number, ttlMs: number);
|
|
20
|
+
configure(maxKeys: number, ttlMs: number): void;
|
|
21
|
+
checkAndRecord(signature: string, hash: string, now?: number): boolean;
|
|
22
|
+
get size(): number;
|
|
23
|
+
}
|
|
24
|
+
export declare function findIdempotencyKey(headers: Record<string, string>, headerNames: string[]): string | undefined;
|
|
25
|
+
export declare function extractIdentity(authClaims: Record<string, unknown> | undefined): string | undefined;
|
|
26
|
+
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,EAAE,OAAO,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAKD,eAAO,MAAM,4BAA4B,EAAE,mBAO1C,CAAC;AAMF,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AACpD,qBAAa,qBAAsB,SAAQ,KAAK;CAAG;AAcnD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEtD;AAQD,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,KAAK;IAJf,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA0D;gBAGpE,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM;IAMvB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAO/C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO;IAa1E,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAKD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAM7G;AAUD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAOnG"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Idempotency/consistency checks: two independent, domain-agnostic
|
|
2
|
+
// detectors for "silent 200" bugs, unlike business-invariant rules — neither
|
|
3
|
+
// needs a dashboard-authored condition tree, since neither depends on
|
|
4
|
+
// knowing anything about the app's data model.
|
|
5
|
+
//
|
|
6
|
+
// 1. Idempotency-key mismatch: the app itself asserts two requests are "the
|
|
7
|
+
// same operation" by sending an identical value in a known header (e.g.
|
|
8
|
+
// Stripe-style Idempotency-Key). If the response differs between them,
|
|
9
|
+
// that's unambiguously a bug — no heuristic tuning needed, the app told
|
|
10
|
+
// us these should match.
|
|
11
|
+
// 2. Duplicate-read mismatch: the SAME GET (route + query + caller
|
|
12
|
+
// identity) repeated within a short window returning a different
|
|
13
|
+
// result. Real false-positive risk exists here (data can legitimately
|
|
14
|
+
// change between two reads), so it's scoped tight: a short window turns
|
|
15
|
+
// it into "did two near-simultaneous reads of what should be the same
|
|
16
|
+
// snapshot disagree" (a race-condition/double-fetch signal), not general
|
|
17
|
+
// staleness detection — and it's skipped entirely when no caller
|
|
18
|
+
// identity is available, since an identity-less GET trivially disagrees
|
|
19
|
+
// across different callers.
|
|
20
|
+
import { createHash } from 'node:crypto';
|
|
21
|
+
// Mirrors the control plane's idempotency_settings table defaults — used
|
|
22
|
+
// before the first successful poll, and if polling never succeeds at all
|
|
23
|
+
// (e.g. an older control plane without this endpoint yet).
|
|
24
|
+
export const DEFAULT_IDEMPOTENCY_SETTINGS = {
|
|
25
|
+
key_check_enabled: true,
|
|
26
|
+
header_names: ['Idempotency-Key', 'X-Idempotency-Key'],
|
|
27
|
+
ttl_ms: 86_400_000,
|
|
28
|
+
max_keys: 100_000,
|
|
29
|
+
duplicate_read_enabled: false,
|
|
30
|
+
duplicate_read_window_ms: 500,
|
|
31
|
+
};
|
|
32
|
+
// One reusable Error class per detector, mirroring BusinessInvariantViolation
|
|
33
|
+
// — per-endpoint identity comes from capture()'s errorType override, not a
|
|
34
|
+
// per-instance subclass, so many distinct endpoints share one class without
|
|
35
|
+
// collapsing into a single issue.
|
|
36
|
+
export class IdempotencyKeyMismatch extends Error {
|
|
37
|
+
}
|
|
38
|
+
export class DuplicateReadMismatch extends Error {
|
|
39
|
+
}
|
|
40
|
+
// Deterministic regardless of key insertion order, so { a:1, b:2 } and
|
|
41
|
+
// { b:2, a:1 } hash identically — otherwise two genuinely-equivalent
|
|
42
|
+
// responses serialized in a different key order would look like a mismatch
|
|
43
|
+
// that isn't one.
|
|
44
|
+
function stableStringify(value) {
|
|
45
|
+
if (value === null || typeof value !== 'object')
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
if (Array.isArray(value))
|
|
48
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
49
|
+
const obj = value;
|
|
50
|
+
const keys = Object.keys(obj).sort();
|
|
51
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
|
|
52
|
+
}
|
|
53
|
+
export function hashResponseBody(body) {
|
|
54
|
+
return createHash('sha256').update(stableStringify(body)).digest('hex');
|
|
55
|
+
}
|
|
56
|
+
// Bounded signature -> last-seen-hash cache with TTL + LRU eviction, shared
|
|
57
|
+
// by both detectors — the underlying mechanism ("has this signature's hash
|
|
58
|
+
// changed within its window") is identical for both; only what goes into
|
|
59
|
+
// the signature and how long it's remembered differs. Map iteration order
|
|
60
|
+
// is insertion order, and delete-then-reinsert on every access keeps it in
|
|
61
|
+
// least-recently-used order, so eviction always drops the LRU entry.
|
|
62
|
+
export class HashMismatchCache {
|
|
63
|
+
maxKeys;
|
|
64
|
+
ttlMs;
|
|
65
|
+
map = new Map();
|
|
66
|
+
constructor(maxKeys, ttlMs) {
|
|
67
|
+
this.maxKeys = maxKeys;
|
|
68
|
+
this.ttlMs = ttlMs;
|
|
69
|
+
}
|
|
70
|
+
// Settings can change on every poll — reconfigure in place rather than
|
|
71
|
+
// recreating the cache, so a TTL/cap edit doesn't silently drop everything
|
|
72
|
+
// currently tracked.
|
|
73
|
+
configure(maxKeys, ttlMs) {
|
|
74
|
+
this.maxKeys = maxKeys;
|
|
75
|
+
this.ttlMs = ttlMs;
|
|
76
|
+
}
|
|
77
|
+
// Records `hash` for `signature`, returning true if a live (non-expired)
|
|
78
|
+
// prior hash for the same signature exists and differs from this one.
|
|
79
|
+
checkAndRecord(signature, hash, now = Date.now()) {
|
|
80
|
+
const existing = this.map.get(signature);
|
|
81
|
+
this.map.delete(signature); // re-inserted below either way, refreshing LRU order
|
|
82
|
+
const mismatch = !!existing && existing.expiresAt > now && existing.hash !== hash;
|
|
83
|
+
this.map.set(signature, { hash, expiresAt: now + this.ttlMs });
|
|
84
|
+
while (this.map.size > this.maxKeys) {
|
|
85
|
+
const oldest = this.map.keys().next().value;
|
|
86
|
+
if (oldest === undefined)
|
|
87
|
+
break;
|
|
88
|
+
this.map.delete(oldest);
|
|
89
|
+
}
|
|
90
|
+
return mismatch;
|
|
91
|
+
}
|
|
92
|
+
get size() {
|
|
93
|
+
return this.map.size;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Checks each configured header name case-insensitively — Node normalizes
|
|
97
|
+
// incoming header names to lowercase, and captureHeaders (stack.ts) passes
|
|
98
|
+
// that through unchanged, so ctx.request.headers keys are already lowercase.
|
|
99
|
+
export function findIdempotencyKey(headers, headerNames) {
|
|
100
|
+
for (const name of headerNames) {
|
|
101
|
+
const value = headers[name.toLowerCase()];
|
|
102
|
+
if (value)
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
// A small, conventional set of claim keys checked in order — not exhaustive,
|
|
108
|
+
// just enough to cover common shapes (Auth0/JWT `sub`, a plain `id`,
|
|
109
|
+
// Passport-style `userId`/`user_id`). Returns undefined, never a guess, when
|
|
110
|
+
// none match: duplicate-read detection is skipped entirely without an
|
|
111
|
+
// identity, rather than risk comparing two different callers' data and
|
|
112
|
+
// calling the difference a bug.
|
|
113
|
+
const IDENTITY_CLAIM_KEYS = ['sub', 'id', 'userId', 'user_id'];
|
|
114
|
+
export function extractIdentity(authClaims) {
|
|
115
|
+
if (!authClaims)
|
|
116
|
+
return undefined;
|
|
117
|
+
for (const key of IDENTITY_CLAIM_KEYS) {
|
|
118
|
+
const value = authClaims[key];
|
|
119
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
120
|
+
return String(value);
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=idempotency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IdempotencySettings } from './idempotency.js';
|
|
2
|
+
interface IdempotencySettingsPollerOptions {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
intervalMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class IdempotencySettingsPoller {
|
|
8
|
+
private settings;
|
|
9
|
+
private timer;
|
|
10
|
+
private lastWarnAt;
|
|
11
|
+
private readonly endpoint;
|
|
12
|
+
private readonly apiKey;
|
|
13
|
+
private readonly intervalMs;
|
|
14
|
+
constructor(opts: IdempotencySettingsPollerOptions);
|
|
15
|
+
start(): void;
|
|
16
|
+
stop(): void;
|
|
17
|
+
getSettings(): IdempotencySettings;
|
|
18
|
+
refresh(): Promise<void>;
|
|
19
|
+
seed(settings: IdempotencySettings): void;
|
|
20
|
+
private warnOnce;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=idempotencySettingsPoller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotencySettingsPoller.d.ts","sourceRoot":"","sources":["../src/idempotencySettingsPoller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,UAAU,gCAAgC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAqD;IACrE,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,UAAU,CAAS;gBAExB,IAAI,EAAE,gCAAgC;IAMlD,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAOZ,WAAW,IAAI,mBAAmB;IAI5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B,IAAI,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAIzC,OAAO,CAAC,QAAQ;CAOjB"}
|