@nocobase/plugin-flow-engine 2.2.0-beta.13 → 2.2.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/getContextApis.js +2 -2
  2. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/getContextEnvs.js +2 -2
  3. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/getContextVars.js +2 -2
  4. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/lintAndTestJS.js +2 -2
  5. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/patchJSCode.js +2 -2
  6. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/readJSCode.js +2 -2
  7. package/dist/ai/ai-employees/nathan/skills/frontend-developer/tools/writeJSCode.js +2 -2
  8. package/dist/ai/docs/runjs/context/ai.md +206 -0
  9. package/dist/externalVersion.js +9 -9
  10. package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
  11. package/dist/node_modules/acorn/package.json +1 -1
  12. package/dist/node_modules/acorn-jsx/package.json +1 -1
  13. package/dist/node_modules/acorn-walk/package.json +1 -1
  14. package/dist/node_modules/ses/package.json +1 -1
  15. package/dist/node_modules/zod/package.json +1 -1
  16. package/dist/server/flow-surfaces/authoring-validation.d.ts +2 -1
  17. package/dist/server/flow-surfaces/authoring-validation.js +16 -10
  18. package/dist/server/flow-surfaces/blueprint/compile-plan.js +19 -15
  19. package/dist/server/flow-surfaces/blueprint/normalize-document.js +18 -2
  20. package/dist/server/flow-surfaces/blueprint/public-types.d.ts +1 -0
  21. package/dist/server/flow-surfaces/constants.d.ts +9 -2
  22. package/dist/server/flow-surfaces/constants.js +1 -0
  23. package/dist/server/flow-surfaces/errors.d.ts +2 -2
  24. package/dist/server/flow-surfaces/errors.js +4 -4
  25. package/dist/server/flow-surfaces/navigation-targets.d.ts +73 -0
  26. package/dist/server/flow-surfaces/navigation-targets.js +439 -0
  27. package/dist/server/flow-surfaces/runjs-authoring/runtime/constants.js +1 -0
  28. package/dist/server/flow-surfaces/service.d.ts +14 -2
  29. package/dist/server/flow-surfaces/service.js +464 -110
  30. package/dist/swagger/flow-surfaces.d.ts +96 -0
  31. package/dist/swagger/flow-surfaces.js +88 -7
  32. package/dist/swagger/index.d.ts +96 -0
  33. package/package.json +2 -2
@@ -66,6 +66,7 @@ var import_runtime = require("./planning/runtime");
66
66
  var import_created_keys = require("./planning/created-keys");
67
67
  var import_key_persistence = require("./planning/key-persistence");
68
68
  var import_payload_shape = require("./payload-shape");
69
+ var import_navigation_targets = require("./navigation-targets");
69
70
  var import_field_semantics = require("./field-semantics");
70
71
  var import_field_grid_layout = require("./field-grid-layout");
71
72
  var import_authoring_validation = require("./authoring-validation");
@@ -689,7 +690,6 @@ function assertNoFlowSurfaceIdTitleFieldSettings(value, options = {}) {
689
690
  };
690
691
  visit(value, options.basePath || []);
691
692
  }
692
- const DEFAULT_ADMIN_UI_LAYOUT_UID = "admin-layout-model";
693
693
  const MOBILE_UI_LAYOUT_TYPE = "mobile";
694
694
  class FlowSurfacesService {
695
695
  constructor(plugin) {
@@ -711,6 +711,9 @@ class FlowSurfacesService {
711
711
  (values, options) => this.patchFlowSurfaceModelOptions(values, options)
712
712
  );
713
713
  }
714
+ get navigationTargets() {
715
+ return new import_navigation_targets.FlowSurfaceNavigationTargetsService(this.db);
716
+ }
714
717
  getFlowTemplateRepository(actionName) {
715
718
  try {
716
719
  const repo = this.db.getRepository("flowModelTemplates");
@@ -924,7 +927,7 @@ class FlowSurfacesService {
924
927
  transaction
925
928
  });
926
929
  }
927
- async findMenuGroupRoutesByTitle(title, transaction, layoutUid) {
930
+ async findMenuGroupRoutesByTitle(title, transaction, layoutUid, portalUid) {
928
931
  const normalizedTitle = String(title || "").trim();
929
932
  if (!normalizedTitle) {
930
933
  return [];
@@ -938,6 +941,20 @@ class FlowSurfacesService {
938
941
  transaction
939
942
  })
940
943
  );
944
+ const normalizedPortalUid = this.navigationTargets.normalizePortalUid(portalUid);
945
+ if (normalizedPortalUid) {
946
+ const matchedRoutes2 = [];
947
+ for (const route of routes) {
948
+ const routePortalUids = await this.navigationTargets.readRoutePortalUids(
949
+ this.readRouteField(route, "id"),
950
+ transaction
951
+ );
952
+ if (routePortalUids.includes(normalizedPortalUid)) {
953
+ matchedRoutes2.push(route);
954
+ }
955
+ }
956
+ return matchedRoutes2;
957
+ }
941
958
  const layoutUidFilter = await this.resolveDesktopRouteLayoutUidFilter(layoutUid, transaction);
942
959
  if (!layoutUidFilter.length || !this.hasDesktopRouteUiLayoutsRelation()) {
943
960
  return routes;
@@ -957,13 +974,13 @@ class FlowSurfacesService {
957
974
  }
958
975
  return String(routeParentId ?? "") === String(parentId ?? "");
959
976
  }
960
- async findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction, layoutUid) {
961
- const routes = await this.findMenuGroupRoutesByTitle(title, transaction, layoutUid);
977
+ async findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction, layoutUid, portalUid) {
978
+ const routes = await this.findMenuGroupRoutesByTitle(title, transaction, layoutUid, portalUid);
962
979
  return routes.filter(
963
980
  (route) => this.routeParentIdMatches(this.readRouteField(route, "parentId") ?? null, parentId)
964
981
  );
965
982
  }
966
- async findFlowPageRoutesByParentIdAndTitle(parentId, title, transaction, layoutUid) {
983
+ async findFlowPageRoutesByParentIdAndTitle(parentId, title, transaction, layoutUid, portalUid) {
967
984
  const normalizedTitle = String(title || "").trim();
968
985
  if (!normalizedTitle) {
969
986
  return [];
@@ -978,6 +995,22 @@ class FlowSurfacesService {
978
995
  transaction
979
996
  })
980
997
  );
998
+ const normalizedPortalUid = this.navigationTargets.normalizePortalUid(portalUid);
999
+ if (normalizedPortalUid) {
1000
+ const portalMatchedRoutes = [];
1001
+ for (const route of routes) {
1002
+ const routePortalUids = await this.navigationTargets.readRoutePortalUids(
1003
+ this.readRouteField(route, "id"),
1004
+ transaction
1005
+ );
1006
+ if (routePortalUids.includes(normalizedPortalUid)) {
1007
+ portalMatchedRoutes.push(route);
1008
+ }
1009
+ }
1010
+ return portalMatchedRoutes.filter(
1011
+ (route) => this.routeParentIdMatches(this.readRouteField(route, "parentId") ?? null, parentId)
1012
+ );
1013
+ }
981
1014
  const layoutUidFilter = await this.resolveDesktopRouteLayoutUidFilter(layoutUid, transaction);
982
1015
  const layoutMatchedRoutes = [];
983
1016
  if (!layoutUidFilter.length || !this.hasDesktopRouteUiLayoutsRelation()) {
@@ -1091,12 +1124,12 @@ class FlowSurfacesService {
1091
1124
  }
1092
1125
  const defaultLayout = await this.db.getRepository("uiLayouts").findOne({
1093
1126
  filter: {
1094
- uid: DEFAULT_ADMIN_UI_LAYOUT_UID,
1127
+ uid: import_navigation_targets.DEFAULT_ADMIN_UI_LAYOUT_UID,
1095
1128
  enabled: true
1096
1129
  },
1097
1130
  transaction
1098
1131
  });
1099
- return defaultLayout ? [DEFAULT_ADMIN_UI_LAYOUT_UID] : [];
1132
+ return defaultLayout ? [import_navigation_targets.DEFAULT_ADMIN_UI_LAYOUT_UID] : [];
1100
1133
  }
1101
1134
  async findDesktopRouteUiLayoutByUid(layoutUid, transaction) {
1102
1135
  const normalizedLayoutUid = this.normalizeExplicitDesktopRouteLayoutUid(layoutUid);
@@ -1147,6 +1180,199 @@ class FlowSurfacesService {
1147
1180
  }
1148
1181
  return this.resolveDefaultDesktopRouteUiLayoutUids(transaction);
1149
1182
  }
1183
+ assertNavigationTargetIsExclusive(actionName, layoutUid, portalUid, paths) {
1184
+ const normalizedLayoutUid = this.normalizeExplicitDesktopRouteLayoutUid(layoutUid);
1185
+ const normalizedPortalUid = this.navigationTargets.normalizePortalUid(portalUid);
1186
+ if (!normalizedLayoutUid || !normalizedPortalUid) {
1187
+ return;
1188
+ }
1189
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} cannot use layoutUid and portalUid together`, {
1190
+ ruleId: "navigation-target-mutually-exclusive",
1191
+ path: paths.portal,
1192
+ details: {
1193
+ layoutUid: normalizedLayoutUid,
1194
+ portalUid: normalizedPortalUid,
1195
+ layoutPath: paths.layout,
1196
+ portalPath: paths.portal
1197
+ }
1198
+ });
1199
+ }
1200
+ async readDesktopRouteScope(route, transaction) {
1201
+ const routeId = this.readRouteField(route, "id");
1202
+ return {
1203
+ portalUids: await this.navigationTargets.readRoutePortalUids(routeId, transaction),
1204
+ layoutUids: await this.readDesktopRouteUiLayoutUids(routeId, transaction)
1205
+ };
1206
+ }
1207
+ async resolveRequestedDesktopRouteScope(values, parentRoute, actionName, options = {}) {
1208
+ const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(values.layoutUid);
1209
+ const portalUid = this.navigationTargets.normalizePortalUid(values.portalUid);
1210
+ this.assertNavigationTargetIsExclusive(actionName, layoutUid, portalUid, {
1211
+ layout: "values.layoutUid",
1212
+ portal: "values.portalUid"
1213
+ });
1214
+ if (portalUid) {
1215
+ const portal = await this.navigationTargets.resolvePortal(portalUid, {
1216
+ actionName,
1217
+ path: "values.portalUid",
1218
+ currentRoles: options.currentRoles,
1219
+ transaction: options.transaction
1220
+ });
1221
+ if (parentRoute) {
1222
+ await this.navigationTargets.assertRouteBelongsToPortal(
1223
+ actionName,
1224
+ this.readRouteField(parentRoute, "id"),
1225
+ portal.uid,
1226
+ "values.parentMenuRouteId",
1227
+ options.transaction
1228
+ );
1229
+ }
1230
+ return {
1231
+ portalUids: [portal.uid],
1232
+ layoutUids: [],
1233
+ selectedPortal: portal,
1234
+ layoutType: portal.layoutType
1235
+ };
1236
+ }
1237
+ if (layoutUid) {
1238
+ await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, "values.layoutUid", options.transaction);
1239
+ if (parentRoute) {
1240
+ await this.assertDesktopRouteBelongsToUiLayout(
1241
+ actionName,
1242
+ parentRoute,
1243
+ layoutUid,
1244
+ "values.parentMenuRouteId",
1245
+ options.transaction
1246
+ );
1247
+ }
1248
+ return {
1249
+ portalUids: [],
1250
+ layoutUids: [layoutUid],
1251
+ layoutType: await this.getDesktopRouteUiLayoutTypeByUid(layoutUid, options.transaction)
1252
+ };
1253
+ }
1254
+ if (parentRoute) {
1255
+ const inheritedScope = await this.readDesktopRouteScope(parentRoute, options.transaction);
1256
+ if (inheritedScope.portalUids.length > 1) {
1257
+ (0, import_errors.throwBadRequest)(
1258
+ `flowSurfaces ${actionName} parent route belongs to multiple portals; pass values.portalUid explicitly`,
1259
+ {
1260
+ ruleId: "navigation-route-scope-ambiguous",
1261
+ path: "values.parentMenuRouteId",
1262
+ details: {
1263
+ routeId: this.readRouteField(parentRoute, "id"),
1264
+ portalUids: inheritedScope.portalUids
1265
+ }
1266
+ }
1267
+ );
1268
+ }
1269
+ if (inheritedScope.portalUids.length === 1) {
1270
+ const portal = await this.navigationTargets.resolvePortal(inheritedScope.portalUids[0], {
1271
+ actionName,
1272
+ path: "values.parentMenuRouteId",
1273
+ currentRoles: options.currentRoles,
1274
+ transaction: options.transaction
1275
+ });
1276
+ return {
1277
+ ...inheritedScope,
1278
+ selectedPortal: portal,
1279
+ layoutType: portal.layoutType
1280
+ };
1281
+ }
1282
+ }
1283
+ const layoutUids = await this.resolveInheritedDesktopRouteUiLayoutUids(parentRoute, options.transaction);
1284
+ return {
1285
+ portalUids: [],
1286
+ layoutUids,
1287
+ layoutType: layoutUids.length === 1 ? await this.getDesktopRouteUiLayoutTypeByUid(layoutUids[0], options.transaction) : void 0
1288
+ };
1289
+ }
1290
+ async resolveExistingDesktopRouteScope(values, route, actionName, options = {}) {
1291
+ const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(values.layoutUid);
1292
+ const portalUid = this.navigationTargets.normalizePortalUid(values.portalUid);
1293
+ this.assertNavigationTargetIsExclusive(actionName, layoutUid, portalUid, {
1294
+ layout: "values.layoutUid",
1295
+ portal: "values.portalUid"
1296
+ });
1297
+ const routeScope = await this.readDesktopRouteScope(route, options.transaction);
1298
+ if (portalUid) {
1299
+ const portal = await this.navigationTargets.resolvePortal(portalUid, {
1300
+ actionName,
1301
+ path: "values.portalUid",
1302
+ currentRoles: options.currentRoles,
1303
+ transaction: options.transaction
1304
+ });
1305
+ await this.navigationTargets.assertRouteBelongsToPortal(
1306
+ actionName,
1307
+ this.readRouteField(route, "id"),
1308
+ portal.uid,
1309
+ "values.menuRouteId",
1310
+ options.transaction
1311
+ );
1312
+ return {
1313
+ ...routeScope,
1314
+ selectedPortal: portal,
1315
+ layoutType: portal.layoutType
1316
+ };
1317
+ }
1318
+ if (layoutUid) {
1319
+ await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, "values.layoutUid", options.transaction);
1320
+ await this.assertDesktopRouteBelongsToUiLayout(
1321
+ actionName,
1322
+ route,
1323
+ layoutUid,
1324
+ "values.menuRouteId",
1325
+ options.transaction
1326
+ );
1327
+ return {
1328
+ ...routeScope,
1329
+ layoutType: await this.getDesktopRouteUiLayoutTypeByUid(layoutUid, options.transaction)
1330
+ };
1331
+ }
1332
+ if (routeScope.portalUids.length > 1) {
1333
+ (0, import_errors.throwBadRequest)(
1334
+ `flowSurfaces ${actionName} menu route belongs to multiple portals; pass values.portalUid explicitly`,
1335
+ {
1336
+ ruleId: "navigation-route-scope-ambiguous",
1337
+ path: "values.menuRouteId",
1338
+ details: {
1339
+ routeId: this.readRouteField(route, "id"),
1340
+ portalUids: routeScope.portalUids
1341
+ }
1342
+ }
1343
+ );
1344
+ }
1345
+ if (routeScope.portalUids.length === 1) {
1346
+ const portal = await this.navigationTargets.resolvePortal(routeScope.portalUids[0], {
1347
+ actionName,
1348
+ path: "values.menuRouteId",
1349
+ currentRoles: options.currentRoles,
1350
+ transaction: options.transaction
1351
+ });
1352
+ return {
1353
+ ...routeScope,
1354
+ selectedPortal: portal,
1355
+ layoutType: portal.layoutType
1356
+ };
1357
+ }
1358
+ const layoutUids = routeScope.layoutUids.length ? routeScope.layoutUids : await this.ensureDesktopRouteUiLayouts(route, options.transaction);
1359
+ return {
1360
+ portalUids: [],
1361
+ layoutUids,
1362
+ layoutType: layoutUids.length === 1 ? await this.getDesktopRouteUiLayoutTypeByUid(layoutUids[0], options.transaction) : void 0
1363
+ };
1364
+ }
1365
+ async attachDesktopRouteScopeToRouteAndChildren(routeId, scope, transaction) {
1366
+ if (scope.layoutUids.length) {
1367
+ await this.attachDesktopRouteUiLayoutsToRouteAndChildren(routeId, scope.layoutUids, transaction);
1368
+ }
1369
+ if (scope.portalUids.length) {
1370
+ await this.navigationTargets.attachRouteTreeToPortals(routeId, scope.portalUids, {
1371
+ portalOnly: scope.layoutUids.length === 0,
1372
+ transaction
1373
+ });
1374
+ }
1375
+ }
1150
1376
  async assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, path, transaction) {
1151
1377
  if (!this.hasDesktopRouteUiLayoutsRelation()) {
1152
1378
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} requires plugin-ui-layout desktop route relation support`, {
@@ -1194,23 +1420,6 @@ class FlowSurfacesService {
1194
1420
  }
1195
1421
  });
1196
1422
  }
1197
- async resolveRequestedDesktopRouteUiLayoutUids(values, parentRoute, actionName, transaction) {
1198
- const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(values.layoutUid);
1199
- if (layoutUid) {
1200
- await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, "values.layoutUid", transaction);
1201
- if (parentRoute) {
1202
- await this.assertDesktopRouteBelongsToUiLayout(
1203
- actionName,
1204
- parentRoute,
1205
- layoutUid,
1206
- "values.parentMenuRouteId",
1207
- transaction
1208
- );
1209
- }
1210
- return [layoutUid];
1211
- }
1212
- return this.resolveInheritedDesktopRouteUiLayoutUids(parentRoute, transaction);
1213
- }
1214
1423
  async setDesktopRouteUiLayouts(routeId, uiLayoutUids, transaction) {
1215
1424
  const relationRouteId = this.normalizeDesktopRouteRelationId(routeId);
1216
1425
  if (import_lodash.default.isUndefined(relationRouteId) || !uiLayoutUids.length || !this.hasDesktopRouteUiLayoutsRelation()) {
@@ -1308,21 +1517,18 @@ class FlowSurfacesService {
1308
1517
  });
1309
1518
  }
1310
1519
  }
1311
- async createFlowMenuGroup(values, transaction) {
1312
- const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1520
+ async createFlowMenuGroup(values, options = {}) {
1521
+ var _a;
1522
+ const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, options.transaction);
1313
1523
  const parentId = this.readRouteField(parentRoute, "id") ?? null;
1314
- const routeUiLayoutUids = await this.resolveRequestedDesktopRouteUiLayoutUids(
1315
- values,
1316
- parentRoute,
1317
- "createMenu",
1318
- transaction
1319
- );
1524
+ const routeScope = await this.resolveRequestedDesktopRouteScope(values, parentRoute, "createMenu", options);
1320
1525
  const title = String(values.title || "").trim();
1321
1526
  const existingGroups = await this.findMenuGroupRoutesByParentIdAndTitle(
1322
1527
  parentId,
1323
1528
  title,
1324
- transaction,
1325
- routeUiLayoutUids
1529
+ options.transaction,
1530
+ routeScope.layoutUids,
1531
+ (_a = routeScope.selectedPortal) == null ? void 0 : _a.uid
1326
1532
  );
1327
1533
  if (existingGroups.length === 1) {
1328
1534
  return this.buildMenuResult(existingGroups[0]);
@@ -1359,24 +1565,19 @@ class FlowSurfacesService {
1359
1565
  hideInMenu: !!values.hideInMenu,
1360
1566
  parentId
1361
1567
  },
1362
- transaction
1568
+ transaction: options.transaction
1363
1569
  });
1364
- await this.attachDesktopRouteUiLayoutsToRouteAndChildren(
1570
+ await this.attachDesktopRouteScopeToRouteAndChildren(
1365
1571
  this.readRouteField(route, "id"),
1366
- routeUiLayoutUids,
1367
- transaction
1572
+ routeScope,
1573
+ options.transaction
1368
1574
  );
1369
1575
  return this.buildMenuResult(route);
1370
1576
  }
1371
- async createFlowMenuItem(values, transaction) {
1577
+ async createFlowMenuItem(values, options = {}) {
1372
1578
  var _a;
1373
- const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1374
- const routeUiLayoutUids = await this.resolveRequestedDesktopRouteUiLayoutUids(
1375
- values,
1376
- parentRoute,
1377
- "createMenu",
1378
- transaction
1379
- );
1579
+ const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, options.transaction);
1580
+ const routeScope = await this.resolveRequestedDesktopRouteScope(values, parentRoute, "createMenu", options);
1380
1581
  this.assertVisibleNavigationIcon("createMenu", "values", values);
1381
1582
  const pageSchemaUid = values.pageSchemaUid || (0, import_utils.uid)();
1382
1583
  const menuSchemaUid = (0, import_utils.uid)();
@@ -1419,20 +1620,20 @@ class FlowSurfacesService {
1419
1620
  }
1420
1621
  ]
1421
1622
  },
1422
- transaction
1623
+ transaction: options.transaction
1423
1624
  });
1424
- await this.attachDesktopRouteUiLayoutsToRouteAndChildren(
1625
+ await this.attachDesktopRouteScopeToRouteAndChildren(
1425
1626
  this.readRouteField(createdRoute, "id"),
1426
- routeUiLayoutUids,
1427
- transaction
1627
+ routeScope,
1628
+ options.transaction
1428
1629
  );
1429
1630
  const route = await desktopRoutes.findOne({
1430
1631
  filter: { schemaUid: pageSchemaUid },
1431
1632
  appends: ["children"],
1432
- transaction
1633
+ transaction: options.transaction
1433
1634
  });
1434
1635
  const tabRoute = import_lodash.default.sortBy(import_lodash.default.castArray(((_a = route == null ? void 0 : route.get) == null ? void 0 : _a.call(route, "children")) || []), "sort")[0];
1435
- await this.ensureFlowRoutePageSchemaShell(pageSchemaUid, transaction);
1636
+ await this.ensureFlowRoutePageSchemaShell(pageSchemaUid, options.transaction);
1436
1637
  const pageTree = (0, import_builder.buildPersistedRootPageModel)({
1437
1638
  pageUid,
1438
1639
  pageTitle: title,
@@ -1448,7 +1649,7 @@ class FlowSurfacesService {
1448
1649
  subType: "object",
1449
1650
  ...pageTree
1450
1651
  },
1451
- { transaction }
1652
+ { transaction: options.transaction }
1452
1653
  );
1453
1654
  return this.buildMenuResult(route, {
1454
1655
  pageSchemaUid,
@@ -1460,7 +1661,16 @@ class FlowSurfacesService {
1460
1661
  });
1461
1662
  }
1462
1663
  sanitizePublicCreateMenuValues(values) {
1463
- return import_lodash.default.pick(values, ["title", "type", "layoutUid", "icon", "tooltip", "hideInMenu", "parentMenuRouteId"]);
1664
+ return import_lodash.default.pick(values, [
1665
+ "title",
1666
+ "type",
1667
+ "layoutUid",
1668
+ "portalUid",
1669
+ "icon",
1670
+ "tooltip",
1671
+ "hideInMenu",
1672
+ "parentMenuRouteId"
1673
+ ]);
1464
1674
  }
1465
1675
  async loadRouteBackedPageStructure(route, transaction) {
1466
1676
  const routeId = this.readRouteField(route, "id");
@@ -3372,11 +3582,13 @@ class FlowSurfacesService {
3372
3582
  return document;
3373
3583
  }
3374
3584
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(document.navigation.layoutUid);
3585
+ const portalUid = this.navigationTargets.normalizePortalUid(document.navigation.portalUid);
3375
3586
  const matchedRoutes = await this.findMenuGroupRoutesByParentIdAndTitle(
3376
3587
  null,
3377
3588
  groupTitle,
3378
3589
  transaction,
3379
- layoutUid || await this.resolveDefaultDesktopRouteUiLayoutUids(transaction)
3590
+ layoutUid || await this.resolveDefaultDesktopRouteUiLayoutUids(transaction),
3591
+ portalUid
3380
3592
  );
3381
3593
  if (!matchedRoutes.length) {
3382
3594
  return document;
@@ -3402,13 +3614,20 @@ class FlowSurfacesService {
3402
3614
  }
3403
3615
  };
3404
3616
  }
3405
- async normalizeApplyBlueprintCreateMobileNavigation(document, transaction) {
3406
- var _a;
3617
+ async normalizeApplyBlueprintCreateMobileNavigation(document, options = {}) {
3618
+ var _a, _b;
3407
3619
  if (document.mode !== "create") {
3408
3620
  return document;
3409
3621
  }
3410
3622
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid((_a = document.navigation) == null ? void 0 : _a.layoutUid);
3411
- if (!await this.isMobileDesktopRouteUiLayoutUid(layoutUid, transaction)) {
3623
+ const portalUid = this.navigationTargets.normalizePortalUid((_b = document.navigation) == null ? void 0 : _b.portalUid);
3624
+ const layoutType = portalUid ? (await this.navigationTargets.resolvePortal(portalUid, {
3625
+ actionName: "applyBlueprint",
3626
+ path: "values.navigation.portalUid",
3627
+ currentRoles: options.currentRoles,
3628
+ transaction: options.transaction
3629
+ })).layoutType : await this.getDesktopRouteUiLayoutTypeByUid(layoutUid || "", options.transaction);
3630
+ if (layoutType !== MOBILE_UI_LAYOUT_TYPE) {
3412
3631
  return document;
3413
3632
  }
3414
3633
  return {
@@ -3416,73 +3635,125 @@ class FlowSurfacesService {
3416
3635
  navigation: (0, import_service_utils.buildDefinedPayload)({
3417
3636
  ...document.navigation,
3418
3637
  layoutUid,
3638
+ portalUid,
3419
3639
  group: void 0
3420
3640
  })
3421
3641
  };
3422
3642
  }
3423
- async assertApplyBlueprintCreateNavigationLayout(document, transaction) {
3424
- var _a, _b, _c;
3643
+ async assertApplyBlueprintCreateNavigationTarget(document, options = {}) {
3644
+ var _a, _b, _c, _d, _e, _f;
3425
3645
  if (document.mode !== "create") {
3426
3646
  return;
3427
3647
  }
3428
3648
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid((_a = document.navigation) == null ? void 0 : _a.layoutUid);
3429
- if (!layoutUid) {
3649
+ const portalUid = this.navigationTargets.normalizePortalUid((_b = document.navigation) == null ? void 0 : _b.portalUid);
3650
+ this.assertNavigationTargetIsExclusive("applyBlueprint", layoutUid, portalUid, {
3651
+ layout: "values.navigation.layoutUid",
3652
+ portal: "values.navigation.portalUid"
3653
+ });
3654
+ if (!layoutUid && !portalUid) {
3655
+ return;
3656
+ }
3657
+ if (portalUid) {
3658
+ const portal = await this.navigationTargets.resolvePortal(portalUid, {
3659
+ actionName: "applyBlueprint",
3660
+ path: "values.navigation.portalUid",
3661
+ currentRoles: options.currentRoles,
3662
+ transaction: options.transaction
3663
+ });
3664
+ if (portal.layoutType === MOBILE_UI_LAYOUT_TYPE) {
3665
+ return;
3666
+ }
3667
+ const groupRouteId2 = (_d = (_c = document.navigation) == null ? void 0 : _c.group) == null ? void 0 : _d.routeId;
3668
+ if (import_lodash.default.isNil(groupRouteId2) || groupRouteId2 === "") {
3669
+ return;
3670
+ }
3671
+ const groupRoute2 = await this.assertMenuParentIsGroup(groupRouteId2, options.transaction);
3672
+ await this.navigationTargets.assertRouteBelongsToPortal(
3673
+ "applyBlueprint",
3674
+ this.readRouteField(groupRoute2, "id"),
3675
+ portal.uid,
3676
+ "values.navigation.group.routeId",
3677
+ options.transaction
3678
+ );
3430
3679
  return;
3431
3680
  }
3432
3681
  await this.assertEnabledDesktopRouteUiLayoutUid(
3433
3682
  layoutUid,
3434
3683
  "applyBlueprint",
3435
3684
  "values.navigation.layoutUid",
3436
- transaction
3685
+ options.transaction
3437
3686
  );
3438
- if (await this.isMobileDesktopRouteUiLayoutUid(layoutUid, transaction)) {
3687
+ if (await this.isMobileDesktopRouteUiLayoutUid(layoutUid, options.transaction)) {
3439
3688
  return;
3440
3689
  }
3441
- const groupRouteId = (_c = (_b = document.navigation) == null ? void 0 : _b.group) == null ? void 0 : _c.routeId;
3690
+ const groupRouteId = (_f = (_e = document.navigation) == null ? void 0 : _e.group) == null ? void 0 : _f.routeId;
3442
3691
  if (import_lodash.default.isNil(groupRouteId) || groupRouteId === "") {
3443
3692
  return;
3444
3693
  }
3445
- const groupRoute = await this.assertMenuParentIsGroup(groupRouteId, transaction);
3694
+ const groupRoute = await this.assertMenuParentIsGroup(groupRouteId, options.transaction);
3446
3695
  await this.assertDesktopRouteBelongsToUiLayout(
3447
3696
  "applyBlueprint",
3448
3697
  groupRoute,
3449
3698
  layoutUid,
3450
3699
  "values.navigation.group.routeId",
3451
- transaction
3700
+ options.transaction
3452
3701
  );
3453
3702
  }
3454
3703
  async resolveApplyBlueprintCreatePageIdentity(document, transaction) {
3455
- var _a, _b, _c, _d, _e, _f;
3704
+ var _a, _b, _c, _d, _e, _f, _g;
3456
3705
  if (document.mode !== "create") {
3457
3706
  return document;
3458
3707
  }
3459
3708
  const groupRouteId = (_b = (_a = document.navigation) == null ? void 0 : _a.group) == null ? void 0 : _b.routeId;
3460
3709
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid((_c = document.navigation) == null ? void 0 : _c.layoutUid);
3461
- const pageTitle = String(((_d = document.page) == null ? void 0 : _d.title) || ((_f = (_e = document.navigation) == null ? void 0 : _e.item) == null ? void 0 : _f.title) || "").trim();
3710
+ let portalUid = this.navigationTargets.normalizePortalUid((_d = document.navigation) == null ? void 0 : _d.portalUid);
3711
+ const pageTitle = String(((_e = document.page) == null ? void 0 : _e.title) || ((_g = (_f = document.navigation) == null ? void 0 : _f.item) == null ? void 0 : _g.title) || "").trim();
3462
3712
  const hasGroupRouteId = !import_lodash.default.isNil(groupRouteId) && groupRouteId !== "";
3463
- if (!pageTitle || !hasGroupRouteId && !layoutUid) {
3713
+ if (!pageTitle || !hasGroupRouteId && !layoutUid && !portalUid) {
3464
3714
  return document;
3465
3715
  }
3466
3716
  const groupRoute = hasGroupRouteId ? await this.findMenuRouteById(groupRouteId, transaction) : null;
3467
- const layoutUidFilter = layoutUid || (groupRoute ? await this.resolveInheritedDesktopRouteUiLayoutUids(groupRoute, transaction) : void 0);
3717
+ if (!portalUid && groupRoute) {
3718
+ const groupPortalUids = await this.navigationTargets.readRoutePortalUids(
3719
+ this.readRouteField(groupRoute, "id"),
3720
+ transaction
3721
+ );
3722
+ if (groupPortalUids.length > 1) {
3723
+ (0, import_errors.throwBadRequest)(
3724
+ `flowSurfaces applyBlueprint navigation.group.routeId '${groupRouteId}' belongs to multiple portals; pass navigation.portalUid explicitly`,
3725
+ {
3726
+ ruleId: "navigation-route-scope-ambiguous",
3727
+ path: "values.navigation.group.routeId",
3728
+ details: {
3729
+ routeId: groupRouteId,
3730
+ portalUids: groupPortalUids
3731
+ }
3732
+ }
3733
+ );
3734
+ }
3735
+ portalUid = groupPortalUids[0];
3736
+ }
3737
+ const layoutUidFilter = portalUid ? void 0 : layoutUid || (groupRoute ? await this.resolveInheritedDesktopRouteUiLayoutUids(groupRoute, transaction) : void 0);
3468
3738
  const matchedPages = await this.findFlowPageRoutesByParentIdAndTitle(
3469
3739
  hasGroupRouteId ? groupRouteId : null,
3470
3740
  pageTitle,
3471
3741
  transaction,
3472
- layoutUidFilter
3742
+ layoutUidFilter,
3743
+ portalUid
3473
3744
  );
3474
3745
  if (!matchedPages.length) {
3475
3746
  return document;
3476
3747
  }
3477
3748
  if (matchedPages.length > 1) {
3478
- const locationLabel = hasGroupRouteId ? `under navigation.group.routeId '${groupRouteId}'` : `at the root of ui layout '${layoutUid}'`;
3749
+ const locationLabel = hasGroupRouteId ? `under navigation.group.routeId '${groupRouteId}'` : portalUid ? `at the root of portal '${portalUid}'` : `at the root of ui layout '${layoutUid}'`;
3479
3750
  (0, import_errors.throwBadRequest)(
3480
3751
  `flowSurfaces applyBlueprint ${locationLabel} already has ${matchedPages.length} flow pages titled '${pageTitle}'; pass target.pageSchemaUid explicitly before applyBlueprint`
3481
3752
  );
3482
3753
  }
3483
3754
  const pageSchemaUid = String(this.readRouteField(matchedPages[0], "schemaUid") || "").trim();
3484
3755
  if (!pageSchemaUid) {
3485
- const locationLabel = hasGroupRouteId ? `under navigation.group.routeId '${groupRouteId}'` : `at the root of ui layout '${layoutUid}'`;
3756
+ const locationLabel = hasGroupRouteId ? `under navigation.group.routeId '${groupRouteId}'` : portalUid ? `at the root of portal '${portalUid}'` : `at the root of ui layout '${layoutUid}'`;
3486
3757
  (0, import_errors.throwBadRequest)(
3487
3758
  `flowSurfaces applyBlueprint existing flow page '${pageTitle}' ${locationLabel} is missing schemaUid; pass target.pageSchemaUid explicitly before applyBlueprint`
3488
3759
  );
@@ -3496,21 +3767,18 @@ class FlowSurfacesService {
3496
3767
  navigation: void 0
3497
3768
  };
3498
3769
  }
3499
- async prepareApplyBlueprintRequest(values, transaction, createdKanbanSortFields) {
3770
+ async prepareApplyBlueprintRequest(values, options = {}, createdKanbanSortFields) {
3500
3771
  var _a;
3501
3772
  const initialDocument = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3502
- await this.assertApplyBlueprintCreateNavigationLayout(initialDocument, transaction);
3503
- const mobileNormalizedDocument = await this.normalizeApplyBlueprintCreateMobileNavigation(
3504
- initialDocument,
3505
- transaction
3506
- );
3773
+ await this.assertApplyBlueprintCreateNavigationTarget(initialDocument, options);
3774
+ const mobileNormalizedDocument = await this.normalizeApplyBlueprintCreateMobileNavigation(initialDocument, options);
3507
3775
  const groupResolvedDocument = await this.resolveApplyBlueprintCreateNavigationGroup(
3508
3776
  mobileNormalizedDocument,
3509
- transaction
3777
+ options.transaction
3510
3778
  );
3511
- const document = await this.resolveApplyBlueprintCreatePageIdentity(groupResolvedDocument, transaction);
3512
- await this.prepareApplyBlueprintKanbanBlocks(document, transaction, createdKanbanSortFields);
3513
- const replaceTarget = document.mode === "replace" && document.target ? await this.resolveApplyBlueprintReplaceTargetInfo(document.target.pageSchemaUid, transaction) : void 0;
3779
+ const document = await this.resolveApplyBlueprintCreatePageIdentity(groupResolvedDocument, options.transaction);
3780
+ await this.prepareApplyBlueprintKanbanBlocks(document, options.transaction, createdKanbanSortFields);
3781
+ const replaceTarget = document.mode === "replace" && document.target ? await this.resolveApplyBlueprintReplaceTargetInfo(document.target.pageSchemaUid, options.transaction) : void 0;
3514
3782
  if (document.mode === "replace" && document.tabs.length > 1 && typeof ((_a = document.page) == null ? void 0 : _a.enableTabs) === "undefined" && (replaceTarget == null ? void 0 : replaceTarget.pageEnableTabs) === false) {
3515
3783
  (0, import_errors.throwBadRequest)(
3516
3784
  `flowSurfaces applyBlueprint replace mode requires page.enableTabs=true when tabs.length > 1 and the current page has enableTabs=false`
@@ -4160,12 +4428,36 @@ class FlowSurfacesService {
4160
4428
  }
4161
4429
  }
4162
4430
  async assertApplyBlueprintAuthoringPayload(values, options = {}) {
4431
+ var _a, _b;
4163
4432
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
4433
+ const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid((_a = values == null ? void 0 : values.navigation) == null ? void 0 : _a.layoutUid);
4434
+ const portalUid = this.navigationTargets.normalizePortalUid((_b = values == null ? void 0 : values.navigation) == null ? void 0 : _b.portalUid);
4435
+ this.assertNavigationTargetIsExclusive("applyBlueprint", layoutUid, portalUid, {
4436
+ layout: "values.navigation.layoutUid",
4437
+ portal: "values.navigation.portalUid"
4438
+ });
4439
+ const resolvedPortal = portalUid ? await this.navigationTargets.resolvePortal(portalUid, {
4440
+ actionName: "applyBlueprint",
4441
+ path: "values.navigation.portalUid",
4442
+ currentRoles: options.currentRoles,
4443
+ transaction: options.transaction
4444
+ }) : void 0;
4164
4445
  await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("applyBlueprint", values, {
4165
4446
  transaction: options.transaction,
4166
4447
  enabledPackages,
4167
- findMenuGroupRoutesByTitle: (title, transaction, layoutUid) => this.findMenuGroupRoutesByTitle(title, transaction, layoutUid),
4168
- getUiLayoutTypeByUid: (layoutUid, transaction) => this.getDesktopRouteUiLayoutTypeByUid(layoutUid, transaction),
4448
+ findMenuGroupRoutesByTitle: (title, transaction, targetLayoutUid, targetPortalUid) => this.findMenuGroupRoutesByTitle(title, transaction, targetLayoutUid, targetPortalUid),
4449
+ getUiLayoutTypeByUid: (layoutUid2, transaction) => this.getDesktopRouteUiLayoutTypeByUid(layoutUid2, transaction),
4450
+ getPortalLayoutTypeByUid: async (targetPortalUid, transaction) => {
4451
+ if ((resolvedPortal == null ? void 0 : resolvedPortal.uid) === targetPortalUid) {
4452
+ return resolvedPortal.layoutType;
4453
+ }
4454
+ return (await this.navigationTargets.resolvePortal(targetPortalUid, {
4455
+ actionName: "applyBlueprint",
4456
+ path: "values.navigation.portalUid",
4457
+ currentRoles: options.currentRoles,
4458
+ transaction
4459
+ })).layoutType;
4460
+ },
4169
4461
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
4170
4462
  });
4171
4463
  }
@@ -4173,7 +4465,7 @@ class FlowSurfacesService {
4173
4465
  if (options.skipAuthoringValidation !== true) {
4174
4466
  await this.assertApplyBlueprintAuthoringPayload(values, options);
4175
4467
  }
4176
- const prepared = await this.prepareApplyBlueprintRequest(values, options.transaction, createdKanbanSortFields);
4468
+ const prepared = await this.prepareApplyBlueprintRequest(values, options, createdKanbanSortFields);
4177
4469
  const popupTemplateAliasSession = this.createPopupTemplateAliasSession();
4178
4470
  const popupTemplateTreeCache = /* @__PURE__ */ new Map();
4179
4471
  this.validateApplyBlueprintPopupTemplateAliases(prepared.document, popupTemplateAliasSession);
@@ -6188,6 +6480,9 @@ class FlowSurfacesService {
6188
6480
  }
6189
6481
  (0, import_errors.throwBadRequest)(`flowSurfaces configure does not support configureOptions on '${(current == null ? void 0 : current.use) || resolved.uid}'`);
6190
6482
  }
6483
+ async listNavigationTargets(_values = {}, options = {}) {
6484
+ return this.navigationTargets.listNavigationTargets(options.currentRoles, options.transaction);
6485
+ }
6191
6486
  async createMenu(values, options = {}) {
6192
6487
  const publicValues = this.sanitizePublicCreateMenuValues(values || {});
6193
6488
  const type = publicValues.type === "group" ? "group" : "item";
@@ -6195,9 +6490,9 @@ class FlowSurfacesService {
6195
6490
  (0, import_errors.throwBadRequest)("flowSurfaces createMenu requires title");
6196
6491
  }
6197
6492
  if (type === "group") {
6198
- return this.createFlowMenuGroup(publicValues, options.transaction);
6493
+ return this.createFlowMenuGroup(publicValues, options);
6199
6494
  }
6200
- return this.createFlowMenuItem(publicValues, options.transaction);
6495
+ return this.createFlowMenuItem(publicValues, options);
6201
6496
  }
6202
6497
  async updateMenu(values, options = {}) {
6203
6498
  const menuRouteId = values.menuRouteId;
@@ -6207,6 +6502,53 @@ class FlowSurfacesService {
6207
6502
  const route = await this.assertMenuRouteUpdatable(menuRouteId, options.transaction);
6208
6503
  const nextParentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, options.transaction);
6209
6504
  await this.assertMenuParentNotSelfOrDescendant(route, values.parentMenuRouteId, options.transaction);
6505
+ const portalUid = this.navigationTargets.normalizePortalUid(values.portalUid);
6506
+ this.assertNavigationTargetIsExclusive("updateMenu", values.layoutUid, portalUid, {
6507
+ layout: "values.layoutUid",
6508
+ portal: "values.portalUid"
6509
+ });
6510
+ const routeScope = await this.readDesktopRouteScope(route, options.transaction);
6511
+ if (portalUid) {
6512
+ const portal = await this.navigationTargets.resolvePortal(portalUid, {
6513
+ actionName: "updateMenu",
6514
+ path: "values.portalUid",
6515
+ currentRoles: options.currentRoles,
6516
+ transaction: options.transaction
6517
+ });
6518
+ await this.navigationTargets.assertRouteBelongsToPortal(
6519
+ "updateMenu",
6520
+ this.readRouteField(route, "id"),
6521
+ portal.uid,
6522
+ "values.menuRouteId",
6523
+ options.transaction
6524
+ );
6525
+ if (nextParentRoute) {
6526
+ await this.navigationTargets.assertRouteBelongsToPortal(
6527
+ "updateMenu",
6528
+ this.readRouteField(nextParentRoute, "id"),
6529
+ portal.uid,
6530
+ "values.parentMenuRouteId",
6531
+ options.transaction
6532
+ );
6533
+ }
6534
+ }
6535
+ if (Object.prototype.hasOwnProperty.call(values, "parentMenuRouteId") && nextParentRoute) {
6536
+ const parentScope = await this.readDesktopRouteScope(nextParentRoute, options.transaction);
6537
+ const routePortalUids = [...routeScope.portalUids].sort();
6538
+ const parentPortalUids = [...parentScope.portalUids].sort();
6539
+ if (!import_lodash.default.isEqual(routePortalUids, parentPortalUids) && (routePortalUids.length || parentPortalUids.length)) {
6540
+ (0, import_errors.throwBadRequest)(`flowSurfaces updateMenu cannot move a menu route across portal scopes`, {
6541
+ ruleId: "navigation-route-portal-mismatch",
6542
+ path: "values.parentMenuRouteId",
6543
+ details: {
6544
+ routeId: this.readRouteField(route, "id"),
6545
+ routePortalUids,
6546
+ parentRouteId: this.readRouteField(nextParentRoute, "id"),
6547
+ parentPortalUids
6548
+ }
6549
+ });
6550
+ }
6551
+ }
6210
6552
  const nextOptions = { ...this.readRouteOptions(route) };
6211
6553
  const updateValues = {};
6212
6554
  if (Object.prototype.hasOwnProperty.call(values, "title")) {
@@ -6237,25 +6579,29 @@ class FlowSurfacesService {
6237
6579
  }
6238
6580
  const updated = await this.findMenuRouteById(menuRouteId, options.transaction);
6239
6581
  if (updated && Object.prototype.hasOwnProperty.call(values, "parentMenuRouteId")) {
6240
- await this.attachDesktopRouteUiLayoutsToRouteAndChildren(
6241
- this.readRouteField(updated, "id"),
6242
- await this.resolveInheritedDesktopRouteUiLayoutUids(nextParentRoute, options.transaction),
6243
- options.transaction
6244
- );
6582
+ if (routeScope.portalUids.length) {
6583
+ await this.attachDesktopRouteScopeToRouteAndChildren(
6584
+ this.readRouteField(updated, "id"),
6585
+ routeScope,
6586
+ options.transaction
6587
+ );
6588
+ } else {
6589
+ await this.attachDesktopRouteUiLayoutsToRouteAndChildren(
6590
+ this.readRouteField(updated, "id"),
6591
+ await this.resolveInheritedDesktopRouteUiLayoutUids(nextParentRoute, options.transaction),
6592
+ options.transaction
6593
+ );
6594
+ }
6245
6595
  }
6246
6596
  return this.buildMenuResult(updated);
6247
6597
  }
6248
- async initializeFlowPageForRoute(route, values, transaction) {
6598
+ async initializeFlowPageForRoute(route, values, options = {}) {
6599
+ const transaction = options.transaction;
6249
6600
  const routeId = this.readRouteField(route, "id");
6250
6601
  const pageSchemaUid = this.readRouteField(route, "schemaUid");
6251
6602
  const structure = await this.loadRouteBackedPageStructure(route, transaction);
6252
6603
  let tabRoute = structure.tabRoutes[0];
6253
- const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(values.layoutUid);
6254
- if (layoutUid) {
6255
- await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, "createPage", "values.layoutUid", transaction);
6256
- await this.assertDesktopRouteBelongsToUiLayout("createPage", route, layoutUid, "values.menuRouteId", transaction);
6257
- }
6258
- const routeUiLayoutUids = await this.ensureDesktopRouteUiLayouts(route, transaction);
6604
+ const routeScope = await this.resolveExistingDesktopRouteScope(values, route, "createPage", options);
6259
6605
  const routeOptions = this.readRouteOptions(route);
6260
6606
  if (!routeOptions[FLOW_SURFACE_MENU_BINDABLE_OPTION_KEY]) {
6261
6607
  (0, import_errors.throwBadRequest)(`flowSurfaces createPage only accepts a bindable menu route created by createMenu(type="item")`);
@@ -6294,7 +6640,11 @@ class FlowSurfacesService {
6294
6640
  },
6295
6641
  transaction
6296
6642
  });
6297
- await this.setDesktopRouteUiLayouts(this.readRouteField(tabRoute, "id"), routeUiLayoutUids, transaction);
6643
+ await this.attachDesktopRouteScopeToRouteAndChildren(
6644
+ this.readRouteField(tabRoute, "id"),
6645
+ routeScope,
6646
+ transaction
6647
+ );
6298
6648
  } else {
6299
6649
  await this.db.getRepository("desktopRoutes").update({
6300
6650
  filterByTk: String(this.readRouteField(tabRoute, "id")),
@@ -6319,7 +6669,11 @@ class FlowSurfacesService {
6319
6669
  });
6320
6670
  }
6321
6671
  if (tabRoute) {
6322
- await this.setDesktopRouteUiLayouts(this.readRouteField(tabRoute, "id"), routeUiLayoutUids, transaction);
6672
+ await this.attachDesktopRouteScopeToRouteAndChildren(
6673
+ this.readRouteField(tabRoute, "id"),
6674
+ routeScope,
6675
+ transaction
6676
+ );
6323
6677
  }
6324
6678
  await this.ensureFlowRoutePageSchemaShell(pageSchemaUid, transaction);
6325
6679
  await this.db.getRepository("desktopRoutes").update({
@@ -6371,11 +6725,11 @@ class FlowSurfacesService {
6371
6725
  let result;
6372
6726
  if (!import_lodash.default.isNil(values.menuRouteId) && values.menuRouteId !== "") {
6373
6727
  const route = await this.assertMenuRouteBindable(values.menuRouteId, options.transaction);
6374
- result = await this.initializeFlowPageForRoute(route, values, options.transaction);
6728
+ result = await this.initializeFlowPageForRoute(route, values, options);
6375
6729
  } else {
6376
- const createdMenu = await this.createFlowMenuItem(values, options.transaction);
6730
+ const createdMenu = await this.createFlowMenuItem(values, options);
6377
6731
  const route = await this.assertMenuRouteBindable(createdMenu.routeId, options.transaction);
6378
- result = await this.initializeFlowPageForRoute(route, values, options.transaction);
6732
+ result = await this.initializeFlowPageForRoute(route, values, options);
6379
6733
  }
6380
6734
  await this.persistCreatedKeysForAction("createPage", values, result, options.transaction);
6381
6735
  return result;
@@ -6460,11 +6814,11 @@ class FlowSurfacesService {
6460
6814
  },
6461
6815
  transaction: options.transaction
6462
6816
  });
6463
- await this.setDesktopRouteUiLayouts(
6464
- route.get("id"),
6465
- await this.ensureDesktopRouteUiLayouts(pageRoute, options.transaction),
6466
- options.transaction
6467
- );
6817
+ const pageRouteScope = await this.readDesktopRouteScope(pageRoute, options.transaction);
6818
+ if (!pageRouteScope.portalUids.length && !pageRouteScope.layoutUids.length) {
6819
+ pageRouteScope.layoutUids = await this.ensureDesktopRouteUiLayouts(pageRoute, options.transaction);
6820
+ }
6821
+ await this.attachDesktopRouteScopeToRouteAndChildren(route.get("id"), pageRouteScope, options.transaction);
6468
6822
  const gridUid = await this.ensureGridChild(tabSchemaUid, "BlockGridModel", options.transaction);
6469
6823
  const result = {
6470
6824
  pageUid: pageTarget.uid,