@betterportal/framework 10.1.61 → 10.1.62
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/lib/adapters/h3.d.ts.map +1 -1
- package/lib/adapters/h3.js +48 -28
- package/lib/adapters/h3.js.map +1 -1
- package/lib/cli/client.d.ts +1 -1
- package/lib/cli/client.d.ts.map +1 -1
- package/lib/cli/client.js +8 -8
- package/lib/cli/client.js.map +1 -1
- package/lib/codegen/emitter.d.ts.map +1 -1
- package/lib/codegen/emitter.js +22 -48
- package/lib/codegen/emitter.js.map +1 -1
- package/lib/codegen/scanner.d.ts +2 -1
- package/lib/codegen/scanner.d.ts.map +1 -1
- package/lib/codegen/scanner.js +22 -13
- package/lib/codegen/scanner.js.map +1 -1
- package/lib/codegen/validate.d.ts.map +1 -1
- package/lib/codegen/validate.js +70 -0
- package/lib/codegen/validate.js.map +1 -1
- package/lib/contracts/manifest.d.ts +166 -150
- package/lib/contracts/manifest.d.ts.map +1 -1
- package/lib/contracts/manifest.js +5 -2
- package/lib/contracts/manifest.js.map +1 -1
- package/lib/contracts/platformConfig.d.ts +85 -65
- package/lib/contracts/platformConfig.d.ts.map +1 -1
- package/lib/contracts/platformConfig.js +37 -28
- package/lib/contracts/platformConfig.js.map +1 -1
- package/lib/contracts/registry.d.ts +12 -17
- package/lib/contracts/registry.d.ts.map +1 -1
- package/lib/contracts/view.d.ts +97 -6
- package/lib/contracts/view.d.ts.map +1 -1
- package/lib/contracts/view.js +12 -6
- package/lib/contracts/view.js.map +1 -1
- package/lib/runtime/manifest.js +1 -1
- package/lib/runtime/registry.d.ts.map +1 -1
- package/lib/runtime/registry.js +99 -65
- package/lib/runtime/registry.js.map +1 -1
- package/lib/runtime/view.d.ts +2 -1
- package/lib/runtime/view.d.ts.map +1 -1
- package/lib/runtime/view.js +18 -13
- package/lib/runtime/view.js.map +1 -1
- package/package.json +1 -1
package/lib/adapters/h3.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h3.d.ts","sourceRoot":"","sources":["../../src/adapters/h3.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxG,OAAO,KAAK,EAAsB,oBAAoB,EAAE,eAAe,EAAwD,MAAM,0BAA0B,CAAC;AAChK,OAAO,KAAK,EAGV,WAAW,EAIX,mBAAmB,EACnB,oBAAoB,EAIrB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,sBAAsB,CAAC;AAErE,OAAO,EASL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AA0B1B,MAAM,WAAW,4BAA4B;IAC3C,0BAA0B,CAAC,EAAE,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,uBAAuB,KAChC,yBAAyB,CAAC;IAC/B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,GAAG,aAAa,GAAG,SAAS,CAAC;IACnI;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,sBAAsB,EAAE,mBAAmB,CAAC,GAAG,OAAO,sBAAsB,EAAE,mBAAmB,CAAC;IAC1K,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7I;AAQD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,oBAAoB,CAAC;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE;QACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AA+KD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAKvE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExG;AA2CD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,oBAAoB,EAC9B,GAAG,EAAE,iBAAiB,EACtB,OAAO,GAAE,4BAAiC,GACzC,IAAI,CAqGN;
|
|
1
|
+
{"version":3,"file":"h3.d.ts","sourceRoot":"","sources":["../../src/adapters/h3.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxG,OAAO,KAAK,EAAsB,oBAAoB,EAAE,eAAe,EAAwD,MAAM,0BAA0B,CAAC;AAChK,OAAO,KAAK,EAGV,WAAW,EAIX,mBAAmB,EACnB,oBAAoB,EAIrB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,sBAAsB,CAAC;AAErE,OAAO,EASL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AA0B1B,MAAM,WAAW,4BAA4B;IAC3C,0BAA0B,CAAC,EAAE,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,uBAAuB,KAChC,yBAAyB,CAAC;IAC/B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,GAAG,aAAa,GAAG,SAAS,CAAC;IACnI;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,sBAAsB,EAAE,mBAAmB,CAAC,GAAG,OAAO,sBAAsB,EAAE,mBAAmB,CAAC;IAC1K,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7I;AAQD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,oBAAoB,CAAC;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE;QACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AA+KD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAKvE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExG;AA2CD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,oBAAoB,EAC9B,GAAG,EAAE,iBAAiB,EACtB,OAAO,GAAE,4BAAiC,GACzC,IAAI,CAqGN;AA8iDD;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;CAChC,GACL,IAAI,CAuDN"}
|
package/lib/adapters/h3.js
CHANGED
|
@@ -142,7 +142,7 @@ async function resolveRequiredHandlerContext(event, routerOptions, route) {
|
|
|
142
142
|
: null;
|
|
143
143
|
}
|
|
144
144
|
export function isBpManagementAuthRoute(route) {
|
|
145
|
-
return route.auth.required && (route.path === "/.well-known/bp"
|
|
145
|
+
return Object.values(route.methodRoutes ?? {}).some((operation) => operation?.auth.required) && (route.path === "/.well-known/bp"
|
|
146
146
|
|| route.path.startsWith("/.well-known/bp/"));
|
|
147
147
|
}
|
|
148
148
|
export function isBpManagementAuthPath(routes, pathname) {
|
|
@@ -288,6 +288,9 @@ function requestAttributes(event, route, method, extra = {}) {
|
|
|
288
288
|
"network.protocol.name": requestUrl.protocol.replace(":", ""),
|
|
289
289
|
"bp.route.path": route.path,
|
|
290
290
|
"bp.route.view_id": route.viewId,
|
|
291
|
+
...route.methodRoutes?.[method]?.operationId
|
|
292
|
+
? { "bp.route.operation_id": route.methodRoutes[method].operationId }
|
|
293
|
+
: {},
|
|
291
294
|
...(requestIp ? { "client.address": requestIp } : {}),
|
|
292
295
|
...extra
|
|
293
296
|
};
|
|
@@ -326,6 +329,7 @@ function logNegotiationFailure(obs, route, method, reason, attributes = {}) {
|
|
|
326
329
|
status: 406,
|
|
327
330
|
reason,
|
|
328
331
|
"bp.route.view_id": route.viewId,
|
|
332
|
+
"bp.route.operation_id": route.methodRoutes?.[method]?.operationId ?? "",
|
|
329
333
|
...attributes
|
|
330
334
|
});
|
|
331
335
|
}
|
|
@@ -347,19 +351,20 @@ function routePathsMatch(left, right) {
|
|
|
347
351
|
function routeMountServicePath(routeMount) {
|
|
348
352
|
return routeMount.resolvedServicePath ?? routeMount.servicePathVariant ?? routeMount.targetPath;
|
|
349
353
|
}
|
|
350
|
-
function methodAllowed(methods, method) {
|
|
351
|
-
return (methods?.length ? methods : ["GET"]).some((candidate) => candidate.toUpperCase() === method);
|
|
352
|
-
}
|
|
353
354
|
function appAllowsRoute(app, route, method, url, acceptHeader) {
|
|
354
355
|
// Well-known routes are control-plane/service endpoints. Their access policy
|
|
355
356
|
// is declared by the route and must not depend on being mounted as an app page.
|
|
356
357
|
if (route.path.startsWith("/.well-known/"))
|
|
357
358
|
return { allowed: true };
|
|
359
|
+
const operationId = route.methodRoutes?.[method]?.operationId;
|
|
360
|
+
if (!operationId)
|
|
361
|
+
return { allowed: false, reason: "operation_not_registered" };
|
|
362
|
+
const legacyOperationId = `legacy:${route.viewId}:${method}`;
|
|
358
363
|
const appRoute = app.routes.find((candidate) => {
|
|
359
364
|
const servicePath = routeMountServicePath(candidate);
|
|
360
365
|
return candidate.enabled !== false
|
|
361
366
|
&& candidate.viewId === route.viewId
|
|
362
|
-
&&
|
|
367
|
+
&& (candidate.operations.includes(operationId) || candidate.operations.includes(legacyOperationId))
|
|
363
368
|
&& (!servicePath || routePathsMatch(servicePath, route.path));
|
|
364
369
|
});
|
|
365
370
|
if (appRoute)
|
|
@@ -622,7 +627,7 @@ function createUiRouteUrlBuilder(event, extraContext, dependencyAliases, current
|
|
|
622
627
|
const serviceIds = serviceReferenceIds(extraContext, targetServiceId, dependencyAliases);
|
|
623
628
|
const routes = appRouteIndex(extraContext.app).filter((candidate) => candidate.enabled !== false
|
|
624
629
|
&& (candidate.kind ?? "page") === "page"
|
|
625
|
-
&&
|
|
630
|
+
&& (candidate.resolvedMethods?.includes("GET") ?? false)
|
|
626
631
|
&& candidate.viewId === viewId
|
|
627
632
|
&& serviceIds.has(candidate.serviceId));
|
|
628
633
|
const resolved = routes.flatMap((route) => {
|
|
@@ -649,6 +654,7 @@ function rejectUnallowedAppRoute(obs, route, method, extraContext, reason) {
|
|
|
649
654
|
method,
|
|
650
655
|
reason,
|
|
651
656
|
"bp.route.view_id": route.viewId,
|
|
657
|
+
"bp.route.operation_id": route.methodRoutes?.[method]?.operationId ?? "",
|
|
652
658
|
"bp.route.path": route.path,
|
|
653
659
|
"bp.app.id": extraContext.app.id,
|
|
654
660
|
"bp.tenant.id": extraContext.tenant.id,
|
|
@@ -656,6 +662,7 @@ function rejectUnallowedAppRoute(obs, route, method, extraContext, reason) {
|
|
|
656
662
|
});
|
|
657
663
|
return coreJsonResponse({ error: "Route not found" }, 404, "route.not_mounted", reason, {
|
|
658
664
|
"bp.route.view_id": route.viewId,
|
|
665
|
+
"bp.route.operation_id": route.methodRoutes?.[method]?.operationId ?? "",
|
|
659
666
|
"bp.route.path": route.path,
|
|
660
667
|
"bp.route_allowlist.reason": reason
|
|
661
668
|
});
|
|
@@ -713,13 +720,13 @@ async function withSpan(obs, name, attributes, handler) {
|
|
|
713
720
|
}
|
|
714
721
|
async function handleRouteRequest(registryRoutes, dependencyAliases, route, method, event, obs, routerOptions = {}) {
|
|
715
722
|
const methodRoute = route.methodRoutes?.[method];
|
|
716
|
-
const handler = methodRoute?.handler
|
|
723
|
+
const handler = methodRoute?.handler;
|
|
717
724
|
const schemas = {
|
|
718
725
|
...route.schemas,
|
|
719
726
|
...(methodRoute?.schemas ?? {}),
|
|
720
727
|
params: methodRoute?.schemas.params ?? route.schemas.params
|
|
721
728
|
};
|
|
722
|
-
if (!handler) {
|
|
729
|
+
if (!handler || !methodRoute) {
|
|
723
730
|
return coreJsonResponse({ error: `No handler for ${method} ${route.path}` }, 405, "route.handler_missing", `No handler for ${method} ${route.path}`);
|
|
724
731
|
}
|
|
725
732
|
// -- Parse inputs -------------------------------------------------
|
|
@@ -810,11 +817,11 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
810
817
|
}, dependencyAliases, renderer, "page", "page", undefined, status) : undefined;
|
|
811
818
|
};
|
|
812
819
|
// -- Auth resolution (per spec section 0.5) ----------------------
|
|
813
|
-
const apiAuth =
|
|
820
|
+
const apiAuth = methodRoute.auth;
|
|
814
821
|
const authResolved = await loadAuthContext(event, route, routerOptions, obs);
|
|
815
822
|
const authResult = await resolveRequestAuth(apiAuth, event, authResolved, route, method, obs);
|
|
816
823
|
if (authResult.error) {
|
|
817
|
-
return renderAuthError(route, event, authResult.status, authResult.code ?? "auth.unclassified", authResult.error, authResult.requiredPermissions, earlyRenderContext(authResult.status));
|
|
824
|
+
return renderAuthError(route, event, method, authResult.status, authResult.code ?? "auth.unclassified", authResult.error, authResult.requiredPermissions, earlyRenderContext(authResult.status));
|
|
818
825
|
}
|
|
819
826
|
// -- Tenant/app activation check (validateTenantApp hook -> 426) -----
|
|
820
827
|
const tenantApp = readTenantAppFromEvent(event);
|
|
@@ -827,13 +834,13 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
827
834
|
appId: tenantApp.appId,
|
|
828
835
|
reason: validation.reason ?? "(unspecified)"
|
|
829
836
|
});
|
|
830
|
-
return renderUpgradeRequired(route, event, validation, earlyRenderContext(426));
|
|
837
|
+
return renderUpgradeRequired(route, event, method, validation, earlyRenderContext(426));
|
|
831
838
|
}
|
|
832
839
|
}
|
|
833
840
|
catch (err) {
|
|
834
841
|
obs?.logger.warn("validateTenantApp threw: {msg}", { msg: err.message });
|
|
835
842
|
// Fail-open: validation error treated as block.
|
|
836
|
-
return renderUpgradeRequired(route, event, {
|
|
843
|
+
return renderUpgradeRequired(route, event, method, {
|
|
837
844
|
allowed: false,
|
|
838
845
|
reason: "Tenant-app validation error"
|
|
839
846
|
}, earlyRenderContext(426));
|
|
@@ -877,6 +884,7 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
877
884
|
}
|
|
878
885
|
rawData = await withCoreFailure(event, "handler.exception", () => withSpan(obs, "bp.route.handler", {
|
|
879
886
|
"bp.route.view_id": route.viewId,
|
|
887
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
880
888
|
"bp.route.path": route.path,
|
|
881
889
|
"http.request.method": method,
|
|
882
890
|
"bp.route.stream_buffered": true
|
|
@@ -885,6 +893,7 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
885
893
|
else {
|
|
886
894
|
rawData = await withCoreFailure(event, "handler.exception", () => withSpan(obs, "bp.route.handler", {
|
|
887
895
|
"bp.route.view_id": route.viewId,
|
|
896
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
888
897
|
"bp.route.path": route.path,
|
|
889
898
|
"http.request.method": method
|
|
890
899
|
}, () => handler(ctx)));
|
|
@@ -912,9 +921,10 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
912
921
|
title: route.title,
|
|
913
922
|
description: route.description,
|
|
914
923
|
path: route.path,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
924
|
+
operationId: methodRoute.operationId,
|
|
925
|
+
method,
|
|
926
|
+
auth: { ...methodRoute.auth, callers: [...(methodRoute.auth.callers ?? ["user"])] },
|
|
927
|
+
cacheHints: methodRoute.cacheHints
|
|
918
928
|
}, 200, {
|
|
919
929
|
"content-type": "application/vnd.betterportal.metadata+json; charset=utf-8"
|
|
920
930
|
});
|
|
@@ -976,11 +986,12 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
976
986
|
}
|
|
977
987
|
const html = await withCoreFailure(event, "render.status_failed", () => withSpan(obs, "bp.view.render", {
|
|
978
988
|
"bp.route.view_id": route.viewId,
|
|
989
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
979
990
|
"bp.view.renderer": renderer,
|
|
980
991
|
"bp.view.kind": requestedKind,
|
|
981
992
|
"bp.view.status": handlerStatus
|
|
982
993
|
}, () => statusRenderer.render(data, renderContext)));
|
|
983
|
-
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("status",
|
|
994
|
+
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("status", methodRoute.chrome));
|
|
984
995
|
}
|
|
985
996
|
// No specific renderer found.
|
|
986
997
|
if (!shouldFallThroughToDefaultRenderer(handlerStatus)) {
|
|
@@ -1009,11 +1020,12 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
1009
1020
|
}
|
|
1010
1021
|
const html = await withCoreFailure(event, "render.fragment_failed", () => withSpan(obs, "bp.view.render", {
|
|
1011
1022
|
"bp.route.view_id": route.viewId,
|
|
1023
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
1012
1024
|
"bp.view.renderer": renderer,
|
|
1013
1025
|
"bp.view.kind": "fragment",
|
|
1014
1026
|
"bp.view.key": fragmentKey
|
|
1015
1027
|
}, () => resolved.renderer.render(data, renderContext)));
|
|
1016
|
-
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("fragment",
|
|
1028
|
+
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("fragment", methodRoute.chrome));
|
|
1017
1029
|
}
|
|
1018
1030
|
// Component request via `_c` query param
|
|
1019
1031
|
if (componentId) {
|
|
@@ -1031,11 +1043,12 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
1031
1043
|
}
|
|
1032
1044
|
const html = await withCoreFailure(event, "render.component_failed", () => withSpan(obs, "bp.view.render", {
|
|
1033
1045
|
"bp.route.view_id": route.viewId,
|
|
1046
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
1034
1047
|
"bp.view.renderer": renderer,
|
|
1035
1048
|
"bp.view.kind": "component",
|
|
1036
1049
|
"bp.view.key": componentId
|
|
1037
1050
|
}, () => resolved.renderer.render(data, renderContext)));
|
|
1038
|
-
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("fragment",
|
|
1051
|
+
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType("fragment", methodRoute.chrome));
|
|
1039
1052
|
}
|
|
1040
1053
|
// Page request - only page renderers allowed
|
|
1041
1054
|
const resolved = resolveRenderer(route, renderer, "page", method);
|
|
@@ -1051,11 +1064,12 @@ async function handleRouteRequest(registryRoutes, dependencyAliases, route, meth
|
|
|
1051
1064
|
}
|
|
1052
1065
|
const html = await withCoreFailure(event, "render.page_failed", () => withSpan(obs, "bp.view.render", {
|
|
1053
1066
|
"bp.route.view_id": route.viewId,
|
|
1067
|
+
"bp.route.operation_id": methodRoute.operationId,
|
|
1054
1068
|
"bp.view.renderer": renderer,
|
|
1055
1069
|
"bp.view.kind": "page"
|
|
1056
1070
|
}, () => resolved.renderer.render(data, renderContext)));
|
|
1057
1071
|
const mode = representation.mode ?? "page";
|
|
1058
|
-
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType(mode,
|
|
1072
|
+
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), handlerStatus, htmlContentType(mode, methodRoute.chrome));
|
|
1059
1073
|
}
|
|
1060
1074
|
/**
|
|
1061
1075
|
* Handle representations that stream, returning null for buffered ones so the
|
|
@@ -1093,7 +1107,7 @@ async function handleStreamRepresentation(route, handler, ctx, event, url, metho
|
|
|
1093
1107
|
"bp.view.renderer": renderer,
|
|
1094
1108
|
"bp.view.kind": "stream-shell"
|
|
1095
1109
|
}, () => streamSet.renderShell(shellCtx)));
|
|
1096
|
-
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), 200, htmlContentType("fragment", route.chrome));
|
|
1110
|
+
return htmlResponse(rewriteServiceRouteTokens(toHtmlString(html), ctx.routeUrl, obs), 200, htmlContentType("fragment", route.methodRoutes?.[method]?.chrome));
|
|
1097
1111
|
}
|
|
1098
1112
|
/**
|
|
1099
1113
|
* SSE delivery of the frame stream at `{path}/__sse`. With a theme context and
|
|
@@ -1117,8 +1131,12 @@ async function handleStreamSse(registryRoutes, dependencyAliases, route, handler
|
|
|
1117
1131
|
return params;
|
|
1118
1132
|
// The frame stream carries the same data as the view route - enforce the
|
|
1119
1133
|
// same auth requirement.
|
|
1134
|
+
const operation = route.methodRoutes?.GET;
|
|
1135
|
+
if (!operation) {
|
|
1136
|
+
return coreJsonResponse({ error: "Streaming GET operation metadata is missing" }, 500, "route.operation_missing", "Streaming GET operation metadata is missing");
|
|
1137
|
+
}
|
|
1120
1138
|
const authResolved = await loadAuthContext(event, route, routerOptions, obs);
|
|
1121
|
-
const authResult = await resolveRequestAuth(
|
|
1139
|
+
const authResult = await resolveRequestAuth(operation.auth, event, authResolved, route, "GET", obs);
|
|
1122
1140
|
if (authResult.error) {
|
|
1123
1141
|
return coreJsonResponse({ error: authResult.error, status: authResult.status }, authResult.status, authResult.code ?? "auth.unclassified", authResult.error);
|
|
1124
1142
|
}
|
|
@@ -1428,11 +1446,12 @@ function formatRequiredPermissions(requiredPermissions = []) {
|
|
|
1428
1446
|
.map((requirement) => `${requirement.serviceId} / ${requirement.viewId} / ${requirement.permissions.join("+")}`)
|
|
1429
1447
|
.join("; ");
|
|
1430
1448
|
}
|
|
1431
|
-
function renderAuthError(route, event, status, code, message, requiredPermissions = [], context) {
|
|
1449
|
+
function renderAuthError(route, event, method, status, code, message, requiredPermissions = [], context) {
|
|
1432
1450
|
const renderer = rendererFromEvent(event);
|
|
1433
1451
|
const acceptHeader = acceptHeaderFromEvent(event);
|
|
1434
1452
|
const representation = resolveRequestedRepresentation(acceptHeader);
|
|
1435
1453
|
const corsHeaders = corsHeadersFromEvent(event);
|
|
1454
|
+
const chrome = route.methodRoutes?.[method]?.chrome;
|
|
1436
1455
|
// Auth errors NEVER emit navigation headers (HX-Location / HX-Redirect). A
|
|
1437
1456
|
// service has no reliable knowledge of where the auth provider lives - it only
|
|
1438
1457
|
// knows the JWKS for token *validation*, not a URL the browser should navigate
|
|
@@ -1442,13 +1461,13 @@ function renderAuthError(route, event, status, code, message, requiredPermission
|
|
|
1442
1461
|
// Prefer a route/theme status view so the body swaps cleanly into the htmx
|
|
1443
1462
|
// target as a fragment rather than replacing the shell.
|
|
1444
1463
|
if (renderer && (representation.kind === "html")) {
|
|
1445
|
-
const statusRenderer = resolveStatusRenderer(route, renderer, status, "page", undefined,
|
|
1464
|
+
const statusRenderer = resolveStatusRenderer(route, renderer, status, "page", undefined, method);
|
|
1446
1465
|
if (statusRenderer && context) {
|
|
1447
1466
|
try {
|
|
1448
1467
|
const html = statusRenderer.render({ error: message, status, requiredPermissions }, context);
|
|
1449
1468
|
return coreResponse(new Response(toHtmlString(html), {
|
|
1450
1469
|
status,
|
|
1451
|
-
headers: { ...corsHeaders, "content-type": htmlContentType("status",
|
|
1470
|
+
headers: { ...corsHeaders, "content-type": htmlContentType("status", chrome) }
|
|
1452
1471
|
}), code, message);
|
|
1453
1472
|
}
|
|
1454
1473
|
catch {
|
|
@@ -1469,7 +1488,7 @@ function renderAuthError(route, event, status, code, message, requiredPermission
|
|
|
1469
1488
|
`;
|
|
1470
1489
|
return coreResponse(new Response(html, {
|
|
1471
1490
|
status,
|
|
1472
|
-
headers: { ...corsHeaders, "content-type": renderer ? htmlContentType("status",
|
|
1491
|
+
headers: { ...corsHeaders, "content-type": renderer ? htmlContentType("status", chrome) : "text/html; charset=utf-8" }
|
|
1473
1492
|
}), code, message);
|
|
1474
1493
|
}
|
|
1475
1494
|
return coreJsonResponse({ error: message, status, requiredPermissions }, status, code, message, undefined, corsHeaders);
|
|
@@ -1492,18 +1511,19 @@ function readTenantAppFromEvent(event) {
|
|
|
1492
1511
|
return undefined;
|
|
1493
1512
|
return { tenantId: ctx.__bpTenantId, appId: ctx.__bpAppId };
|
|
1494
1513
|
}
|
|
1495
|
-
function renderUpgradeRequired(route, event, validation, context) {
|
|
1514
|
+
function renderUpgradeRequired(route, event, method, validation, context) {
|
|
1496
1515
|
const renderer = rendererFromEvent(event);
|
|
1497
1516
|
const acceptHeader = acceptHeaderFromEvent(event);
|
|
1498
1517
|
const representation = resolveRequestedRepresentation(acceptHeader);
|
|
1499
1518
|
const status = 426;
|
|
1519
|
+
const chrome = route.methodRoutes?.[method]?.chrome;
|
|
1500
1520
|
// Honor Retry-After if requested
|
|
1501
1521
|
const extraHeaders = {};
|
|
1502
1522
|
if (validation.retryAfterSeconds) {
|
|
1503
1523
|
extraHeaders["retry-after"] = String(validation.retryAfterSeconds);
|
|
1504
1524
|
}
|
|
1505
1525
|
if (renderer && representation.kind === "html") {
|
|
1506
|
-
const statusRenderer = resolveStatusRenderer(route, renderer, status, "page", undefined,
|
|
1526
|
+
const statusRenderer = resolveStatusRenderer(route, renderer, status, "page", undefined, method);
|
|
1507
1527
|
if (statusRenderer && context) {
|
|
1508
1528
|
try {
|
|
1509
1529
|
const html = statusRenderer.render({
|
|
@@ -1511,7 +1531,7 @@ function renderUpgradeRequired(route, event, validation, context) {
|
|
|
1511
1531
|
reason: validation.reason,
|
|
1512
1532
|
upgradeUrl: validation.upgradeUrl
|
|
1513
1533
|
}, context);
|
|
1514
|
-
return coreResponse(htmlResponse(toHtmlString(html), status, htmlContentType("status",
|
|
1534
|
+
return coreResponse(htmlResponse(toHtmlString(html), status, htmlContentType("status", chrome)), "route.tenant_app_unavailable", validation.reason ?? "Tenant/app is not available for this service");
|
|
1515
1535
|
}
|
|
1516
1536
|
catch {
|
|
1517
1537
|
// fall through to JSON
|