@reprova/sdk 0.4.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.
Files changed (147) hide show
  1. package/README.md +170 -8
  2. package/dist/cjs/context.js +28 -0
  3. package/dist/cjs/distributionBaselinePoller.js +87 -0
  4. package/dist/cjs/distributionDrift.js +170 -0
  5. package/dist/cjs/drizzle.js +204 -0
  6. package/dist/cjs/frameworkAdapter.js +2 -0
  7. package/dist/cjs/httpFetch.js +59 -0
  8. package/dist/cjs/idempotency.js +132 -0
  9. package/dist/cjs/idempotencySettingsPoller.js +71 -0
  10. package/dist/cjs/index.js +96 -0
  11. package/dist/cjs/invariantInference.js +90 -0
  12. package/dist/cjs/invariantRules.js +142 -0
  13. package/dist/cjs/knex.js +86 -0
  14. package/dist/cjs/kysely.js +175 -0
  15. package/dist/cjs/legacyPrisma.js +130 -0
  16. package/dist/cjs/mikroorm.js +74 -0
  17. package/dist/cjs/mongoSchemaReporter.js +85 -0
  18. package/dist/cjs/mongoose.js +335 -0
  19. package/dist/cjs/nPlusOne.js +0 -0
  20. package/dist/cjs/nest.js +136 -0
  21. package/dist/cjs/nextjs.js +180 -0
  22. package/dist/cjs/package.json +1 -0
  23. package/dist/cjs/prisma.js +363 -0
  24. package/dist/cjs/proto.gen.js +4 -0
  25. package/dist/cjs/registry.js +100 -0
  26. package/dist/cjs/retryStorm.js +70 -0
  27. package/dist/cjs/rulesPoller.js +77 -0
  28. package/dist/cjs/sdk.js +1231 -0
  29. package/dist/cjs/sequelize.js +108 -0
  30. package/dist/cjs/shape.js +45 -0
  31. package/dist/cjs/stack.js +51 -0
  32. package/dist/cjs/transport.js +71 -0
  33. package/dist/cjs/typeorm.js +200 -0
  34. package/dist/context.d.ts +32 -0
  35. package/dist/context.d.ts.map +1 -1
  36. package/dist/context.js +18 -0
  37. package/dist/context.js.map +1 -1
  38. package/dist/distributionBaselinePoller.d.ts +36 -0
  39. package/dist/distributionBaselinePoller.d.ts.map +1 -0
  40. package/dist/distributionBaselinePoller.js +84 -0
  41. package/dist/distributionBaselinePoller.js.map +1 -0
  42. package/dist/distributionDrift.d.ts +41 -0
  43. package/dist/distributionDrift.d.ts.map +1 -0
  44. package/dist/distributionDrift.js +161 -0
  45. package/dist/distributionDrift.js.map +1 -0
  46. package/dist/drizzle.d.ts +22 -0
  47. package/dist/drizzle.d.ts.map +1 -1
  48. package/dist/drizzle.js +129 -1
  49. package/dist/drizzle.js.map +1 -1
  50. package/dist/frameworkAdapter.d.ts +5 -0
  51. package/dist/frameworkAdapter.d.ts.map +1 -0
  52. package/dist/frameworkAdapter.js +2 -0
  53. package/dist/frameworkAdapter.js.map +1 -0
  54. package/dist/httpFetch.d.ts +13 -0
  55. package/dist/httpFetch.d.ts.map +1 -0
  56. package/dist/httpFetch.js +54 -0
  57. package/dist/httpFetch.js.map +1 -0
  58. package/dist/idempotency.d.ts +26 -0
  59. package/dist/idempotency.d.ts.map +1 -0
  60. package/dist/idempotency.js +124 -0
  61. package/dist/idempotency.js.map +1 -0
  62. package/dist/idempotencySettingsPoller.d.ts +23 -0
  63. package/dist/idempotencySettingsPoller.d.ts.map +1 -0
  64. package/dist/idempotencySettingsPoller.js +68 -0
  65. package/dist/idempotencySettingsPoller.js.map +1 -0
  66. package/dist/index.d.ts +33 -6
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +19 -4
  69. package/dist/index.js.map +1 -1
  70. package/dist/invariantInference.d.ts +28 -0
  71. package/dist/invariantInference.d.ts.map +1 -0
  72. package/dist/invariantInference.js +87 -0
  73. package/dist/invariantInference.js.map +1 -0
  74. package/dist/invariantRules.d.ts +16 -0
  75. package/dist/invariantRules.d.ts.map +1 -0
  76. package/dist/invariantRules.js +104 -0
  77. package/dist/invariantRules.js.map +1 -0
  78. package/dist/knex.d.ts.map +1 -1
  79. package/dist/knex.js +1 -0
  80. package/dist/knex.js.map +1 -1
  81. package/dist/kysely.d.ts.map +1 -1
  82. package/dist/kysely.js +2 -1
  83. package/dist/kysely.js.map +1 -1
  84. package/dist/legacyPrisma.d.ts +6 -0
  85. package/dist/legacyPrisma.d.ts.map +1 -0
  86. package/dist/legacyPrisma.js +128 -0
  87. package/dist/legacyPrisma.js.map +1 -0
  88. package/dist/mikroorm.d.ts +2 -0
  89. package/dist/mikroorm.d.ts.map +1 -0
  90. package/dist/mikroorm.js +72 -0
  91. package/dist/mikroorm.js.map +1 -0
  92. package/dist/mongoSchemaReporter.d.ts +23 -0
  93. package/dist/mongoSchemaReporter.d.ts.map +1 -0
  94. package/dist/mongoSchemaReporter.js +82 -0
  95. package/dist/mongoSchemaReporter.js.map +1 -0
  96. package/dist/mongoose.d.ts +48 -0
  97. package/dist/mongoose.d.ts.map +1 -0
  98. package/dist/mongoose.js +331 -0
  99. package/dist/mongoose.js.map +1 -0
  100. package/dist/nPlusOne.d.ts +52 -0
  101. package/dist/nPlusOne.d.ts.map +1 -0
  102. package/dist/nPlusOne.js +0 -0
  103. package/dist/nPlusOne.js.map +1 -0
  104. package/dist/nest.d.ts +31 -0
  105. package/dist/nest.d.ts.map +1 -0
  106. package/dist/nest.js +133 -0
  107. package/dist/nest.js.map +1 -0
  108. package/dist/nextjs.d.ts +27 -0
  109. package/dist/nextjs.d.ts.map +1 -0
  110. package/dist/nextjs.js +178 -0
  111. package/dist/nextjs.js.map +1 -0
  112. package/dist/prisma.d.ts +21 -5
  113. package/dist/prisma.d.ts.map +1 -1
  114. package/dist/prisma.js +247 -22
  115. package/dist/prisma.js.map +1 -1
  116. package/dist/proto.gen.d.ts +19 -1
  117. package/dist/proto.gen.d.ts.map +1 -1
  118. package/dist/registry.d.ts +1 -0
  119. package/dist/registry.d.ts.map +1 -1
  120. package/dist/registry.js +24 -0
  121. package/dist/registry.js.map +1 -1
  122. package/dist/retryStorm.d.ts +42 -0
  123. package/dist/retryStorm.d.ts.map +1 -0
  124. package/dist/retryStorm.js +65 -0
  125. package/dist/retryStorm.js.map +1 -0
  126. package/dist/rulesPoller.d.ts +26 -0
  127. package/dist/rulesPoller.d.ts.map +1 -0
  128. package/dist/rulesPoller.js +74 -0
  129. package/dist/rulesPoller.js.map +1 -0
  130. package/dist/sdk.d.ts +53 -6
  131. package/dist/sdk.d.ts.map +1 -1
  132. package/dist/sdk.js +663 -34
  133. package/dist/sdk.js.map +1 -1
  134. package/dist/sequelize.d.ts.map +1 -1
  135. package/dist/sequelize.js +4 -3
  136. package/dist/sequelize.js.map +1 -1
  137. package/dist/stack.d.ts.map +1 -1
  138. package/dist/stack.js +6 -3
  139. package/dist/stack.js.map +1 -1
  140. package/dist/transport.d.ts.map +1 -1
  141. package/dist/transport.js +2 -1
  142. package/dist/transport.js.map +1 -1
  143. package/dist/typeorm.d.ts +1 -0
  144. package/dist/typeorm.d.ts.map +1 -1
  145. package/dist/typeorm.js +133 -1
  146. package/dist/typeorm.js.map +1 -1
  147. package/package.json +20 -5
@@ -0,0 +1,84 @@
1
+ import { httpFetch } from './httpFetch.js';
2
+ const WARN_INTERVAL_MS = 60_000;
3
+ // Same start()/stop()/setInterval+unref shape as RulesPoller and
4
+ // IdempotencySettingsPoller — periodically fetches the tenant's current
5
+ // aggregate baselines (and the drift-detection on/off toggle) so a running
6
+ // process can check each response against a live baseline without a
7
+ // redeploy, and so drift detection stays off by default until an admin
8
+ // opts in from the dashboard. Falls back to disabled + no baselines before
9
+ // the first successful poll and on any failure, so a bad response or an
10
+ // unreachable control plane fails closed (no captures), not open (spurious
11
+ // captures from a stale or empty baseline).
12
+ export class DistributionBaselinePoller {
13
+ enabled = false;
14
+ baselines = new Map();
15
+ timer = null;
16
+ lastWarnAt = 0;
17
+ endpoint;
18
+ apiKey;
19
+ intervalMs;
20
+ constructor(opts) {
21
+ this.endpoint = opts.endpoint;
22
+ this.apiKey = opts.apiKey;
23
+ this.intervalMs = opts.intervalMs ?? 60_000;
24
+ }
25
+ start() {
26
+ void this.refresh();
27
+ this.timer = setInterval(() => void this.refresh(), this.intervalMs);
28
+ if (this.timer.unref)
29
+ this.timer.unref();
30
+ }
31
+ stop() {
32
+ if (this.timer) {
33
+ clearInterval(this.timer);
34
+ this.timer = null;
35
+ }
36
+ }
37
+ isEnabled() {
38
+ return this.enabled;
39
+ }
40
+ getBaseline(route, field) {
41
+ return this.baselines.get(`${route}\x00${field}`);
42
+ }
43
+ async refresh() {
44
+ try {
45
+ const res = await httpFetch(this.endpoint, {
46
+ headers: { 'X-Reprova-Key': this.apiKey },
47
+ });
48
+ if (!res.ok) {
49
+ this.warnOnce(`reprova: distribution baselines fetch returned ${res.status}`);
50
+ return;
51
+ }
52
+ const body = (await res.json());
53
+ this.applyResponse(body);
54
+ }
55
+ catch (err) {
56
+ this.warnOnce(`reprova: distribution baselines fetch error: ${err.message}`);
57
+ }
58
+ }
59
+ // Applies an already-fetched response directly, with no network call —
60
+ // used by replay mode to seed a one-time snapshot the CLI fetched ahead of
61
+ // time (there's no live control plane to poll from inside a replayed
62
+ // app), so drift detection (checkDistributionDriftIfAny/
63
+ // captureWriteCountDriftIfAny in sdk.ts) sees exactly the same shape it
64
+ // would from a real refresh().
65
+ seed(body) {
66
+ this.applyResponse(body);
67
+ }
68
+ applyResponse(body) {
69
+ this.enabled = body.enabled ?? false;
70
+ const next = new Map();
71
+ for (const b of body.baselines ?? []) {
72
+ next.set(`${b.route}\x00${b.field}`, { sampleCount: b.sample_count, mean: b.mean, stddev: b.stddev });
73
+ }
74
+ this.baselines = next;
75
+ }
76
+ warnOnce(msg) {
77
+ const now = Date.now();
78
+ if (now - this.lastWarnAt > WARN_INTERVAL_MS) {
79
+ this.lastWarnAt = now;
80
+ console.warn(msg);
81
+ }
82
+ }
83
+ }
84
+ //# sourceMappingURL=distributionBaselinePoller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distributionBaselinePoller.js","sourceRoot":"","sources":["../src/distributionBaselinePoller.ts"],"names":[],"mappings":"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"}
@@ -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 CHANGED
@@ -7,5 +7,27 @@ export interface DrizzleLoggerOptions {
7
7
  export interface DrizzleLogger {
8
8
  logQuery(query: string, params: unknown[]): void;
9
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
+ }
10
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
+ };
11
33
  //# sourceMappingURL=drizzle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"AAkBA,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;AAuCD,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CA2BlF"}
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',
@@ -35,12 +36,48 @@ function whereColumns(sql) {
35
36
  cols.add(m[2] ?? m[1]);
36
37
  return [...cols].sort();
37
38
  }
39
+ function extractPks(rows, pkCols) {
40
+ if (pkCols.length === 0)
41
+ return [];
42
+ const seen = new Set();
43
+ const pks = [];
44
+ for (const row of rows) {
45
+ const vals = pkCols.map((c) => row[c]);
46
+ if (vals.some((v) => v === undefined))
47
+ continue; // column not selected — can't form a PK
48
+ const pk = vals.map((v) => String(v)).join(':');
49
+ if (!seen.has(pk)) {
50
+ seen.add(pk);
51
+ pks.push(pk);
52
+ }
53
+ }
54
+ return pks;
55
+ }
56
+ // Drizzle's own cache key: SHA-256(sql + '-' + JSON.stringify(params)) — see
57
+ // drizzle-orm/cache/core/cache.js. Reimplemented (not imported) so a query's
58
+ // hash computed here from `logger.logQuery`'s (sql, params) matches the hash
59
+ // Drizzle later passes to `cache.put()` for the SAME query, letting the two
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.
66
+ async function hashQuery(sql, params) {
67
+ const data = `${sql}-${JSON.stringify(params)}`;
68
+ return createHash('sha256').update(data).digest('hex');
69
+ }
70
+ // A pending SELECT footprint entry, keyed by query hash, waiting to be
71
+ // upgraded by a paired cache's put(). Linked to its logger via WeakMap so the
72
+ // map is GC'd along with the logger if the app never pairs a cache to it.
73
+ const pendingByLogger = new WeakMap();
38
74
  // Creates a Drizzle-compatible logger that records footprints.
39
75
  // Usage: drizzle(client, { logger: createDrizzleLogger() })
40
76
  export function createDrizzleLogger(opts = {}) {
41
77
  const captureWhereShape = opts.captureWhereShape !== false;
42
78
  const forward = opts.forward;
43
- return {
79
+ const pending = new Map();
80
+ const logger = {
44
81
  logQuery(query, params) {
45
82
  try {
46
83
  const ctx = contextStorage.getStore();
@@ -56,8 +93,17 @@ export function createDrizzleLogger(opts = {}) {
56
93
  where_shape: where,
57
94
  count: 0,
58
95
  note: 'drizzle: shape-only (no result access — PKs/count unavailable)',
96
+ resolved: true,
59
97
  };
60
98
  ctx.footprint.push(entry);
99
+ // A paired cache can upgrade this SAME entry once the real rows
100
+ // are known — see createDrizzleCache(). Mutations get nothing
101
+ // more from Drizzle's cache hook, so they stay as pushed above.
102
+ if (op === 'select') {
103
+ hashQuery(query, params)
104
+ .then((h) => pending.set(h, entry))
105
+ .catch(() => { });
106
+ }
61
107
  }
62
108
  }
63
109
  }
@@ -68,5 +114,87 @@ export function createDrizzleLogger(opts = {}) {
68
114
  catch { /* ignore */ }
69
115
  },
70
116
  };
117
+ pendingByLogger.set(logger, pending);
118
+ return logger;
119
+ }
120
+ // node-postgres (`pg`) QueryResult vs. mysql2's `[rows, fields]` tuple — the
121
+ // two response shapes `cache.put()` actually sees for Reprova's two real
122
+ // customer-database dialects (Postgres, MySQL; see agent/internal/dialect).
123
+ // Unrecognized shapes degrade to "no rows found" rather than guessing.
124
+ function rowsFromResponse(response) {
125
+ if (Array.isArray(response)) {
126
+ const [rows] = response; // mysql2: client.query() resolves [rows, fields]
127
+ return Array.isArray(rows) ? rows : undefined;
128
+ }
129
+ const rows = response?.rows;
130
+ return Array.isArray(rows) ? rows : undefined;
131
+ }
132
+ // Creates a Drizzle Cache that observes (never serves) query results to
133
+ // upgrade SELECT footprints with exact primary keys and row counts.
134
+ // Usage: drizzle(client, { cache: createDrizzleCache({ logger }) })
135
+ export function createDrizzleCache(opts = {}) {
136
+ const primaryKey = opts.primaryKey ?? (() => ['id']);
137
+ const pending = opts.logger ? pendingByLogger.get(opts.logger) : undefined;
138
+ return {
139
+ // 'all' — every SELECT is observed automatically; apps never need to
140
+ // call Drizzle's own `.$withCache()` for this to work.
141
+ strategy() {
142
+ return 'all';
143
+ },
144
+ // Always a miss: this cache exists to OBSERVE results, never to REPLACE a
145
+ // real query with a stale one.
146
+ async get() {
147
+ return undefined;
148
+ },
149
+ async put(hashedQuery, response, tables) {
150
+ try {
151
+ if (!tables || tables.length === 0)
152
+ return;
153
+ const rows = rowsFromResponse(response);
154
+ if (!rows)
155
+ return;
156
+ const primary = tables[0];
157
+ const pks = extractPks(rows, primaryKey(primary));
158
+ const count = rows.length;
159
+ const note = count === 0 ? 'returned 0 rows' : undefined;
160
+ const linked = pending?.get(hashedQuery);
161
+ const ctx = contextStorage.getStore();
162
+ if (linked) {
163
+ pending.delete(hashedQuery);
164
+ linked.model = primary;
165
+ linked.pks = pks;
166
+ linked.count = count;
167
+ linked.note = note;
168
+ }
169
+ else if (ctx) {
170
+ ctx.footprint.push({ model: primary, op: 'select', pks, count, note, resolved: true });
171
+ }
172
+ // Joined/subquery tables: recorded as referenced, without PKs — same
173
+ // convention as the Kysely adapter (result columns can't be reliably
174
+ // attributed to a joined table's own primary key).
175
+ if (ctx) {
176
+ for (const t of tables.slice(1)) {
177
+ ctx.footprint.push({ model: t, op: 'select', pks: [], count: 0, note: 'referenced (join/subquery)', resolved: true });
178
+ }
179
+ }
180
+ }
181
+ catch { /* never break a query */ }
182
+ },
183
+ // Drizzle's mutation hook gets table names only — no rows, no count, and
184
+ // it races the real query (Promise.all internally). Nothing usable to
185
+ // record here; mutations are already captured (shape-only) by the paired
186
+ // logger.
187
+ async onMutate() { },
188
+ };
189
+ }
190
+ // Convenience: builds a linked logger+cache pair in one call, the
191
+ // recommended way to get exact SELECT pks/count without double-counting.
192
+ // Usage:
193
+ // const { logger, cache } = createDrizzleFootprint();
194
+ // const db = drizzle(client, { logger, cache });
195
+ export function createDrizzleFootprint(opts = {}) {
196
+ const logger = createDrizzleLogger({ captureWhereShape: opts.captureWhereShape });
197
+ const cache = createDrizzleCache({ primaryKey: opts.primaryKey, logger });
198
+ return { logger, cache };
71
199
  }
72
200
  //# sourceMappingURL=drizzle.js.map
@@ -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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AA4E9C,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF,iCAAiC;AACjC,SAAS,IAAI,CAAC,GAAW;IACvB,MAAM,CAAC,GAAG,sCAAsC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5D,CAAC;AAED,uDAAuD;AACvD,SAAS,OAAO,CAAC,GAAW,EAAE,EAAU;IACtC,IAAI,CAAC,GAA2B,IAAI,CAAC;IACrC,IAAI,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ;QAAE,CAAC,GAAG,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SACjF,IAAI,EAAE,KAAK,QAAQ;QAAE,CAAC,GAAG,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SACnE,IAAI,EAAE,KAAK,QAAQ;QAAE,CAAC,GAAG,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9B,CAAC;AAED,6EAA6E;AAC7E,wEAAwE;AACxE,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACtC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,EAAE,GAAG,kDAAkD,CAAC;IAC9D,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI;QAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CAAC,IAA4C,EAAE,MAAgB;IAChF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;YAAE,SAAS,CAAC,wCAAwC;QACzF,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,wDAAwD;AACxD,wEAAwE;AACxE,qEAAqE;AACrE,wEAAwE;AACxE,0EAA0E;AAC1E,gDAAgD;AAChD,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,MAAiB;IACrD,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAChD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,uEAAuE;AACvE,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,eAAe,GAAG,IAAI,OAAO,EAA8C,CAAC;AAElF,+DAA+D;AAC/D,4DAA4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,OAA6B,EAAE;IACjE,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,KAAK,KAAK,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,MAAM,MAAM,GAAkB;QAC5B,QAAQ,CAAC,KAAa,EAAE,MAAiB;YACvC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;oBACvB,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;oBAClD,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;wBAChB,MAAM,KAAK,GAAG,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;wBACzF,MAAM,KAAK,GAAmB;4BAC5B,KAAK,EAAE,KAAK;4BACZ,EAAE;4BACF,GAAG,EAAE,EAAE;4BACP,WAAW,EAAE,KAAK;4BAClB,KAAK,EAAE,CAAC;4BACR,IAAI,EAAE,gEAAgE;4BACtE,QAAQ,EAAE,IAAI;yBACf,CAAC;wBACF,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBAC1B,gEAAgE;wBAChE,8DAA8D;wBAC9D,gEAAgE;wBAChE,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;4BACpB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;iCACrB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;iCAClC,KAAK,CAAC,GAAG,EAAE,GAA6B,CAAC,CAAC,CAAC;wBAChD,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;YACrC,IAAI,CAAC;gBAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAClE,CAAC;KACF,CAAC;IAEF,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,QAAiB;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,iDAAiD;QAC1E,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAuC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,GAAI,QAA2C,EAAE,IAAI,CAAC;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAuC,CAAC,CAAC,CAAC,SAAS,CAAC;AACpF,CAAC;AAED,wEAAwE;AACxE,oEAAoE;AACpE,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,OAA4B,EAAE;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3E,OAAO;QACL,qEAAqE;QACrE,uDAAuD;QACvD,QAAQ;YACN,OAAO,KAAK,CAAC;QACf,CAAC;QAED,0EAA0E;QAC1E,+BAA+B;QAC/B,KAAK,CAAC,GAAG;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,WAAmB,EAAE,QAAiB,EAAE,MAAgB;YAChE,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBAC3C,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;gBACxC,IAAI,CAAC,IAAI;oBAAE,OAAO;gBAClB,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;gBAC3B,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;gBAClD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC1B,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;gBAEzD,MAAM,MAAM,GAAG,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;gBACzC,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,MAAM,EAAE,CAAC;oBACX,OAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBAC7B,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;oBACvB,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;oBACjB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;oBACrB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACrB,CAAC;qBAAM,IAAI,GAAG,EAAE,CAAC;oBACf,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzF,CAAC;gBAED,qEAAqE;gBACrE,qEAAqE;gBACrE,mDAAmD;gBACnD,IAAI,GAAG,EAAE,CAAC;oBACR,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;wBAChC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;oBACxH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;QACvC,CAAC;QAED,yEAAyE;QACzE,sEAAsE;QACtE,yEAAyE;QACzE,UAAU;QACV,KAAK,CAAC,QAAQ,KAAmB,CAAC;KACnC,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,yEAAyE;AACzE,SAAS;AACT,wDAAwD;AACxD,mDAAmD;AACnD,MAAM,UAAU,sBAAsB,CACpC,OAAgC,EAAE;IAElC,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,kBAAkB,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface FrameworkAdapter {
2
+ extractAuthClaims?(rawRequest: unknown): Record<string, unknown> | undefined;
3
+ resolveMigrationId?(): Promise<string>;
4
+ }
5
+ //# sourceMappingURL=frameworkAdapter.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=frameworkAdapter.js.map
@@ -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