@spotpatch/next 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/cli.js +579 -48
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs +29 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +9 -0
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +20 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
import path4 from "path";
|
|
6
|
+
import { runSpotPatchBridgeCli } from "@spotpatch/bridge";
|
|
6
7
|
|
|
7
8
|
// src/cli-owner.ts
|
|
8
9
|
import { spawn } from "child_process";
|
|
9
10
|
import { randomBytes } from "crypto";
|
|
10
11
|
import { serializeResolvedSpotPatchOptions } from "@spotpatch/dev-server";
|
|
12
|
+
import { SPOTPATCH_API_BASE as SPOTPATCH_API_BASE2 } from "@spotpatch/shared";
|
|
11
13
|
|
|
12
14
|
// src/cli-args.ts
|
|
13
15
|
import { isLoopbackHostname } from "@spotpatch/dev-server";
|
|
@@ -282,13 +284,18 @@ import {
|
|
|
282
284
|
} from "http";
|
|
283
285
|
import {
|
|
284
286
|
createAgentJobManager,
|
|
287
|
+
createExternalHandoffService,
|
|
285
288
|
createRuntimeAiConfig,
|
|
286
289
|
createRuntimeDataFlowConfig,
|
|
287
290
|
createSession,
|
|
288
291
|
createSourceRegistrationService,
|
|
289
292
|
createSourceRegistry,
|
|
290
|
-
createSpotPatchMiddleware
|
|
293
|
+
createSpotPatchMiddleware,
|
|
294
|
+
resolveManagedExecutionValidation
|
|
291
295
|
} from "@spotpatch/dev-server";
|
|
296
|
+
import {
|
|
297
|
+
createExternalAgentSupervisor
|
|
298
|
+
} from "@spotpatch/bridge";
|
|
292
299
|
import {
|
|
293
300
|
SPOTPATCH_API_BASE,
|
|
294
301
|
SPOTPATCH_ENDPOINTS,
|
|
@@ -298,7 +305,7 @@ import {
|
|
|
298
305
|
// package.json
|
|
299
306
|
var package_default = {
|
|
300
307
|
name: "@spotpatch/next",
|
|
301
|
-
version: "0.
|
|
308
|
+
version: "0.7.0",
|
|
302
309
|
description: "Development-only Next.js adapter and CLI for SpotPatch.",
|
|
303
310
|
license: "MIT",
|
|
304
311
|
repository: {
|
|
@@ -391,6 +398,7 @@ var package_default = {
|
|
|
391
398
|
"verify:loader": "node scripts/verify-loader.cjs"
|
|
392
399
|
},
|
|
393
400
|
dependencies: {
|
|
401
|
+
"@spotpatch/bridge": "workspace:^",
|
|
394
402
|
"@spotpatch/compiler": "workspace:^",
|
|
395
403
|
"@spotpatch/dev-server": "workspace:^",
|
|
396
404
|
"@spotpatch/runtime": "workspace:^",
|
|
@@ -513,38 +521,102 @@ function validateCredentialEnvironment(options, credentials) {
|
|
|
513
521
|
throw new TypeError("The SpotPatch credential environment is inconsistent.");
|
|
514
522
|
}
|
|
515
523
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
524
|
+
function diagnosticUrl(value, fallbackOrigin) {
|
|
525
|
+
try {
|
|
526
|
+
const url = new URL(value, fallbackOrigin);
|
|
527
|
+
return `${url.origin}${url.pathname}`;
|
|
528
|
+
} catch {
|
|
529
|
+
return fallbackOrigin;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
async function checkBootstrapRoute(routeOrigin, expectedOrigin, expectedConfig) {
|
|
533
|
+
const requestUrl = new URL(SPOTPATCH_ENDPOINTS.bootstrap, routeOrigin);
|
|
534
|
+
let response;
|
|
535
|
+
try {
|
|
536
|
+
response = await fetch(requestUrl, {
|
|
537
|
+
method: "POST",
|
|
538
|
+
headers: {
|
|
539
|
+
"Content-Type": "application/json",
|
|
540
|
+
Origin: expectedOrigin,
|
|
541
|
+
"Sec-Fetch-Site": "same-origin"
|
|
542
|
+
},
|
|
543
|
+
body: "{}",
|
|
544
|
+
redirect: "manual",
|
|
545
|
+
signal: AbortSignal.timeout(SIDECAR_SELF_CHECK_TIMEOUT_MS)
|
|
546
|
+
});
|
|
547
|
+
} catch {
|
|
548
|
+
return Object.freeze({
|
|
549
|
+
finalUrl: diagnosticUrl(requestUrl.href, routeOrigin),
|
|
550
|
+
kind: "unreachable",
|
|
551
|
+
ok: false
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
const redirectLocation = response.headers.get("location");
|
|
555
|
+
const finalUrl = diagnosticUrl(
|
|
556
|
+
redirectLocation === null ? response.url || requestUrl.href : new URL(redirectLocation, response.url || requestUrl.href).href,
|
|
557
|
+
routeOrigin
|
|
558
|
+
);
|
|
527
559
|
const declaredLength = Number(response.headers.get("content-length"));
|
|
528
560
|
if (Number.isFinite(declaredLength) && declaredLength > SIDECAR_SELF_CHECK_LIMIT_BYTES) {
|
|
529
561
|
await response.body?.cancel();
|
|
530
|
-
|
|
562
|
+
return Object.freeze({
|
|
563
|
+
finalUrl,
|
|
564
|
+
kind: "response-invalid",
|
|
565
|
+
ok: false,
|
|
566
|
+
status: response.status
|
|
567
|
+
});
|
|
531
568
|
}
|
|
532
569
|
const text = await response.text();
|
|
533
|
-
if (!response.ok
|
|
534
|
-
|
|
570
|
+
if (!response.ok) {
|
|
571
|
+
return Object.freeze({
|
|
572
|
+
finalUrl,
|
|
573
|
+
kind: "http",
|
|
574
|
+
ok: false,
|
|
575
|
+
status: response.status
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
if (!response.headers.get("cache-control")?.toLowerCase().includes("no-store") || Buffer.byteLength(text, "utf8") > SIDECAR_SELF_CHECK_LIMIT_BYTES) {
|
|
579
|
+
return Object.freeze({
|
|
580
|
+
finalUrl,
|
|
581
|
+
kind: "response-invalid",
|
|
582
|
+
ok: false,
|
|
583
|
+
status: response.status
|
|
584
|
+
});
|
|
535
585
|
}
|
|
536
586
|
let value;
|
|
537
587
|
try {
|
|
538
588
|
value = JSON.parse(text);
|
|
539
|
-
} catch
|
|
540
|
-
|
|
589
|
+
} catch {
|
|
590
|
+
return Object.freeze({
|
|
591
|
+
finalUrl,
|
|
592
|
+
kind: "response-invalid",
|
|
593
|
+
ok: false,
|
|
594
|
+
status: response.status
|
|
595
|
+
});
|
|
541
596
|
}
|
|
542
597
|
if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).length !== 2 || !("ok" in value) || value.ok !== true || !("data" in value)) {
|
|
543
|
-
|
|
598
|
+
return Object.freeze({
|
|
599
|
+
finalUrl,
|
|
600
|
+
kind: "response-invalid",
|
|
601
|
+
ok: false,
|
|
602
|
+
status: response.status
|
|
603
|
+
});
|
|
544
604
|
}
|
|
545
605
|
const parsed = runtimeConfigSchema.safeParse(value.data);
|
|
546
606
|
const parsedExpected = runtimeConfigSchema.safeParse(expectedConfig);
|
|
547
607
|
if (!parsed.success || !parsedExpected.success || JSON.stringify(parsed.data) !== JSON.stringify(parsedExpected.data)) {
|
|
608
|
+
return Object.freeze({
|
|
609
|
+
finalUrl,
|
|
610
|
+
kind: "response-invalid",
|
|
611
|
+
ok: false,
|
|
612
|
+
status: response.status
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return Object.freeze({ ok: true });
|
|
616
|
+
}
|
|
617
|
+
async function selfCheckSidecar(sidecarOrigin, publicOrigin, expectedConfig) {
|
|
618
|
+
const result = await checkBootstrapRoute(sidecarOrigin, publicOrigin, expectedConfig);
|
|
619
|
+
if (!result.ok) {
|
|
548
620
|
throw new Error("SpotPatch Sidecar self-check failed.");
|
|
549
621
|
}
|
|
550
622
|
}
|
|
@@ -564,6 +636,10 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
564
636
|
let closed = false;
|
|
565
637
|
let registry;
|
|
566
638
|
let agentManager;
|
|
639
|
+
let externalHandoffService;
|
|
640
|
+
let externalAgentSupervisor;
|
|
641
|
+
let spotPatchMiddleware;
|
|
642
|
+
let publicRouteCheck;
|
|
567
643
|
let handler = (_request, response) => {
|
|
568
644
|
writeUnavailable(response, 503);
|
|
569
645
|
};
|
|
@@ -626,6 +702,7 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
626
702
|
bundler: input.bundler,
|
|
627
703
|
debug: input.options.debug,
|
|
628
704
|
editor: input.options.editor,
|
|
705
|
+
externalAgent: input.options.externalAgent,
|
|
629
706
|
framework: "next",
|
|
630
707
|
frameworkVersion: input.nextVersion,
|
|
631
708
|
locale: input.options.locale,
|
|
@@ -642,8 +719,49 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
642
719
|
environment: input.credentials,
|
|
643
720
|
root: input.projectRoot
|
|
644
721
|
});
|
|
722
|
+
const handoffService = input.options.externalAgent.enabled ? createExternalHandoffService({
|
|
723
|
+
framework: "next",
|
|
724
|
+
root: input.appRoot,
|
|
725
|
+
sessionId: session.id
|
|
726
|
+
}) : void 0;
|
|
727
|
+
let handoffReady = false;
|
|
728
|
+
if (handoffService !== void 0) {
|
|
729
|
+
try {
|
|
730
|
+
await handoffService.start();
|
|
731
|
+
handoffReady = true;
|
|
732
|
+
} catch {
|
|
733
|
+
process.stderr.write(
|
|
734
|
+
"[spotpatch:next] External Agent handoff is unavailable; core tools remain active.\n"
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
externalHandoffService = handoffService;
|
|
739
|
+
let supervisor;
|
|
740
|
+
if (handoffReady) {
|
|
741
|
+
try {
|
|
742
|
+
const validation = await resolveManagedExecutionValidation({
|
|
743
|
+
ai: input.options.ai,
|
|
744
|
+
appRoot: input.appRoot
|
|
745
|
+
});
|
|
746
|
+
supervisor = await createExternalAgentSupervisor({
|
|
747
|
+
bridgeAdapter: "next",
|
|
748
|
+
checks: validation.checks,
|
|
749
|
+
limits: validation.limits,
|
|
750
|
+
root: input.appRoot,
|
|
751
|
+
sessionId: session.id,
|
|
752
|
+
projectLabel: input.appRoot.split(/[\\/]/u).at(-1) ?? "project"
|
|
753
|
+
});
|
|
754
|
+
} catch {
|
|
755
|
+
process.stderr.write(
|
|
756
|
+
"[spotpatch:next] Managed Agent control is unavailable; Inbox remains active.\n"
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
externalAgentSupervisor = supervisor;
|
|
645
761
|
const middleware = createSpotPatchMiddleware({
|
|
646
762
|
...manager === void 0 ? {} : { agentManager: manager },
|
|
763
|
+
...handoffService === void 0 ? {} : { externalHandoffService: handoffService },
|
|
764
|
+
...supervisor === void 0 ? {} : { externalAgentControl: supervisor },
|
|
647
765
|
bootstrap: {
|
|
648
766
|
expectedOrigin: input.publicOrigin,
|
|
649
767
|
runtimeConfig
|
|
@@ -668,6 +786,7 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
668
786
|
});
|
|
669
787
|
registry = sourceRegistry;
|
|
670
788
|
agentManager = manager;
|
|
789
|
+
spotPatchMiddleware = middleware;
|
|
671
790
|
handler = (request, response) => {
|
|
672
791
|
if (requestPath(request.url) === NEXT_INTERNAL_REGISTRATION_PATH) {
|
|
673
792
|
registration.handler(request, response);
|
|
@@ -678,8 +797,26 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
678
797
|
});
|
|
679
798
|
};
|
|
680
799
|
await selfCheckSidecar(sidecarOrigin, input.publicOrigin, runtimeConfig);
|
|
800
|
+
publicRouteCheck = Object.freeze({
|
|
801
|
+
expectedConfig: runtimeConfig,
|
|
802
|
+
publicOrigin: input.publicOrigin
|
|
803
|
+
});
|
|
681
804
|
active = true;
|
|
682
805
|
},
|
|
806
|
+
async checkPublicRoute() {
|
|
807
|
+
if (closed || !active || publicRouteCheck === void 0) {
|
|
808
|
+
return Object.freeze({
|
|
809
|
+
finalUrl: sidecarOrigin,
|
|
810
|
+
kind: "unreachable",
|
|
811
|
+
ok: false
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
return await checkBootstrapRoute(
|
|
815
|
+
publicRouteCheck.publicOrigin,
|
|
816
|
+
publicRouteCheck.publicOrigin,
|
|
817
|
+
publicRouteCheck.expectedConfig
|
|
818
|
+
);
|
|
819
|
+
},
|
|
683
820
|
async close() {
|
|
684
821
|
if (closed) {
|
|
685
822
|
return;
|
|
@@ -689,6 +826,12 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
689
826
|
writeUnavailable(response, 503);
|
|
690
827
|
};
|
|
691
828
|
registry?.clear();
|
|
829
|
+
spotPatchMiddleware?.dispose();
|
|
830
|
+
spotPatchMiddleware = void 0;
|
|
831
|
+
await externalAgentSupervisor?.dispose();
|
|
832
|
+
externalAgentSupervisor = void 0;
|
|
833
|
+
await externalHandoffService?.close();
|
|
834
|
+
externalHandoffService = void 0;
|
|
692
835
|
await agentManager?.close();
|
|
693
836
|
server.closeIdleConnections();
|
|
694
837
|
const closing = closeServer(server);
|
|
@@ -701,6 +844,8 @@ async function createNextSidecar(sidecarOptions) {
|
|
|
701
844
|
// src/cli-owner.ts
|
|
702
845
|
var CONFIGURATION_STARTUP_TIMEOUT_MS = 6e4;
|
|
703
846
|
var FORCED_TERMINATION_TIMEOUT_MS = 5e3;
|
|
847
|
+
var PUBLIC_ROUTE_CANARY_RETRY_MS = 250;
|
|
848
|
+
var PUBLIC_ROUTE_CANARY_TIMEOUT_MS = 6e4;
|
|
704
849
|
var MAX_CONFIGURATION_REQUESTS = 32;
|
|
705
850
|
var ID_PATTERN2 = /^[A-Za-z0-9_-]{16,128}$/u;
|
|
706
851
|
function isRecord2(value) {
|
|
@@ -759,6 +904,51 @@ function waitForChild(child) {
|
|
|
759
904
|
});
|
|
760
905
|
});
|
|
761
906
|
}
|
|
907
|
+
function waitForDelay(milliseconds, signal) {
|
|
908
|
+
return new Promise((resolve, reject) => {
|
|
909
|
+
if (signal.aborted) {
|
|
910
|
+
reject(
|
|
911
|
+
signal.reason instanceof Error ? signal.reason : new Error("SpotPatch public-route wait was aborted.")
|
|
912
|
+
);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
const timer = setTimeout(resolve, milliseconds);
|
|
916
|
+
timer.unref();
|
|
917
|
+
signal.addEventListener(
|
|
918
|
+
"abort",
|
|
919
|
+
() => {
|
|
920
|
+
clearTimeout(timer);
|
|
921
|
+
reject(
|
|
922
|
+
signal.reason instanceof Error ? signal.reason : new Error("SpotPatch public-route wait was aborted.")
|
|
923
|
+
);
|
|
924
|
+
},
|
|
925
|
+
{ once: true }
|
|
926
|
+
);
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
function publicRouteCanaryError(result) {
|
|
930
|
+
if (result.ok) {
|
|
931
|
+
throw new TypeError("A successful public route canary has no error.");
|
|
932
|
+
}
|
|
933
|
+
const status = result.status === void 0 ? "unavailable" : String(result.status);
|
|
934
|
+
return new Error(
|
|
935
|
+
`SpotPatch public-route canary failed at ${result.finalUrl} (stage=${result.kind}, status=${status}). Check Proxy/Middleware matcher exclusions for ${SPOTPATCH_API_BASE2}.`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
async function waitForPublicRoute(sidecar, signal) {
|
|
939
|
+
const deadline = Date.now() + PUBLIC_ROUTE_CANARY_TIMEOUT_MS;
|
|
940
|
+
while (!signal.aborted) {
|
|
941
|
+
const result = await sidecar.checkPublicRoute();
|
|
942
|
+
if (result.ok) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (result.kind !== "unreachable" || Date.now() >= deadline) {
|
|
946
|
+
throw publicRouteCanaryError(result);
|
|
947
|
+
}
|
|
948
|
+
await waitForDelay(PUBLIC_ROUTE_CANARY_RETRY_MS, signal);
|
|
949
|
+
}
|
|
950
|
+
throw signal.reason;
|
|
951
|
+
}
|
|
762
952
|
async function closeSidecar(sidecar) {
|
|
763
953
|
let timer;
|
|
764
954
|
try {
|
|
@@ -797,6 +987,8 @@ async function runNextDevelopment(arguments_) {
|
|
|
797
987
|
let configured = false;
|
|
798
988
|
let failureCode;
|
|
799
989
|
let configurationQueue = Promise.resolve();
|
|
990
|
+
const publicRouteCanaryAbort = new AbortController();
|
|
991
|
+
let publicRouteCanary;
|
|
800
992
|
const handleConfiguration = async (value) => {
|
|
801
993
|
const correlation = readCorrelation(value, launchNonce);
|
|
802
994
|
let message;
|
|
@@ -858,10 +1050,30 @@ async function runNextDevelopment(arguments_) {
|
|
|
858
1050
|
if (!configured) {
|
|
859
1051
|
configured = true;
|
|
860
1052
|
clearTimeout(startupTimer);
|
|
861
|
-
|
|
862
|
-
|
|
1053
|
+
if (message.options.enabled) {
|
|
1054
|
+
publicRouteCanary = waitForPublicRoute(
|
|
1055
|
+
sidecar,
|
|
1056
|
+
publicRouteCanaryAbort.signal
|
|
1057
|
+
).then(
|
|
1058
|
+
() => {
|
|
1059
|
+
process.stdout.write(
|
|
1060
|
+
`[spotpatch:next] ready for Next.js ${project.nextVersion} (${dev.bundler}) at ${dev.publicOrigin}
|
|
863
1061
|
`
|
|
864
|
-
|
|
1062
|
+
);
|
|
1063
|
+
},
|
|
1064
|
+
(error) => {
|
|
1065
|
+
if (publicRouteCanaryAbort.signal.aborted) {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
failureCode = "PUBLIC_ROUTE_CANARY_FAILED";
|
|
1069
|
+
process.stderr.write(
|
|
1070
|
+
`[spotpatch:next] ${error instanceof Error ? error.message : "SpotPatch public-route canary failed."}
|
|
1071
|
+
`
|
|
1072
|
+
);
|
|
1073
|
+
lifecycle.child?.kill("SIGTERM");
|
|
1074
|
+
}
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
865
1077
|
}
|
|
866
1078
|
return createAck(message, { ok: true });
|
|
867
1079
|
};
|
|
@@ -938,6 +1150,7 @@ async function runNextDevelopment(arguments_) {
|
|
|
938
1150
|
process.once("SIGINT", onSigint);
|
|
939
1151
|
process.once("SIGTERM", onSigterm);
|
|
940
1152
|
const result = await waitForChild(child);
|
|
1153
|
+
publicRouteCanaryAbort.abort();
|
|
941
1154
|
clearTimeout(startupTimer);
|
|
942
1155
|
process.off("SIGINT", onSigint);
|
|
943
1156
|
process.off("SIGTERM", onSigterm);
|
|
@@ -945,6 +1158,7 @@ async function runNextDevelopment(arguments_) {
|
|
|
945
1158
|
clearTimeout(forceTimer);
|
|
946
1159
|
}
|
|
947
1160
|
await configurationQueue;
|
|
1161
|
+
await publicRouteCanary;
|
|
948
1162
|
try {
|
|
949
1163
|
await closeSidecar(sidecar);
|
|
950
1164
|
} catch {
|
|
@@ -974,7 +1188,7 @@ import {
|
|
|
974
1188
|
integrationPathExists,
|
|
975
1189
|
readIntegrationFile
|
|
976
1190
|
} from "@spotpatch/dev-server";
|
|
977
|
-
import { DEFAULT_AGENT_LIMITS } from "@spotpatch/shared";
|
|
1191
|
+
import { DEFAULT_AGENT_LIMITS, SPOTPATCH_API_BASE as SPOTPATCH_API_BASE3 } from "@spotpatch/shared";
|
|
978
1192
|
import { MagicString } from "magic-string";
|
|
979
1193
|
import {
|
|
980
1194
|
parseSync,
|
|
@@ -1000,6 +1214,8 @@ var INSTRUMENTATION_EXTENSIONS = Object.freeze([
|
|
|
1000
1214
|
".cts",
|
|
1001
1215
|
".cjs"
|
|
1002
1216
|
]);
|
|
1217
|
+
var PROXY_FILE_BASE_NAMES = Object.freeze(["proxy", "middleware"]);
|
|
1218
|
+
var SPOTPATCH_MATCHER_EXCLUSION = "__spotpatch(?:/|$)";
|
|
1003
1219
|
var SIMPLE_SCRIPT_ARGUMENT_PATTERN = /^[A-Za-z0-9._:/=@%+,-]+$/u;
|
|
1004
1220
|
function isParserErrorSeverity(value) {
|
|
1005
1221
|
return value === "Error";
|
|
@@ -1007,6 +1223,10 @@ function isParserErrorSeverity(value) {
|
|
|
1007
1223
|
function isRecord3(value) {
|
|
1008
1224
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1009
1225
|
}
|
|
1226
|
+
function staticStringLiteral(value) {
|
|
1227
|
+
if (!isRecord3(value) || value.type !== "Literal") return void 0;
|
|
1228
|
+
return typeof value.value === "string" ? value.value : void 0;
|
|
1229
|
+
}
|
|
1010
1230
|
function parseModule(absolutePath, source) {
|
|
1011
1231
|
const result = parseSync(absolutePath, source, {
|
|
1012
1232
|
sourceType: "module",
|
|
@@ -1039,9 +1259,7 @@ function importInsertionOffset(program) {
|
|
|
1039
1259
|
function insertStaticImport(magicString, program, statement) {
|
|
1040
1260
|
const offset = importInsertionOffset(program);
|
|
1041
1261
|
if (offset === 0) {
|
|
1042
|
-
magicString.prepend(`${statement}
|
|
1043
|
-
|
|
1044
|
-
`);
|
|
1262
|
+
magicString.prepend(`${statement}${program.body.length === 0 ? "\n" : "\n\n"}`);
|
|
1045
1263
|
return;
|
|
1046
1264
|
}
|
|
1047
1265
|
magicString.appendRight(offset, `
|
|
@@ -1127,21 +1345,102 @@ function staticPropertyName(property) {
|
|
|
1127
1345
|
if (property.key.type === "Identifier") return property.key.name;
|
|
1128
1346
|
return property.key.type === "Literal" && typeof property.key.value === "string" ? property.key.value : void 0;
|
|
1129
1347
|
}
|
|
1130
|
-
function
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1348
|
+
function unwrapExpression(expression) {
|
|
1349
|
+
let current = expression;
|
|
1350
|
+
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
1351
|
+
current = current.expression;
|
|
1352
|
+
}
|
|
1353
|
+
return current;
|
|
1354
|
+
}
|
|
1355
|
+
function findObjectProperty(object, name) {
|
|
1356
|
+
const matches = object.properties.filter(
|
|
1357
|
+
(property) => property.type === "Property" && staticPropertyName(property) === name
|
|
1135
1358
|
);
|
|
1136
|
-
if (
|
|
1137
|
-
throw new Error(
|
|
1359
|
+
if (matches.length > 1) {
|
|
1360
|
+
throw new Error(`SpotPatch init found duplicate ${name} options.`);
|
|
1138
1361
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1362
|
+
return matches[0];
|
|
1363
|
+
}
|
|
1364
|
+
function lineIndentAt(source, offset) {
|
|
1365
|
+
const lineStart = source.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
|
|
1366
|
+
return /^[\t ]*/u.exec(source.slice(lineStart, offset))?.[0] ?? "";
|
|
1367
|
+
}
|
|
1368
|
+
function childIndent(source, object) {
|
|
1369
|
+
const first = object.properties[0];
|
|
1370
|
+
return first === void 0 ? `${lineIndentAt(source, object.start)} ` : lineIndentAt(source, first.start);
|
|
1371
|
+
}
|
|
1372
|
+
function initializedNextOptions(trustedFastModeAvailable) {
|
|
1373
|
+
return `{ externalAgent: true${trustedFastModeAvailable ? ", trustedFastMode: true" : ""} }`;
|
|
1374
|
+
}
|
|
1375
|
+
function enableNextOptions(magicString, source, factory, trustedFastModeAvailable) {
|
|
1376
|
+
const argument = factory.arguments[0];
|
|
1377
|
+
if (factory.arguments.length === 0) {
|
|
1378
|
+
magicString.appendLeft(
|
|
1379
|
+
factory.end - 1,
|
|
1380
|
+
initializedNextOptions(trustedFastModeAvailable)
|
|
1381
|
+
);
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
if (factory.arguments.length !== 1 || argument === void 0 || argument.type === "SpreadElement") {
|
|
1385
|
+
throw new Error("SpotPatch init cannot safely update withSpotPatch options.");
|
|
1386
|
+
}
|
|
1387
|
+
const options = unwrapExpression(argument);
|
|
1388
|
+
if (options.type !== "ObjectExpression") {
|
|
1389
|
+
throw new Error("SpotPatch init requires withSpotPatch options to be an object.");
|
|
1390
|
+
}
|
|
1391
|
+
if (options.properties.some((property) => property.type === "SpreadElement")) {
|
|
1392
|
+
throw new Error(
|
|
1393
|
+
"SpotPatch init cannot prove externalAgent through spread withSpotPatch options."
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
const dataFlowProperty = findObjectProperty(options, "dataFlow");
|
|
1397
|
+
const dataFlowValue = dataFlowProperty === void 0 ? void 0 : unwrapExpression(dataFlowProperty.value);
|
|
1398
|
+
if (dataFlowValue !== void 0 && !(dataFlowValue.type === "Literal" && dataFlowValue.value === false)) {
|
|
1141
1399
|
throw new Error(
|
|
1142
1400
|
"SpotPatch Next does not support component dataFlow yet; remove dataFlow or set it to false."
|
|
1143
1401
|
);
|
|
1144
1402
|
}
|
|
1403
|
+
const missing = [];
|
|
1404
|
+
const externalAgentProperty = findObjectProperty(options, "externalAgent");
|
|
1405
|
+
if (externalAgentProperty === void 0) {
|
|
1406
|
+
missing.push("externalAgent: true");
|
|
1407
|
+
} else {
|
|
1408
|
+
const value = unwrapExpression(externalAgentProperty.value);
|
|
1409
|
+
if (value.type !== "Literal" || typeof value.value !== "boolean") {
|
|
1410
|
+
throw new Error("SpotPatch init requires externalAgent to be a boolean literal.");
|
|
1411
|
+
}
|
|
1412
|
+
if (!value.value) {
|
|
1413
|
+
magicString.overwrite(value.start, value.end, "true");
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
const trustedFastModeProperty = findObjectProperty(options, "trustedFastMode");
|
|
1417
|
+
if (trustedFastModeAvailable && trustedFastModeProperty === void 0) {
|
|
1418
|
+
missing.push("trustedFastMode: true");
|
|
1419
|
+
} else if (trustedFastModeProperty !== void 0) {
|
|
1420
|
+
const value = unwrapExpression(trustedFastModeProperty.value);
|
|
1421
|
+
if (value.type !== "Literal" || typeof value.value !== "boolean") {
|
|
1422
|
+
throw new Error(
|
|
1423
|
+
"SpotPatch init requires trustedFastMode to be a boolean literal."
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
if (trustedFastModeAvailable && !value.value) {
|
|
1427
|
+
magicString.overwrite(value.start, value.end, "true");
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
if (missing.length === 0) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
const indent = childIndent(source, options);
|
|
1434
|
+
if (options.properties.length === 0) {
|
|
1435
|
+
magicString.appendLeft(options.end - 1, ` ${missing.join(", ")} `);
|
|
1436
|
+
} else {
|
|
1437
|
+
magicString.appendLeft(
|
|
1438
|
+
options.properties[0]?.start ?? options.end - 1,
|
|
1439
|
+
`${missing.join(`,
|
|
1440
|
+
${indent}`)},
|
|
1441
|
+
${indent}`
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1145
1444
|
}
|
|
1146
1445
|
function transformNextConfig(absolutePath, source, trustedFastModeAvailable) {
|
|
1147
1446
|
if (absolutePath.endsWith(".cjs") || absolutePath.endsWith(".cts")) {
|
|
@@ -1161,16 +1460,8 @@ function transformNextConfig(absolutePath, source, trustedFastModeAvailable) {
|
|
|
1161
1460
|
const wrapperName = existingWrapperName ?? chooseWrapperName(program);
|
|
1162
1461
|
const existingFactory = existingWrapperName === void 0 ? void 0 : wrappedFactoryCall(defaultExport.declaration, existingWrapperName);
|
|
1163
1462
|
if (existingFactory !== void 0) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
magicString.overwrite(
|
|
1167
|
-
existingFactory.start,
|
|
1168
|
-
existingFactory.end,
|
|
1169
|
-
`${wrapperName}({ trustedFastMode: true })`
|
|
1170
|
-
);
|
|
1171
|
-
return magicString.toString();
|
|
1172
|
-
}
|
|
1173
|
-
return source;
|
|
1463
|
+
enableNextOptions(magicString, source, existingFactory, trustedFastModeAvailable);
|
|
1464
|
+
return magicString.toString();
|
|
1174
1465
|
}
|
|
1175
1466
|
if (existingWrapperName === void 0) {
|
|
1176
1467
|
const specifier = wrapperName === "withSpotPatch" ? "withSpotPatch" : `withSpotPatch as ${wrapperName}`;
|
|
@@ -1187,7 +1478,7 @@ function transformNextConfig(absolutePath, source, trustedFastModeAvailable) {
|
|
|
1187
1478
|
magicString.overwrite(
|
|
1188
1479
|
defaultExport.declaration.start,
|
|
1189
1480
|
defaultExport.declaration.end,
|
|
1190
|
-
`${wrapperName}(${trustedFastModeAvailable
|
|
1481
|
+
`${wrapperName}(${initializedNextOptions(trustedFastModeAvailable)})(${expression})`
|
|
1191
1482
|
);
|
|
1192
1483
|
return magicString.toString();
|
|
1193
1484
|
}
|
|
@@ -1212,6 +1503,230 @@ function transformInstrumentationClient(absolutePath, source) {
|
|
|
1212
1503
|
);
|
|
1213
1504
|
return magicString.toString();
|
|
1214
1505
|
}
|
|
1506
|
+
function findVariableInitializer(program, name) {
|
|
1507
|
+
for (const statement of program.body) {
|
|
1508
|
+
if (statement.type !== "VariableDeclaration") {
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
for (const declaration of statement.declarations) {
|
|
1512
|
+
if (declaration.id.type === "Identifier" && declaration.id.name === name && declaration.init !== null) {
|
|
1513
|
+
return declaration.init;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return void 0;
|
|
1518
|
+
}
|
|
1519
|
+
function resolveStaticNextConfigObject(program) {
|
|
1520
|
+
const exported = findDefaultExport(program).declaration;
|
|
1521
|
+
if (exported.type === "FunctionDeclaration" || exported.type === "TSDeclareFunction" || exported.type === "ClassDeclaration" || exported.type === "TSInterfaceDeclaration") {
|
|
1522
|
+
return void 0;
|
|
1523
|
+
}
|
|
1524
|
+
let expression = unwrapExpression(exported);
|
|
1525
|
+
if (expression.type === "Identifier") {
|
|
1526
|
+
const initializer = findVariableInitializer(program, expression.name);
|
|
1527
|
+
expression = initializer === void 0 ? expression : unwrapExpression(initializer);
|
|
1528
|
+
}
|
|
1529
|
+
if (expression.type === "CallExpression") {
|
|
1530
|
+
const factory = unwrapExpression(expression.callee);
|
|
1531
|
+
const hostArgument = expression.arguments[0];
|
|
1532
|
+
if (factory.type === "CallExpression" && hostArgument !== void 0 && hostArgument.type !== "SpreadElement") {
|
|
1533
|
+
expression = unwrapExpression(hostArgument);
|
|
1534
|
+
if (expression.type === "Identifier") {
|
|
1535
|
+
const initializer = findVariableInitializer(program, expression.name);
|
|
1536
|
+
expression = initializer === void 0 ? expression : unwrapExpression(initializer);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
return expression.type === "ObjectExpression" ? expression : void 0;
|
|
1541
|
+
}
|
|
1542
|
+
function proxyFileExtensions(program) {
|
|
1543
|
+
const extensions = new Set(
|
|
1544
|
+
INSTRUMENTATION_EXTENSIONS.map((extension) => extension.slice(1))
|
|
1545
|
+
);
|
|
1546
|
+
const config = resolveStaticNextConfigObject(program);
|
|
1547
|
+
const pageExtensions = config === void 0 ? void 0 : findObjectProperty(config, "pageExtensions");
|
|
1548
|
+
if (pageExtensions === void 0) {
|
|
1549
|
+
return Object.freeze([...extensions]);
|
|
1550
|
+
}
|
|
1551
|
+
const value = unwrapExpression(pageExtensions.value);
|
|
1552
|
+
if (value.type !== "ArrayExpression") {
|
|
1553
|
+
throw new Error(
|
|
1554
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: pageExtensions must be a static string array so SpotPatch can inspect Proxy/Middleware files."
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
for (const element of value.elements) {
|
|
1558
|
+
const extension = staticStringLiteral(element);
|
|
1559
|
+
if (typeof extension !== "string" || extension.length === 0 || extension.includes("/") || extension.includes("\\")) {
|
|
1560
|
+
throw new Error(
|
|
1561
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: pageExtensions must contain only static filename extensions."
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
extensions.add(extension.replace(/^\.+/u, ""));
|
|
1565
|
+
}
|
|
1566
|
+
return Object.freeze([...extensions]);
|
|
1567
|
+
}
|
|
1568
|
+
function rewriteMatcherSource(value) {
|
|
1569
|
+
if (value.includes(SPOTPATCH_MATCHER_EXCLUSION)) {
|
|
1570
|
+
return value;
|
|
1571
|
+
}
|
|
1572
|
+
if (value === SPOTPATCH_API_BASE3 || value.startsWith(`${SPOTPATCH_API_BASE3}/`)) {
|
|
1573
|
+
throw new Error(
|
|
1574
|
+
`SPOTPATCH_PROXY_MATCHER_UNSAFE: Proxy/Middleware explicitly claims ${SPOTPATCH_API_BASE3}.`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
if (value.startsWith("/((?!")) {
|
|
1578
|
+
return `/((?!${SPOTPATCH_MATCHER_EXCLUSION}|${value.slice(5)}`;
|
|
1579
|
+
}
|
|
1580
|
+
if (value === "/:path*" || value === "/:path(.*)" || value === "/(.*)" || value === "/:slug*") {
|
|
1581
|
+
return `/((?!${SPOTPATCH_MATCHER_EXCLUSION}).*)`;
|
|
1582
|
+
}
|
|
1583
|
+
if (value.startsWith("/:") || value.startsWith("/(")) {
|
|
1584
|
+
throw new Error(
|
|
1585
|
+
`SPOTPATCH_PROXY_MATCHER_UNSAFE: cannot safely exclude ${SPOTPATCH_API_BASE3} from matcher ${JSON.stringify(value)}.`
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
return value;
|
|
1589
|
+
}
|
|
1590
|
+
function rewriteMatcherLiteral(magicString, expression) {
|
|
1591
|
+
const value = unwrapExpression(expression);
|
|
1592
|
+
if (value.type !== "Literal" || typeof value.value !== "string") {
|
|
1593
|
+
throw new Error(
|
|
1594
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: matcher entries must use static string sources."
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
const rewritten = rewriteMatcherSource(value.value);
|
|
1598
|
+
if (rewritten !== value.value) {
|
|
1599
|
+
magicString.overwrite(value.start, value.end, JSON.stringify(rewritten));
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
function rewriteMatcherEntry(magicString, expression) {
|
|
1603
|
+
const value = unwrapExpression(expression);
|
|
1604
|
+
if (value.type !== "ObjectExpression") {
|
|
1605
|
+
rewriteMatcherLiteral(magicString, value);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
if (value.properties.some((property) => property.type === "SpreadElement")) {
|
|
1609
|
+
throw new Error(
|
|
1610
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: matcher objects cannot contain spreads."
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
const source = findObjectProperty(value, "source");
|
|
1614
|
+
if (source === void 0) {
|
|
1615
|
+
throw new Error(
|
|
1616
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: matcher objects require a static source."
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
rewriteMatcherLiteral(magicString, source.value);
|
|
1620
|
+
}
|
|
1621
|
+
function transformProxyModule(absolutePath, source) {
|
|
1622
|
+
if (absolutePath.endsWith(".cjs") || absolutePath.endsWith(".cts")) {
|
|
1623
|
+
throw new Error(
|
|
1624
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: CommonJS Proxy/Middleware requires a manual __spotpatch matcher exclusion."
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
const { program } = parseModule(absolutePath, source);
|
|
1628
|
+
const configDeclarations = [];
|
|
1629
|
+
let unsupportedConfigExport = false;
|
|
1630
|
+
for (const statement of program.body) {
|
|
1631
|
+
if (statement.type !== "ExportNamedDeclaration") {
|
|
1632
|
+
continue;
|
|
1633
|
+
}
|
|
1634
|
+
const declaration = statement.declaration;
|
|
1635
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
1636
|
+
for (const declarator of declaration.declarations) {
|
|
1637
|
+
if (declarator.id.type !== "Identifier" || declarator.id.name !== "config") {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
if (declarator.init === null) {
|
|
1641
|
+
unsupportedConfigExport = true;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
const value2 = unwrapExpression(declarator.init);
|
|
1645
|
+
if (value2.type === "ObjectExpression") {
|
|
1646
|
+
configDeclarations.push(value2);
|
|
1647
|
+
} else {
|
|
1648
|
+
unsupportedConfigExport = true;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
if (statement.specifiers.some(
|
|
1653
|
+
(specifier) => specifier.exported.type === "Identifier" && specifier.exported.name === "config"
|
|
1654
|
+
)) {
|
|
1655
|
+
unsupportedConfigExport = true;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
if (unsupportedConfigExport || configDeclarations.length > 1) {
|
|
1659
|
+
throw new Error(
|
|
1660
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: export const config must be one static object."
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
const magicString = new MagicString(source);
|
|
1664
|
+
const config = configDeclarations[0];
|
|
1665
|
+
if (config === void 0) {
|
|
1666
|
+
if (collectIdentifierNames(program).has("config")) {
|
|
1667
|
+
throw new Error(
|
|
1668
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: a local config binding prevents a safe matcher export."
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
magicString.append(
|
|
1672
|
+
`${source.endsWith("\n") || source.length === 0 ? "" : "\n"}
|
|
1673
|
+
export const config = { matcher: [${JSON.stringify(`/((?!${SPOTPATCH_MATCHER_EXCLUSION}).*)`)}] };
|
|
1674
|
+
`
|
|
1675
|
+
);
|
|
1676
|
+
return magicString.toString();
|
|
1677
|
+
}
|
|
1678
|
+
const matcher = findObjectProperty(config, "matcher");
|
|
1679
|
+
if (matcher === void 0) {
|
|
1680
|
+
const indent = childIndent(source, config);
|
|
1681
|
+
const property = `matcher: [${JSON.stringify(`/((?!${SPOTPATCH_MATCHER_EXCLUSION}).*)`)}]`;
|
|
1682
|
+
if (config.properties.length === 0) {
|
|
1683
|
+
magicString.appendLeft(config.end - 1, `
|
|
1684
|
+
${indent}${property},
|
|
1685
|
+
`);
|
|
1686
|
+
} else {
|
|
1687
|
+
magicString.appendLeft(
|
|
1688
|
+
config.properties[0]?.start ?? config.end - 1,
|
|
1689
|
+
`${property},
|
|
1690
|
+
${indent}`
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
return magicString.toString();
|
|
1694
|
+
}
|
|
1695
|
+
const value = unwrapExpression(matcher.value);
|
|
1696
|
+
if (value.type === "ArrayExpression") {
|
|
1697
|
+
for (const element of value.elements) {
|
|
1698
|
+
if (element === null || element.type === "SpreadElement") {
|
|
1699
|
+
throw new Error(
|
|
1700
|
+
"SPOTPATCH_PROXY_MATCHER_UNSAFE: matcher arrays cannot contain holes or spreads."
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
rewriteMatcherEntry(magicString, element);
|
|
1704
|
+
}
|
|
1705
|
+
} else {
|
|
1706
|
+
rewriteMatcherEntry(magicString, value);
|
|
1707
|
+
}
|
|
1708
|
+
return magicString.toString();
|
|
1709
|
+
}
|
|
1710
|
+
async function findProxyModule(appRoot, configPath, configSource) {
|
|
1711
|
+
const { program } = parseModule(configPath, configSource);
|
|
1712
|
+
const extensions = proxyFileExtensions(program);
|
|
1713
|
+
const candidates = (await Promise.all(
|
|
1714
|
+
[appRoot, path3.join(appRoot, "src")].flatMap(
|
|
1715
|
+
(directory) => PROXY_FILE_BASE_NAMES.flatMap(
|
|
1716
|
+
(baseName) => extensions.map(async (extension) => {
|
|
1717
|
+
const candidate = path3.join(directory, `${baseName}.${extension}`);
|
|
1718
|
+
return await integrationPathExists(candidate) ? candidate : void 0;
|
|
1719
|
+
})
|
|
1720
|
+
)
|
|
1721
|
+
)
|
|
1722
|
+
)).filter((value) => value !== void 0);
|
|
1723
|
+
if (candidates.length > 1) {
|
|
1724
|
+
throw new Error(
|
|
1725
|
+
`SPOTPATCH_PROXY_MATCHER_UNSAFE: found multiple Proxy/Middleware files (${candidates.map((candidate) => path3.relative(appRoot, candidate)).join(", ")}).`
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
return candidates[0];
|
|
1729
|
+
}
|
|
1215
1730
|
function parsePackageManifest(source) {
|
|
1216
1731
|
let value;
|
|
1217
1732
|
try {
|
|
@@ -1342,7 +1857,9 @@ async function planNextIntegration(directory = process.cwd()) {
|
|
|
1342
1857
|
]);
|
|
1343
1858
|
const trustedFastModeAvailable = discoveredCheck !== void 0;
|
|
1344
1859
|
const instrumentationPath = await resolveInstrumentationPath(appRoot, configPath);
|
|
1860
|
+
const proxyPath = await findProxyModule(appRoot, configPath, configSource);
|
|
1345
1861
|
const instrumentationSource = await integrationPathExists(instrumentationPath) ? await readIntegrationFile(instrumentationPath) : void 0;
|
|
1862
|
+
const proxySource = proxyPath === void 0 ? void 0 : await readIntegrationFile(proxyPath);
|
|
1346
1863
|
const changes = [
|
|
1347
1864
|
createIntegrationFileChange(
|
|
1348
1865
|
appRoot,
|
|
@@ -1361,7 +1878,15 @@ async function planNextIntegration(directory = process.cwd()) {
|
|
|
1361
1878
|
packagePath,
|
|
1362
1879
|
transformPackageJson(packageSource),
|
|
1363
1880
|
packageSource
|
|
1364
|
-
)
|
|
1881
|
+
),
|
|
1882
|
+
...proxyPath === void 0 || proxySource === void 0 ? [] : [
|
|
1883
|
+
createIntegrationFileChange(
|
|
1884
|
+
appRoot,
|
|
1885
|
+
proxyPath,
|
|
1886
|
+
transformProxyModule(proxyPath, proxySource),
|
|
1887
|
+
proxySource
|
|
1888
|
+
)
|
|
1889
|
+
]
|
|
1365
1890
|
].filter((change) => change !== void 0);
|
|
1366
1891
|
return Object.freeze({
|
|
1367
1892
|
appRoot,
|
|
@@ -1399,7 +1924,7 @@ async function checkNextIntegration(directory = process.cwd()) {
|
|
|
1399
1924
|
// src/cli.ts
|
|
1400
1925
|
function writeUsage() {
|
|
1401
1926
|
process.stderr.write(
|
|
1402
|
-
"Usage: spotpatch-next <dev|init|check>\n dev [next dev options] Start the local Next.js development server.\n init Preview and apply safe integration changes.\n check Verify the integration without writing files.\n"
|
|
1927
|
+
"Usage: spotpatch-next <dev|init|check|connect|bridge>\n dev [next dev options] Start the local Next.js development server.\n init Preview and apply safe integration changes.\n check Verify the integration without writing files.\n connect codex Start the zero-setup Codex Agent connector.\n bridge Run external-Agent MCP, CLI, or setup commands.\n"
|
|
1403
1928
|
);
|
|
1404
1929
|
}
|
|
1405
1930
|
function verifyAdapterExports(appRoot) {
|
|
@@ -1491,6 +2016,12 @@ async function main(arguments_) {
|
|
|
1491
2016
|
if (command === "check") {
|
|
1492
2017
|
return runCheck(rest);
|
|
1493
2018
|
}
|
|
2019
|
+
if (command === "bridge") {
|
|
2020
|
+
return runSpotPatchBridgeCli(rest, { adapter: "next" });
|
|
2021
|
+
}
|
|
2022
|
+
if (command === "connect") {
|
|
2023
|
+
return runSpotPatchBridgeCli(arguments_, { adapter: "next" });
|
|
2024
|
+
}
|
|
1494
2025
|
writeUsage();
|
|
1495
2026
|
return 1;
|
|
1496
2027
|
}
|