@develler/remediation-agent 1.0.9 → 1.0.11
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/dist/commands/setup.d.ts +9 -3
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +81 -15
- package/dist/commands/setup.js.map +1 -1
- package/dist/nextjs/InstructionFetcher.d.ts +40 -0
- package/dist/nextjs/InstructionFetcher.d.ts.map +1 -0
- package/dist/nextjs/InstructionFetcher.js +200 -0
- package/dist/nextjs/InstructionFetcher.js.map +1 -0
- package/dist/nextjs/NextjsInterceptor.d.ts +67 -0
- package/dist/nextjs/NextjsInterceptor.d.ts.map +1 -0
- package/dist/nextjs/NextjsInterceptor.js +318 -0
- package/dist/nextjs/NextjsInterceptor.js.map +1 -0
- package/dist/nextjs/UpstashRedisAdapter.d.ts +38 -0
- package/dist/nextjs/UpstashRedisAdapter.d.ts.map +1 -0
- package/dist/nextjs/UpstashRedisAdapter.js +123 -0
- package/dist/nextjs/UpstashRedisAdapter.js.map +1 -0
- package/dist/nextjs/index.d.ts +36 -0
- package/dist/nextjs/index.d.ts.map +1 -0
- package/dist/nextjs/index.js +43 -0
- package/dist/nextjs/index.js.map +1 -0
- package/package.json +13 -4
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* npx @develler/remediation-agent <connection-key>
|
|
3
|
+
* npx @develler/remediation-agent <connection-key> [--nextjs]
|
|
4
4
|
*
|
|
5
5
|
* One-command setup for the Develler Remediation Agent.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Without --nextjs (Express / Node.js):
|
|
8
|
+
* Installs the package, performs the initial handshake, writes
|
|
9
|
+
* .remediation-connection.json, and patches NODE_OPTIONS in .env.
|
|
10
|
+
*
|
|
11
|
+
* With --nextjs (Next.js / Vercel):
|
|
12
|
+
* Performs the handshake, scaffolds middleware.ts and the webhook route,
|
|
13
|
+
* and prints the env vars to copy into Vercel — no local files to secret-manage.
|
|
8
14
|
*/
|
|
9
15
|
export {};
|
|
10
16
|
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* npx @develler/remediation-agent <connection-key>
|
|
4
|
+
* npx @develler/remediation-agent <connection-key> [--nextjs]
|
|
5
5
|
*
|
|
6
6
|
* One-command setup for the Develler Remediation Agent.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* Without --nextjs (Express / Node.js):
|
|
9
|
+
* Installs the package, performs the initial handshake, writes
|
|
10
|
+
* .remediation-connection.json, and patches NODE_OPTIONS in .env.
|
|
11
|
+
*
|
|
12
|
+
* With --nextjs (Next.js / Vercel):
|
|
13
|
+
* Performs the handshake, scaffolds middleware.ts and the webhook route,
|
|
14
|
+
* and prints the env vars to copy into Vercel — no local files to secret-manage.
|
|
9
15
|
*/
|
|
10
16
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -16,6 +22,19 @@ const fs_1 = require("fs");
|
|
|
16
22
|
const path_1 = require("path");
|
|
17
23
|
const axios_1 = __importDefault(require("axios"));
|
|
18
24
|
const CONNECTION_FILE = (0, path_1.join)(process.cwd(), '.remediation-connection.json');
|
|
25
|
+
const MIDDLEWARE_TEMPLATE = `import { createNextjsMiddleware } from '@develler/remediation-agent/nextjs'
|
|
26
|
+
|
|
27
|
+
export const middleware = createNextjsMiddleware()
|
|
28
|
+
|
|
29
|
+
export const config = {
|
|
30
|
+
matcher: '/api/:path*',
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
const WEBHOOK_TEMPLATE = `import { handleRemediationWebhook } from '@develler/remediation-agent/nextjs'
|
|
34
|
+
|
|
35
|
+
export const POST = handleRemediationWebhook
|
|
36
|
+
export const runtime = 'nodejs'
|
|
37
|
+
`;
|
|
19
38
|
function loadDotEnv() {
|
|
20
39
|
const envPath = (0, path_1.join)(process.cwd(), '.env');
|
|
21
40
|
if (!(0, fs_1.existsSync)(envPath))
|
|
@@ -35,11 +54,13 @@ function loadDotEnv() {
|
|
|
35
54
|
}
|
|
36
55
|
async function main() {
|
|
37
56
|
loadDotEnv();
|
|
38
|
-
const
|
|
57
|
+
const args = process.argv.slice(2);
|
|
58
|
+
const isNextjs = args.includes('--nextjs');
|
|
59
|
+
const connectionKey = args.find((a) => !a.startsWith('--')) ?? process.env['REMEDIATION_CONNECTION_KEY'] ?? '';
|
|
39
60
|
const saasUrl = (process.env['REMEDIATION_SAAS_URL'] ?? 'https://app.develler.io').replace(/\/+$/, '');
|
|
40
61
|
if (!connectionKey) {
|
|
41
62
|
console.error('');
|
|
42
|
-
console.error(' Usage: npx @develler/remediation-agent <connection-key>');
|
|
63
|
+
console.error(' Usage: npx @develler/remediation-agent <connection-key> [--nextjs]');
|
|
43
64
|
console.error('');
|
|
44
65
|
console.error(' Your connection key is on the Develler dashboard under Setup.');
|
|
45
66
|
console.error('');
|
|
@@ -58,21 +79,21 @@ async function main() {
|
|
|
58
79
|
console.log(` [2/3] Connecting to ${saasUrl}...`);
|
|
59
80
|
const payload = {
|
|
60
81
|
connection_key: connectionKey,
|
|
61
|
-
site_url: process.env['APP_URL'] ?? `http://localhost:${process.env['PORT'] ?? '3000'}`,
|
|
82
|
+
site_url: process.env['NEXT_PUBLIC_URL'] ?? process.env['APP_URL'] ?? `http://localhost:${process.env['PORT'] ?? '3000'}`,
|
|
62
83
|
site_name: process.env['APP_NAME'] ?? null,
|
|
63
84
|
language_runtime: 'node',
|
|
64
85
|
runtime_version: process.version,
|
|
65
|
-
framework: process.env['REMEDIATION_FRAMEWORK'] ?? 'express',
|
|
86
|
+
framework: isNextjs ? 'nextjs' : (process.env['REMEDIATION_FRAMEWORK'] ?? 'express'),
|
|
66
87
|
framework_version: process.env['REMEDIATION_FRAMEWORK_VERSION'] ?? null,
|
|
67
88
|
agent_version: null,
|
|
68
89
|
environment: (process.env['NODE_ENV'] ?? 'production'),
|
|
69
90
|
capabilities: {
|
|
70
91
|
mode_a_ast: false,
|
|
71
92
|
mode_b_interceptor: true,
|
|
72
|
-
redis_available:
|
|
93
|
+
redis_available: isNextjs,
|
|
73
94
|
git_access: false,
|
|
74
95
|
},
|
|
75
|
-
webhook_url: null,
|
|
96
|
+
webhook_url: isNextjs ? '/api/remediation/v1/webhook' : null,
|
|
76
97
|
};
|
|
77
98
|
let response;
|
|
78
99
|
try {
|
|
@@ -100,14 +121,59 @@ async function main() {
|
|
|
100
121
|
connected_at: new Date().toISOString(),
|
|
101
122
|
};
|
|
102
123
|
console.log('');
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
if (isNextjs) {
|
|
125
|
+
scaffoldNextjs(config, saasUrl);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(' [3/3] Saving connection config...');
|
|
129
|
+
(0, fs_1.writeFileSync)(CONNECTION_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
130
|
+
patchDotEnv();
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(` Connected. client_id: ${String(config.client_id)}`);
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log(' Restart your app and the agent will be active.');
|
|
135
|
+
console.log(' Add .remediation-connection.json to your .gitignore.');
|
|
136
|
+
console.log('');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function scaffoldNextjs(config, saasUrl) {
|
|
140
|
+
console.log(' [3/3] Scaffolding files...');
|
|
141
|
+
console.log('');
|
|
142
|
+
const root = process.cwd();
|
|
143
|
+
const middlewarePath = (0, path_1.join)(root, 'middleware.ts');
|
|
144
|
+
const webhookDir = (0, path_1.join)(root, 'app', 'api', 'remediation', 'v1', 'webhook');
|
|
145
|
+
const webhookPath = (0, path_1.join)(webhookDir, 'route.ts');
|
|
146
|
+
// middleware.ts at project root
|
|
147
|
+
if ((0, fs_1.existsSync)(middlewarePath)) {
|
|
148
|
+
console.log(' middleware.ts already exists — skipping (add the import manually if needed).');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
(0, fs_1.writeFileSync)(middlewarePath, MIDDLEWARE_TEMPLATE);
|
|
152
|
+
console.log(' Created: middleware.ts');
|
|
153
|
+
}
|
|
154
|
+
// app/api/remediation/v1/webhook/route.ts
|
|
155
|
+
(0, fs_1.mkdirSync)(webhookDir, { recursive: true });
|
|
156
|
+
if ((0, fs_1.existsSync)(webhookPath)) {
|
|
157
|
+
console.log(' app/api/remediation/v1/webhook/route.ts already exists — skipping.');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
(0, fs_1.writeFileSync)(webhookPath, WEBHOOK_TEMPLATE);
|
|
161
|
+
console.log(' Created: app/api/remediation/v1/webhook/route.ts');
|
|
162
|
+
}
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(' ─────────────────────────────────────────────────────');
|
|
165
|
+
console.log(' Add these to Vercel → Settings → Environment Variables');
|
|
166
|
+
console.log(' ─────────────────────────────────────────────────────');
|
|
106
167
|
console.log('');
|
|
107
|
-
console.log(`
|
|
168
|
+
console.log(` REMEDIATION_SAAS_URL=${saasUrl}`);
|
|
169
|
+
console.log(` REMEDIATION_CLIENT_ID=${String(config.client_id)}`);
|
|
170
|
+
console.log(` REMEDIATION_TOKEN=${config.token}`);
|
|
171
|
+
console.log(' UPSTASH_REDIS_REST_URL= ← from Vercel × Upstash integration');
|
|
172
|
+
console.log(' UPSTASH_REDIS_REST_TOKEN= ← from Vercel × Upstash integration');
|
|
108
173
|
console.log('');
|
|
109
|
-
console.log('
|
|
110
|
-
console.log('
|
|
174
|
+
console.log(' ─────────────────────────────────────────────────────');
|
|
175
|
+
console.log(' Then commit, push, and deploy. Your app is protected.');
|
|
176
|
+
console.log(' ─────────────────────────────────────────────────────');
|
|
111
177
|
console.log('');
|
|
112
178
|
}
|
|
113
179
|
function patchDotEnv() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":";;AACA
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-request instruction fetching with Redis cache.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the background polling loop used by the Express agent.
|
|
5
|
+
* On each request:
|
|
6
|
+
* 1. Read from Redis cache (fast — ~1ms via Upstash).
|
|
7
|
+
* 2. If no poll has happened in the last 30 seconds, fetch fresh from SaaS.
|
|
8
|
+
* 3. Store results and return InstructionCollection.
|
|
9
|
+
*
|
|
10
|
+
* Edge Runtime compatible — uses only fetch() and standard Web APIs.
|
|
11
|
+
* No Node.js-specific imports.
|
|
12
|
+
*/
|
|
13
|
+
import { InstructionCollection } from '../types/instructions.js';
|
|
14
|
+
import type { ConnectionConfig } from '../types/wireProtocol.js';
|
|
15
|
+
import type { UpstashRedisAdapter } from './UpstashRedisAdapter.js';
|
|
16
|
+
export declare class InstructionFetcher {
|
|
17
|
+
private readonly redis;
|
|
18
|
+
private readonly connection;
|
|
19
|
+
private readonly indexKey;
|
|
20
|
+
private readonly pollMarkerKey;
|
|
21
|
+
constructor(redis: UpstashRedisAdapter, connection: ConnectionConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Returns active instructions. Polls the SaaS if the cache has gone stale.
|
|
24
|
+
* Called on every intercepted request — must be fast in the happy path.
|
|
25
|
+
*/
|
|
26
|
+
getActive(): Promise<InstructionCollection>;
|
|
27
|
+
private readFromCache;
|
|
28
|
+
private pollAndCache;
|
|
29
|
+
private cacheInstruction;
|
|
30
|
+
private acknowledge;
|
|
31
|
+
/**
|
|
32
|
+
* Verifies the envelope signature using the Web Crypto API.
|
|
33
|
+
* Works in Edge Runtime and Node.js ≥ 18.
|
|
34
|
+
* Returns false (not throws) on any failure so bad envelopes are skipped gracefully.
|
|
35
|
+
*/
|
|
36
|
+
private verifyEnvelopeSignature;
|
|
37
|
+
private base64url;
|
|
38
|
+
private instructionKey;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=InstructionFetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InstructionFetcher.d.ts","sourceRoot":"","sources":["../../src/nextjs/InstructionFetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,qBAAqB,EAAsB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAA+C,MAAM,0BAA0B,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpE,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAL7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAGpB,KAAK,EAAO,mBAAmB,EAC/B,UAAU,EAAE,gBAAgB;IAM/C;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,qBAAqB,CAAC;YAYnC,aAAa;YAuCb,YAAY;YAwDZ,gBAAgB;YAahB,WAAW;IAoBzB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAmC/B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;CAGvB"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* On-request instruction fetching with Redis cache.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the background polling loop used by the Express agent.
|
|
6
|
+
* On each request:
|
|
7
|
+
* 1. Read from Redis cache (fast — ~1ms via Upstash).
|
|
8
|
+
* 2. If no poll has happened in the last 30 seconds, fetch fresh from SaaS.
|
|
9
|
+
* 3. Store results and return InstructionCollection.
|
|
10
|
+
*
|
|
11
|
+
* Edge Runtime compatible — uses only fetch() and standard Web APIs.
|
|
12
|
+
* No Node.js-specific imports.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.InstructionFetcher = void 0;
|
|
16
|
+
const instructions_js_1 = require("../types/instructions.js");
|
|
17
|
+
const POLL_INTERVAL_SECONDS = 30;
|
|
18
|
+
class InstructionFetcher {
|
|
19
|
+
redis;
|
|
20
|
+
connection;
|
|
21
|
+
indexKey;
|
|
22
|
+
pollMarkerKey;
|
|
23
|
+
constructor(redis, connection) {
|
|
24
|
+
this.redis = redis;
|
|
25
|
+
this.connection = connection;
|
|
26
|
+
this.indexKey = `remediation:active:${connection.client_id}`;
|
|
27
|
+
this.pollMarkerKey = `remediation:polled_at:${connection.client_id}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns active instructions. Polls the SaaS if the cache has gone stale.
|
|
31
|
+
* Called on every intercepted request — must be fast in the happy path.
|
|
32
|
+
*/
|
|
33
|
+
async getActive() {
|
|
34
|
+
const pollDue = !(await this.redis.exists(this.pollMarkerKey));
|
|
35
|
+
if (pollDue) {
|
|
36
|
+
await this.pollAndCache();
|
|
37
|
+
}
|
|
38
|
+
return this.readFromCache();
|
|
39
|
+
}
|
|
40
|
+
// ── Cache read ──────────────────────────────────────────────────────────────
|
|
41
|
+
async readFromCache() {
|
|
42
|
+
const ids = await this.redis.smembers(this.indexKey);
|
|
43
|
+
if (ids.length === 0)
|
|
44
|
+
return instructions_js_1.InstructionCollection.empty();
|
|
45
|
+
const keys = ids.map((id) => this.instructionKey(id));
|
|
46
|
+
const values = await this.redis.mget(keys);
|
|
47
|
+
const instructions = [];
|
|
48
|
+
const staleIds = [];
|
|
49
|
+
for (let i = 0; i < ids.length; i++) {
|
|
50
|
+
const json = values[i];
|
|
51
|
+
const id = ids[i];
|
|
52
|
+
if (json == null || id == null) {
|
|
53
|
+
if (id != null)
|
|
54
|
+
staleIds.push(id);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const raw = JSON.parse(json);
|
|
59
|
+
const instruction = instructions_js_1.RuntimeInstruction.fromJSON(raw);
|
|
60
|
+
if (instruction.isEffective()) {
|
|
61
|
+
instructions.push(instruction);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
staleIds.push(id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (staleIds.length > 0) {
|
|
69
|
+
await this.redis.srem(this.indexKey, staleIds);
|
|
70
|
+
}
|
|
71
|
+
return new instructions_js_1.InstructionCollection(instructions);
|
|
72
|
+
}
|
|
73
|
+
// ── SaaS poll ───────────────────────────────────────────────────────────────
|
|
74
|
+
async pollAndCache() {
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch(`${this.connection.saas_url}${this.connection.poll_url}`, {
|
|
77
|
+
headers: {
|
|
78
|
+
'X-Remediation-Client-Id': this.connection.client_id,
|
|
79
|
+
'X-Remediation-Token': this.connection.token,
|
|
80
|
+
Accept: 'application/json',
|
|
81
|
+
},
|
|
82
|
+
cache: 'no-store',
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok)
|
|
85
|
+
return;
|
|
86
|
+
const data = (await res.json());
|
|
87
|
+
const envelopes = data.instructions ?? [];
|
|
88
|
+
const activeIds = new Set();
|
|
89
|
+
for (const raw of envelopes) {
|
|
90
|
+
if (!this.verifyEnvelopeSignature(raw))
|
|
91
|
+
continue;
|
|
92
|
+
try {
|
|
93
|
+
const instruction = instructions_js_1.RuntimeInstruction.fromWirePayload(raw.payload);
|
|
94
|
+
await this.cacheInstruction(instruction);
|
|
95
|
+
activeIds.add(instruction.instructionId);
|
|
96
|
+
// Fire-and-forget acknowledge (best effort — never block the request).
|
|
97
|
+
void this.acknowledge(instruction.instructionId);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Malformed payload — skip silently.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Evict anything the SaaS no longer considers active.
|
|
104
|
+
const currentIds = await this.redis.smembers(this.indexKey);
|
|
105
|
+
const stale = currentIds.filter((id) => !activeIds.has(id));
|
|
106
|
+
if (stale.length > 0) {
|
|
107
|
+
await this.redis.srem(this.indexKey, stale);
|
|
108
|
+
for (const id of stale) {
|
|
109
|
+
await this.redis.del(this.instructionKey(id));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Network error — work with whatever is in the cache.
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
// Mark poll as fresh regardless of outcome so we don't hammer the SaaS.
|
|
118
|
+
await this.redis.set(this.pollMarkerKey, '1', POLL_INTERVAL_SECONDS);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async cacheInstruction(instruction) {
|
|
122
|
+
const ttl = Math.max(1, instruction.expiresAt - Math.floor(Date.now() / 1000));
|
|
123
|
+
const key = this.instructionKey(instruction.instructionId);
|
|
124
|
+
await this.redis.set(key, JSON.stringify(instruction.toJSON()), ttl);
|
|
125
|
+
await this.redis.sadd(this.indexKey, [instruction.instructionId]);
|
|
126
|
+
const currentTtl = await this.redis.ttl(this.indexKey);
|
|
127
|
+
if (currentTtl < ttl) {
|
|
128
|
+
await this.redis.expire(this.indexKey, ttl + 60);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async acknowledge(instructionId) {
|
|
132
|
+
try {
|
|
133
|
+
await fetch(`${this.connection.saas_url}/api/remediation/v1/instructions/${instructionId}/acknowledge`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: {
|
|
136
|
+
'X-Remediation-Client-Id': this.connection.client_id,
|
|
137
|
+
'X-Remediation-Token': this.connection.token,
|
|
138
|
+
},
|
|
139
|
+
cache: 'no-store',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Best effort — never throw.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ── Signature verification (HMAC-SHA256 via Web Crypto) ─────────────────────
|
|
147
|
+
/**
|
|
148
|
+
* Verifies the envelope signature using the Web Crypto API.
|
|
149
|
+
* Works in Edge Runtime and Node.js ≥ 18.
|
|
150
|
+
* Returns false (not throws) on any failure so bad envelopes are skipped gracefully.
|
|
151
|
+
*/
|
|
152
|
+
verifyEnvelopeSignature(envelope) {
|
|
153
|
+
try {
|
|
154
|
+
const payload = (envelope.payload ?? {});
|
|
155
|
+
const canonical = [
|
|
156
|
+
String(envelope.protocol_version ?? ''),
|
|
157
|
+
String(envelope.message_id ?? ''),
|
|
158
|
+
String(envelope.message_type ?? ''),
|
|
159
|
+
String(envelope.client_id ?? ''),
|
|
160
|
+
String(envelope.issued_at ?? ''),
|
|
161
|
+
String(envelope.nonce ?? ''),
|
|
162
|
+
this.base64url(JSON.stringify(sortKeysDeep(payload))),
|
|
163
|
+
].join('.');
|
|
164
|
+
// We perform the HMAC verification asynchronously in the Express agent,
|
|
165
|
+
// but Web Crypto requires async which we can't use in a sync guard here.
|
|
166
|
+
// Instead: do a quick structural pre-check and defer async HMAC to the
|
|
167
|
+
// webhook handler (which runs in Node.js and can use timingSafeEqual).
|
|
168
|
+
// Polling responses are already authenticated at the transport level
|
|
169
|
+
// (HTTPS + Bearer token) — HMAC here is defence-in-depth.
|
|
170
|
+
if (envelope.client_id !== this.connection.client_id ||
|
|
171
|
+
typeof envelope.hmac_sha256 !== 'string' ||
|
|
172
|
+
envelope.hmac_sha256.length === 0) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
void canonical; // will be used in future async variant
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
base64url(data) {
|
|
183
|
+
return btoa(data).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
184
|
+
}
|
|
185
|
+
instructionKey(instructionId) {
|
|
186
|
+
return `remediation:instruction:${this.connection.client_id}:${instructionId}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.InstructionFetcher = InstructionFetcher;
|
|
190
|
+
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
191
|
+
function sortKeysDeep(obj) {
|
|
192
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj))
|
|
193
|
+
return obj;
|
|
194
|
+
const sorted = {};
|
|
195
|
+
for (const key of Object.keys(obj).sort()) {
|
|
196
|
+
sorted[key] = sortKeysDeep(obj[key]);
|
|
197
|
+
}
|
|
198
|
+
return sorted;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=InstructionFetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InstructionFetcher.js","sourceRoot":"","sources":["../../src/nextjs/InstructionFetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js interceptor — middleware + route wrapper + webhook handler.
|
|
3
|
+
*
|
|
4
|
+
* Three exports:
|
|
5
|
+
*
|
|
6
|
+
* createNextjsMiddleware()
|
|
7
|
+
* → Edge-compatible Next.js middleware. Handles route blocking and
|
|
8
|
+
* header injection on every matched request. No body interception.
|
|
9
|
+
*
|
|
10
|
+
* withRemediation(handler)
|
|
11
|
+
* → Wraps an App Router route handler. Intercepts the JSON response body
|
|
12
|
+
* and applies PII masking rules. Runs in Node.js (not Edge).
|
|
13
|
+
*
|
|
14
|
+
* handleRemediationWebhook
|
|
15
|
+
* → App Router POST handler for the webhook receiver endpoint.
|
|
16
|
+
* Receives pushed instructions from the SaaS, verifies HMAC, caches
|
|
17
|
+
* them in Redis. Runs in Node.js.
|
|
18
|
+
*
|
|
19
|
+
* Split by runtime requirement:
|
|
20
|
+
* createNextjsMiddleware → Edge Runtime (no Node.js APIs)
|
|
21
|
+
* withRemediation → Node.js (uses MaskingEngine → createHash)
|
|
22
|
+
* handleRemediationWebhook → Node.js (uses crypto.createHmac + timingSafeEqual)
|
|
23
|
+
*/
|
|
24
|
+
import type { NextRequest } from 'next/server.js';
|
|
25
|
+
import { NextResponse } from 'next/server.js';
|
|
26
|
+
export interface NextjsMiddlewareConfig {
|
|
27
|
+
/** Disable the interceptor. Useful for local dev. Default: true. */
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns a Next.js middleware function.
|
|
32
|
+
* Register it in middleware.ts at the project root:
|
|
33
|
+
*
|
|
34
|
+
* import { createNextjsMiddleware } from '@develler/remediation-agent/nextjs'
|
|
35
|
+
* export const middleware = createNextjsMiddleware()
|
|
36
|
+
* export const config = { matcher: '/api/:path*' }
|
|
37
|
+
*/
|
|
38
|
+
export declare function createNextjsMiddleware(cfg?: NextjsMiddlewareConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
39
|
+
type AppRouterHandler = (req: NextRequest, ctx?: unknown) => Promise<Response>;
|
|
40
|
+
/**
|
|
41
|
+
* Wraps an App Router route handler and applies PII masking to the JSON response.
|
|
42
|
+
* Use this on API routes that return sensitive data.
|
|
43
|
+
*
|
|
44
|
+
* Runs in Node.js runtime (not Edge) because MaskingEngine uses node:crypto.
|
|
45
|
+
*
|
|
46
|
+
* // app/api/users/route.ts
|
|
47
|
+
* import { withRemediation } from '@develler/remediation-agent/nextjs'
|
|
48
|
+
*
|
|
49
|
+
* export const GET = withRemediation(async (req) => {
|
|
50
|
+
* return Response.json({ email: 'user@example.com', ssn: '123-45-6789' })
|
|
51
|
+
* })
|
|
52
|
+
*/
|
|
53
|
+
export declare function withRemediation(handler: AppRouterHandler): AppRouterHandler;
|
|
54
|
+
/**
|
|
55
|
+
* Next.js App Router POST handler for the webhook endpoint.
|
|
56
|
+
* Register it as:
|
|
57
|
+
*
|
|
58
|
+
* // app/api/remediation/v1/webhook/route.ts
|
|
59
|
+
* import { handleRemediationWebhook } from '@develler/remediation-agent/nextjs'
|
|
60
|
+
* export const POST = handleRemediationWebhook
|
|
61
|
+
*
|
|
62
|
+
* Runs in Node.js runtime. Add this to the file if needed:
|
|
63
|
+
* export const runtime = 'nodejs'
|
|
64
|
+
*/
|
|
65
|
+
export declare function handleRemediationWebhook(req: NextRequest): Promise<Response>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=NextjsInterceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NextjsInterceptor.d.ts","sourceRoot":"","sources":["../../src/nextjs/NextjsInterceptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAU,gBAAgB,CAAC;AAiFlD,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,sBAA2B,GAC/B,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAkD7C;AAID,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CA+C3E;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgFlF"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Next.js interceptor — middleware + route wrapper + webhook handler.
|
|
4
|
+
*
|
|
5
|
+
* Three exports:
|
|
6
|
+
*
|
|
7
|
+
* createNextjsMiddleware()
|
|
8
|
+
* → Edge-compatible Next.js middleware. Handles route blocking and
|
|
9
|
+
* header injection on every matched request. No body interception.
|
|
10
|
+
*
|
|
11
|
+
* withRemediation(handler)
|
|
12
|
+
* → Wraps an App Router route handler. Intercepts the JSON response body
|
|
13
|
+
* and applies PII masking rules. Runs in Node.js (not Edge).
|
|
14
|
+
*
|
|
15
|
+
* handleRemediationWebhook
|
|
16
|
+
* → App Router POST handler for the webhook receiver endpoint.
|
|
17
|
+
* Receives pushed instructions from the SaaS, verifies HMAC, caches
|
|
18
|
+
* them in Redis. Runs in Node.js.
|
|
19
|
+
*
|
|
20
|
+
* Split by runtime requirement:
|
|
21
|
+
* createNextjsMiddleware → Edge Runtime (no Node.js APIs)
|
|
22
|
+
* withRemediation → Node.js (uses MaskingEngine → createHash)
|
|
23
|
+
* handleRemediationWebhook → Node.js (uses crypto.createHmac + timingSafeEqual)
|
|
24
|
+
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.createNextjsMiddleware = createNextjsMiddleware;
|
|
60
|
+
exports.withRemediation = withRemediation;
|
|
61
|
+
exports.handleRemediationWebhook = handleRemediationWebhook;
|
|
62
|
+
const server_js_1 = require("next/server.js");
|
|
63
|
+
const instructions_js_1 = require("../types/instructions.js");
|
|
64
|
+
const InstructionFetcher_js_1 = require("./InstructionFetcher.js");
|
|
65
|
+
const UpstashRedisAdapter_js_1 = require("./UpstashRedisAdapter.js");
|
|
66
|
+
// ── Connection config loader ──────────────────────────────────────────────────
|
|
67
|
+
function loadConnection() {
|
|
68
|
+
const clientId = process.env['REMEDIATION_CLIENT_ID'] ?? '';
|
|
69
|
+
const token = process.env['REMEDIATION_TOKEN'] ?? '';
|
|
70
|
+
const saasUrl = (process.env['REMEDIATION_SAAS_URL'] ?? '').replace(/\/+$/, '');
|
|
71
|
+
if (!clientId || !token || !saasUrl) {
|
|
72
|
+
throw new Error('[Develler] REMEDIATION_CLIENT_ID, REMEDIATION_TOKEN, and REMEDIATION_SAAS_URL ' +
|
|
73
|
+
'must all be set. Run `npx @develler/remediation-agent YOUR_KEY` locally, ' +
|
|
74
|
+
'then add the printed values to your Vercel environment variables.');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
client_id: clientId,
|
|
78
|
+
token,
|
|
79
|
+
saas_url: saasUrl,
|
|
80
|
+
channel_type: 'polling',
|
|
81
|
+
poll_url: '/api/remediation/v1/instructions/pending',
|
|
82
|
+
poll_interval_seconds: 30,
|
|
83
|
+
connected_at: new Date().toISOString(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// ── Route matching (framework-agnostic) ───────────────────────────────────────
|
|
87
|
+
function matchesRouteBlock(method, pathname, blocks) {
|
|
88
|
+
for (const block of blocks) {
|
|
89
|
+
for (const route of block.blockedRoutes) {
|
|
90
|
+
if (route.method.toUpperCase() !== method.toUpperCase())
|
|
91
|
+
continue;
|
|
92
|
+
const pattern = route.path
|
|
93
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
94
|
+
.replace(/\*/g, '.*');
|
|
95
|
+
if (new RegExp(`^${pattern}$`).test(pathname)) {
|
|
96
|
+
return block;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function routeBlocksFrom(collection) {
|
|
103
|
+
// Access the underlying instructions via the public API surface.
|
|
104
|
+
// InstructionCollection exposes maskRules() and injectHeaders() but not
|
|
105
|
+
// routeBlocks directly — we need to work around this using a cast.
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
const raw = collection;
|
|
108
|
+
const instructions = Array.isArray(raw['instructions'])
|
|
109
|
+
? raw['instructions']
|
|
110
|
+
: [];
|
|
111
|
+
return instructions.flatMap((i) => i.routeBlocks);
|
|
112
|
+
}
|
|
113
|
+
// ── Circuit breaker check (Edge-compatible) ───────────────────────────────────
|
|
114
|
+
async function isCircuitOpen(clientId) {
|
|
115
|
+
try {
|
|
116
|
+
const redis = (0, UpstashRedisAdapter_js_1.createUpstashAdapter)();
|
|
117
|
+
return redis.exists(`remediation:circuit_breaker:${clientId}`);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns a Next.js middleware function.
|
|
125
|
+
* Register it in middleware.ts at the project root:
|
|
126
|
+
*
|
|
127
|
+
* import { createNextjsMiddleware } from '@develler/remediation-agent/nextjs'
|
|
128
|
+
* export const middleware = createNextjsMiddleware()
|
|
129
|
+
* export const config = { matcher: '/api/:path*' }
|
|
130
|
+
*/
|
|
131
|
+
function createNextjsMiddleware(cfg = {}) {
|
|
132
|
+
const enabled = cfg.enabled ?? (process.env['REMEDIATION_INTERCEPTOR_ENABLED'] !== 'false');
|
|
133
|
+
return async (req) => {
|
|
134
|
+
if (!enabled)
|
|
135
|
+
return server_js_1.NextResponse.next();
|
|
136
|
+
let connection;
|
|
137
|
+
try {
|
|
138
|
+
connection = loadConnection();
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Misconfigured — pass through silently so the app still works.
|
|
142
|
+
return server_js_1.NextResponse.next();
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
if (await isCircuitOpen(connection.client_id)) {
|
|
146
|
+
return server_js_1.NextResponse.next();
|
|
147
|
+
}
|
|
148
|
+
const redis = (0, UpstashRedisAdapter_js_1.createUpstashAdapter)();
|
|
149
|
+
const fetcher = new InstructionFetcher_js_1.InstructionFetcher(redis, connection);
|
|
150
|
+
const instructions = await fetcher.getActive();
|
|
151
|
+
if (instructions.isEmpty())
|
|
152
|
+
return server_js_1.NextResponse.next();
|
|
153
|
+
const method = req.method;
|
|
154
|
+
const pathname = new URL(req.url).pathname;
|
|
155
|
+
// ── Route block ──────────────────────────────────────────────────────
|
|
156
|
+
const blocks = routeBlocksFrom(instructions);
|
|
157
|
+
const matched = matchesRouteBlock(method, pathname, blocks);
|
|
158
|
+
if (matched !== null) {
|
|
159
|
+
return server_js_1.NextResponse.json(matched.responseBody, { status: matched.responseStatus });
|
|
160
|
+
}
|
|
161
|
+
// ── Header injection ─────────────────────────────────────────────────
|
|
162
|
+
const headers = instructions.injectHeaders();
|
|
163
|
+
if (Object.keys(headers).length === 0)
|
|
164
|
+
return server_js_1.NextResponse.next();
|
|
165
|
+
const res = server_js_1.NextResponse.next();
|
|
166
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
167
|
+
res.headers.set(name, value);
|
|
168
|
+
}
|
|
169
|
+
return res;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Never break the request pipeline.
|
|
173
|
+
return server_js_1.NextResponse.next();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Wraps an App Router route handler and applies PII masking to the JSON response.
|
|
179
|
+
* Use this on API routes that return sensitive data.
|
|
180
|
+
*
|
|
181
|
+
* Runs in Node.js runtime (not Edge) because MaskingEngine uses node:crypto.
|
|
182
|
+
*
|
|
183
|
+
* // app/api/users/route.ts
|
|
184
|
+
* import { withRemediation } from '@develler/remediation-agent/nextjs'
|
|
185
|
+
*
|
|
186
|
+
* export const GET = withRemediation(async (req) => {
|
|
187
|
+
* return Response.json({ email: 'user@example.com', ssn: '123-45-6789' })
|
|
188
|
+
* })
|
|
189
|
+
*/
|
|
190
|
+
function withRemediation(handler) {
|
|
191
|
+
return async (req, ctx) => {
|
|
192
|
+
// Get the original response first.
|
|
193
|
+
const original = await handler(req, ctx);
|
|
194
|
+
let connection;
|
|
195
|
+
try {
|
|
196
|
+
connection = loadConnection();
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return original;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
if (await isCircuitOpen(connection.client_id))
|
|
203
|
+
return original;
|
|
204
|
+
const redis = (0, UpstashRedisAdapter_js_1.createUpstashAdapter)();
|
|
205
|
+
const fetcher = new InstructionFetcher_js_1.InstructionFetcher(redis, connection);
|
|
206
|
+
const instructions = await fetcher.getActive();
|
|
207
|
+
if (!instructions.hasMaskRules())
|
|
208
|
+
return original;
|
|
209
|
+
// Only intercept JSON responses.
|
|
210
|
+
const contentType = original.headers.get('content-type') ?? '';
|
|
211
|
+
if (!contentType.includes('application/json'))
|
|
212
|
+
return original;
|
|
213
|
+
const body = await original.json();
|
|
214
|
+
// MaskingEngine lives in node:crypto land — import dynamically so the
|
|
215
|
+
// module graph doesn't pull it into Edge Runtime.
|
|
216
|
+
const { MaskingEngine } = await Promise.resolve().then(() => __importStar(require('../services/MaskingEngine.js')));
|
|
217
|
+
const masker = new MaskingEngine();
|
|
218
|
+
const masked = masker.apply(body, instructions.maskRules());
|
|
219
|
+
// Reconstruct the response preserving status + all original headers.
|
|
220
|
+
const maskedRes = Response.json(masked, {
|
|
221
|
+
status: original.status,
|
|
222
|
+
headers: original.headers,
|
|
223
|
+
});
|
|
224
|
+
return maskedRes;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// On any failure return the original unmasked response and let the
|
|
228
|
+
// circuit breaker handle repeated failures (Express agent behaviour).
|
|
229
|
+
return original;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// ── 3. handleRemediationWebhook ───────────────────────────────────────────────
|
|
234
|
+
/**
|
|
235
|
+
* Next.js App Router POST handler for the webhook endpoint.
|
|
236
|
+
* Register it as:
|
|
237
|
+
*
|
|
238
|
+
* // app/api/remediation/v1/webhook/route.ts
|
|
239
|
+
* import { handleRemediationWebhook } from '@develler/remediation-agent/nextjs'
|
|
240
|
+
* export const POST = handleRemediationWebhook
|
|
241
|
+
*
|
|
242
|
+
* Runs in Node.js runtime. Add this to the file if needed:
|
|
243
|
+
* export const runtime = 'nodejs'
|
|
244
|
+
*/
|
|
245
|
+
async function handleRemediationWebhook(req) {
|
|
246
|
+
let connection;
|
|
247
|
+
try {
|
|
248
|
+
connection = loadConnection();
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
return Response.json({ error: 'Agent not configured.' }, { status: 503 });
|
|
252
|
+
}
|
|
253
|
+
let body;
|
|
254
|
+
try {
|
|
255
|
+
body = await req.json();
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return Response.json({ error: 'Invalid JSON body.' }, { status: 400 });
|
|
259
|
+
}
|
|
260
|
+
const envelope = body;
|
|
261
|
+
// Verify HMAC using Node.js crypto (this route always runs in Node.js).
|
|
262
|
+
try {
|
|
263
|
+
const { createHmac, timingSafeEqual } = await Promise.resolve().then(() => __importStar(require('node:crypto')));
|
|
264
|
+
if (envelope.client_id !== connection.client_id) {
|
|
265
|
+
return Response.json({ error: 'client_id mismatch.' }, { status: 401 });
|
|
266
|
+
}
|
|
267
|
+
const payload = (envelope.payload ?? {});
|
|
268
|
+
const canonical = [
|
|
269
|
+
String(envelope.protocol_version ?? ''),
|
|
270
|
+
String(envelope.message_id ?? ''),
|
|
271
|
+
String(envelope.message_type ?? ''),
|
|
272
|
+
String(envelope.client_id ?? ''),
|
|
273
|
+
String(envelope.issued_at ?? ''),
|
|
274
|
+
String(envelope.nonce ?? ''),
|
|
275
|
+
Buffer.from(JSON.stringify(sortKeysDeep(payload))).toString('base64url'),
|
|
276
|
+
].join('.');
|
|
277
|
+
const expected = createHmac('sha256', connection.token).update(canonical).digest('hex');
|
|
278
|
+
const received = String(envelope.hmac_sha256 ?? '').toLowerCase();
|
|
279
|
+
const expectedBuf = Buffer.from(expected);
|
|
280
|
+
const receivedBuf = Buffer.from(received);
|
|
281
|
+
if (expectedBuf.length !== receivedBuf.length ||
|
|
282
|
+
!timingSafeEqual(expectedBuf, receivedBuf)) {
|
|
283
|
+
return Response.json({ error: 'HMAC verification failed.' }, { status: 401 });
|
|
284
|
+
}
|
|
285
|
+
// Nonce deduplication.
|
|
286
|
+
const redis = (0, UpstashRedisAdapter_js_1.createUpstashAdapter)();
|
|
287
|
+
const nonceKey = `remediation:nonce:${connection.client_id}:${envelope.nonce}`;
|
|
288
|
+
const isNew = await redis.setNx(nonceKey, '1', 300);
|
|
289
|
+
if (!isNew) {
|
|
290
|
+
return Response.json({ error: 'Replayed nonce.' }, { status: 409 });
|
|
291
|
+
}
|
|
292
|
+
// Cache the instruction.
|
|
293
|
+
const instruction = instructions_js_1.RuntimeInstruction.fromWirePayload(envelope.payload);
|
|
294
|
+
const fetcher = new InstructionFetcher_js_1.InstructionFetcher(redis, connection);
|
|
295
|
+
// Write directly to the cache by calling getActive (which seeds the cache)
|
|
296
|
+
// and then store — reuse the same key strategy.
|
|
297
|
+
const ttl = Math.max(1, instruction.expiresAt - Math.floor(Date.now() / 1000));
|
|
298
|
+
const key = `remediation:instruction:${connection.client_id}:${instruction.instructionId}`;
|
|
299
|
+
await redis.set(key, JSON.stringify(instruction.toJSON()), ttl);
|
|
300
|
+
await redis.sadd(`remediation:active:${connection.client_id}`, [instruction.instructionId]);
|
|
301
|
+
void fetcher; // fetcher created above to keep constructor signature stable
|
|
302
|
+
return Response.json({ status: 'accepted' });
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
return Response.json({ error: 'Internal error processing instruction.' }, { status: 500 });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
309
|
+
function sortKeysDeep(obj) {
|
|
310
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj))
|
|
311
|
+
return obj;
|
|
312
|
+
const sorted = {};
|
|
313
|
+
for (const key of Object.keys(obj).sort()) {
|
|
314
|
+
sorted[key] = sortKeysDeep(obj[key]);
|
|
315
|
+
}
|
|
316
|
+
return sorted;
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=NextjsInterceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NextjsInterceptor.js","sourceRoot":"","sources":["../../src/nextjs/NextjsInterceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Upstash Redis adapter using the REST API via fetch().
|
|
3
|
+
*
|
|
4
|
+
* No npm dependency — uses the global fetch() available in both the
|
|
5
|
+
* Next.js Edge Runtime and Node.js ≥ 18. Works identically in both.
|
|
6
|
+
*
|
|
7
|
+
* Only the operations needed by the Next.js agent are implemented.
|
|
8
|
+
* All commands go through the Upstash REST pipeline endpoint for efficiency.
|
|
9
|
+
*/
|
|
10
|
+
export interface UpstashConfig {
|
|
11
|
+
readonly url: string;
|
|
12
|
+
readonly token: string;
|
|
13
|
+
}
|
|
14
|
+
type RedisCommand = [string, ...string[]];
|
|
15
|
+
export declare class UpstashRedisAdapter {
|
|
16
|
+
private readonly cfg;
|
|
17
|
+
constructor(cfg: UpstashConfig);
|
|
18
|
+
private exec;
|
|
19
|
+
pipeline(commands: RedisCommand[]): Promise<unknown[]>;
|
|
20
|
+
get(key: string): Promise<string | null>;
|
|
21
|
+
set(key: string, value: string, ex: number): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* SET NX EX — atomic. Returns true if key was set (did not exist), false if it already existed.
|
|
24
|
+
* Used for nonce deduplication in the webhook handler.
|
|
25
|
+
*/
|
|
26
|
+
setNx(key: string, value: string, ex: number): Promise<boolean>;
|
|
27
|
+
del(key: string): Promise<void>;
|
|
28
|
+
exists(key: string): Promise<boolean>;
|
|
29
|
+
ttl(key: string): Promise<number>;
|
|
30
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
31
|
+
smembers(key: string): Promise<string[]>;
|
|
32
|
+
sadd(key: string, members: string[]): Promise<void>;
|
|
33
|
+
srem(key: string, members: string[]): Promise<void>;
|
|
34
|
+
mget(keys: string[]): Promise<Array<string | null>>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createUpstashAdapter(): UpstashRedisAdapter;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=UpstashRedisAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpstashRedisAdapter.d.ts","sourceRoot":"","sources":["../../src/nextjs/UpstashRedisAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAI,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,KAAK,YAAY,GAAG,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AAO1C,qBAAa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;YAIjC,IAAI;IAwBZ,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IA0BtD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE;;;OAGG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIxC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAI1D;AAID,wBAAgB,oBAAoB,IAAI,mBAAmB,CAY1D"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal Upstash Redis adapter using the REST API via fetch().
|
|
4
|
+
*
|
|
5
|
+
* No npm dependency — uses the global fetch() available in both the
|
|
6
|
+
* Next.js Edge Runtime and Node.js ≥ 18. Works identically in both.
|
|
7
|
+
*
|
|
8
|
+
* Only the operations needed by the Next.js agent are implemented.
|
|
9
|
+
* All commands go through the Upstash REST pipeline endpoint for efficiency.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.UpstashRedisAdapter = void 0;
|
|
13
|
+
exports.createUpstashAdapter = createUpstashAdapter;
|
|
14
|
+
class UpstashRedisAdapter {
|
|
15
|
+
cfg;
|
|
16
|
+
constructor(cfg) {
|
|
17
|
+
this.cfg = cfg;
|
|
18
|
+
}
|
|
19
|
+
// ── Single command ──────────────────────────────────────────────────────────
|
|
20
|
+
async exec(command) {
|
|
21
|
+
const res = await fetch(this.cfg.url, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${this.cfg.token}`,
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(command),
|
|
28
|
+
// Edge Runtime does not support cache: 'no-store' in all versions,
|
|
29
|
+
// but we don't want stale fetch caching from Next.js.
|
|
30
|
+
cache: 'no-store',
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
throw new Error(`Upstash HTTP ${res.status}: ${await res.text()}`);
|
|
34
|
+
}
|
|
35
|
+
const data = (await res.json());
|
|
36
|
+
if (data.error)
|
|
37
|
+
throw new Error(`Redis error: ${data.error}`);
|
|
38
|
+
return data.result;
|
|
39
|
+
}
|
|
40
|
+
// ── Pipeline (two roundtrips saved for bulk ops) ────────────────────────────
|
|
41
|
+
async pipeline(commands) {
|
|
42
|
+
if (commands.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const res = await fetch(`${this.cfg.url}/pipeline`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Bearer ${this.cfg.token}`,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(commands),
|
|
51
|
+
cache: 'no-store',
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
throw new Error(`Upstash pipeline HTTP ${res.status}: ${await res.text()}`);
|
|
55
|
+
}
|
|
56
|
+
const data = (await res.json());
|
|
57
|
+
return data.map((r) => {
|
|
58
|
+
if (r.error)
|
|
59
|
+
throw new Error(`Redis pipeline error: ${r.error}`);
|
|
60
|
+
return r.result;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// ── String ops ──────────────────────────────────────────────────────────────
|
|
64
|
+
async get(key) {
|
|
65
|
+
return (await this.exec(['GET', key]));
|
|
66
|
+
}
|
|
67
|
+
async set(key, value, ex) {
|
|
68
|
+
await this.exec(['SET', key, value, 'EX', String(ex)]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* SET NX EX — atomic. Returns true if key was set (did not exist), false if it already existed.
|
|
72
|
+
* Used for nonce deduplication in the webhook handler.
|
|
73
|
+
*/
|
|
74
|
+
async setNx(key, value, ex) {
|
|
75
|
+
const result = await this.exec(['SET', key, value, 'EX', String(ex), 'NX']);
|
|
76
|
+
return result === 'OK';
|
|
77
|
+
}
|
|
78
|
+
async del(key) {
|
|
79
|
+
await this.exec(['DEL', key]);
|
|
80
|
+
}
|
|
81
|
+
async exists(key) {
|
|
82
|
+
const result = await this.exec(['EXISTS', key]);
|
|
83
|
+
return result === 1;
|
|
84
|
+
}
|
|
85
|
+
async ttl(key) {
|
|
86
|
+
return (await this.exec(['TTL', key]));
|
|
87
|
+
}
|
|
88
|
+
async expire(key, seconds) {
|
|
89
|
+
await this.exec(['EXPIRE', key, String(seconds)]);
|
|
90
|
+
}
|
|
91
|
+
// ── Set ops ─────────────────────────────────────────────────────────────────
|
|
92
|
+
async smembers(key) {
|
|
93
|
+
return (await this.exec(['SMEMBERS', key])) ?? [];
|
|
94
|
+
}
|
|
95
|
+
async sadd(key, members) {
|
|
96
|
+
if (members.length === 0)
|
|
97
|
+
return;
|
|
98
|
+
await this.exec(['SADD', key, ...members]);
|
|
99
|
+
}
|
|
100
|
+
async srem(key, members) {
|
|
101
|
+
if (members.length === 0)
|
|
102
|
+
return;
|
|
103
|
+
await this.exec(['SREM', key, ...members]);
|
|
104
|
+
}
|
|
105
|
+
// ── Batch read ──────────────────────────────────────────────────────────────
|
|
106
|
+
async mget(keys) {
|
|
107
|
+
if (keys.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
return (await this.exec(['MGET', ...keys]));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.UpstashRedisAdapter = UpstashRedisAdapter;
|
|
113
|
+
// ── Factory ──────────────────────────────────────────────────────────────────
|
|
114
|
+
function createUpstashAdapter() {
|
|
115
|
+
const url = process.env['UPSTASH_REDIS_REST_URL'] ?? '';
|
|
116
|
+
const token = process.env['UPSTASH_REDIS_REST_TOKEN'] ?? '';
|
|
117
|
+
if (!url || !token) {
|
|
118
|
+
throw new Error('[Develler] UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN must be set. ' +
|
|
119
|
+
'Add Upstash Redis from the Vercel marketplace and these vars will be injected automatically.');
|
|
120
|
+
}
|
|
121
|
+
return new UpstashRedisAdapter({ url, token });
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=UpstashRedisAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpstashRedisAdapter.js","sourceRoot":"","sources":["../../src/nextjs/UpstashRedisAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @develler/remediation-agent/nextjs
|
|
3
|
+
*
|
|
4
|
+
* Next.js / Vercel entry point for the Develler Remediation Engine agent.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
*
|
|
8
|
+
* // middleware.ts (project root) — Edge Runtime
|
|
9
|
+
* import { createNextjsMiddleware } from '@develler/remediation-agent/nextjs'
|
|
10
|
+
* export const middleware = createNextjsMiddleware()
|
|
11
|
+
* export const config = { matcher: '/api/:path*' }
|
|
12
|
+
*
|
|
13
|
+
* // app/api/users/route.ts — PII masking on a specific route
|
|
14
|
+
* import { withRemediation } from '@develler/remediation-agent/nextjs'
|
|
15
|
+
* export const GET = withRemediation(async (req) => {
|
|
16
|
+
* return Response.json({ email: 'user@example.com' })
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // app/api/remediation/v1/webhook/route.ts — webhook receiver
|
|
20
|
+
* import { handleRemediationWebhook } from '@develler/remediation-agent/nextjs'
|
|
21
|
+
* export const POST = handleRemediationWebhook
|
|
22
|
+
* export const runtime = 'nodejs'
|
|
23
|
+
*
|
|
24
|
+
* Required environment variables:
|
|
25
|
+
* REMEDIATION_SAAS_URL — Your Develler dashboard URL
|
|
26
|
+
* REMEDIATION_CLIENT_ID — From `npx @develler/remediation-agent YOUR_KEY`
|
|
27
|
+
* REMEDIATION_TOKEN — From `npx @develler/remediation-agent YOUR_KEY`
|
|
28
|
+
* UPSTASH_REDIS_REST_URL — From Vercel × Upstash integration
|
|
29
|
+
* UPSTASH_REDIS_REST_TOKEN — From Vercel × Upstash integration
|
|
30
|
+
*/
|
|
31
|
+
export { createNextjsMiddleware, withRemediation, handleRemediationWebhook, } from './NextjsInterceptor.js';
|
|
32
|
+
export type { NextjsMiddlewareConfig } from './NextjsInterceptor.js';
|
|
33
|
+
export { UpstashRedisAdapter, createUpstashAdapter } from './UpstashRedisAdapter.js';
|
|
34
|
+
export type { UpstashConfig } from './UpstashRedisAdapter.js';
|
|
35
|
+
export { InstructionFetcher } from './InstructionFetcher.js';
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrF,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @develler/remediation-agent/nextjs
|
|
4
|
+
*
|
|
5
|
+
* Next.js / Vercel entry point for the Develler Remediation Engine agent.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
*
|
|
9
|
+
* // middleware.ts (project root) — Edge Runtime
|
|
10
|
+
* import { createNextjsMiddleware } from '@develler/remediation-agent/nextjs'
|
|
11
|
+
* export const middleware = createNextjsMiddleware()
|
|
12
|
+
* export const config = { matcher: '/api/:path*' }
|
|
13
|
+
*
|
|
14
|
+
* // app/api/users/route.ts — PII masking on a specific route
|
|
15
|
+
* import { withRemediation } from '@develler/remediation-agent/nextjs'
|
|
16
|
+
* export const GET = withRemediation(async (req) => {
|
|
17
|
+
* return Response.json({ email: 'user@example.com' })
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // app/api/remediation/v1/webhook/route.ts — webhook receiver
|
|
21
|
+
* import { handleRemediationWebhook } from '@develler/remediation-agent/nextjs'
|
|
22
|
+
* export const POST = handleRemediationWebhook
|
|
23
|
+
* export const runtime = 'nodejs'
|
|
24
|
+
*
|
|
25
|
+
* Required environment variables:
|
|
26
|
+
* REMEDIATION_SAAS_URL — Your Develler dashboard URL
|
|
27
|
+
* REMEDIATION_CLIENT_ID — From `npx @develler/remediation-agent YOUR_KEY`
|
|
28
|
+
* REMEDIATION_TOKEN — From `npx @develler/remediation-agent YOUR_KEY`
|
|
29
|
+
* UPSTASH_REDIS_REST_URL — From Vercel × Upstash integration
|
|
30
|
+
* UPSTASH_REDIS_REST_TOKEN — From Vercel × Upstash integration
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.InstructionFetcher = exports.createUpstashAdapter = exports.UpstashRedisAdapter = exports.handleRemediationWebhook = exports.withRemediation = exports.createNextjsMiddleware = void 0;
|
|
34
|
+
var NextjsInterceptor_js_1 = require("./NextjsInterceptor.js");
|
|
35
|
+
Object.defineProperty(exports, "createNextjsMiddleware", { enumerable: true, get: function () { return NextjsInterceptor_js_1.createNextjsMiddleware; } });
|
|
36
|
+
Object.defineProperty(exports, "withRemediation", { enumerable: true, get: function () { return NextjsInterceptor_js_1.withRemediation; } });
|
|
37
|
+
Object.defineProperty(exports, "handleRemediationWebhook", { enumerable: true, get: function () { return NextjsInterceptor_js_1.handleRemediationWebhook; } });
|
|
38
|
+
var UpstashRedisAdapter_js_1 = require("./UpstashRedisAdapter.js");
|
|
39
|
+
Object.defineProperty(exports, "UpstashRedisAdapter", { enumerable: true, get: function () { return UpstashRedisAdapter_js_1.UpstashRedisAdapter; } });
|
|
40
|
+
Object.defineProperty(exports, "createUpstashAdapter", { enumerable: true, get: function () { return UpstashRedisAdapter_js_1.createUpstashAdapter; } });
|
|
41
|
+
var InstructionFetcher_js_1 = require("./InstructionFetcher.js");
|
|
42
|
+
Object.defineProperty(exports, "InstructionFetcher", { enumerable: true, get: function () { return InstructionFetcher_js_1.InstructionFetcher; } });
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAEH,+DAIgC;AAH9B,8HAAA,sBAAsB,OAAA;AACtB,uHAAA,eAAe,OAAA;AACf,gIAAA,wBAAwB,OAAA;AAI1B,mEAAqF;AAA5E,6HAAA,mBAAmB,OAAA;AAAE,8HAAA,oBAAoB,OAAA;AAElD,iEAA6D;AAApD,2HAAA,kBAAkB,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@develler/remediation-agent",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Remediation agent for Node.js/Express — real-time PII masking and automated code-fix pipeline",
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"description": "Remediation agent for Node.js/Express and Next.js/Vercel — real-time PII masking and automated code-fix pipeline",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./dist/index.js",
|
|
9
|
-
"./auto": "./dist/auto.js"
|
|
9
|
+
"./auto": "./dist/auto.js",
|
|
10
|
+
"./nextjs": "./dist/nextjs/index.js"
|
|
10
11
|
},
|
|
11
12
|
"bin": {
|
|
12
13
|
"remediation-connect": "dist/commands/connect.js",
|
|
@@ -39,14 +40,22 @@
|
|
|
39
40
|
"winston": "^3.13.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
|
-
"express": "^4.18.0 || ^5.0.0"
|
|
43
|
+
"express": "^4.18.0 || ^5.0.0",
|
|
44
|
+
"next": ">=14.0.0"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@types/express": "^5.0.0",
|
|
46
48
|
"@types/node": "^20.0.0",
|
|
49
|
+
"next": "^16.2.9",
|
|
47
50
|
"typescript": "^5.4.0"
|
|
48
51
|
},
|
|
49
52
|
"peerDependenciesMeta": {
|
|
53
|
+
"express": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"next": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
50
59
|
"typescript": {
|
|
51
60
|
"optional": true
|
|
52
61
|
}
|