@nocobase/plugin-flow-engine 2.3.0-beta.5 → 2.4.0-alpha.1

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.
@@ -8,16 +8,16 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
- "@nocobase/client": "2.3.0-beta.5",
11
+ "@nocobase/client": "2.4.0-alpha.1",
12
12
  "lodash": "4.18.1",
13
- "@nocobase/database": "2.3.0-beta.5",
14
- "@nocobase/data-source-manager": "2.3.0-beta.5",
15
- "@nocobase/resourcer": "2.3.0-beta.5",
16
- "@nocobase/server": "2.3.0-beta.5",
17
- "@nocobase/utils": "2.3.0-beta.5",
18
- "@nocobase/cache": "2.3.0-beta.5",
19
- "@nocobase/actions": "2.3.0-beta.5",
13
+ "@nocobase/database": "2.4.0-alpha.1",
14
+ "@nocobase/data-source-manager": "2.4.0-alpha.1",
15
+ "@nocobase/resourcer": "2.4.0-alpha.1",
16
+ "@nocobase/server": "2.4.0-alpha.1",
17
+ "@nocobase/utils": "2.4.0-alpha.1",
18
+ "@nocobase/cache": "2.4.0-alpha.1",
19
+ "@nocobase/actions": "2.4.0-alpha.1",
20
20
  "sequelize": "6.35.2",
21
21
  "@formily/json-schema": "2.3.7",
22
- "@nocobase/ai": "2.3.0-beta.5"
22
+ "@nocobase/ai": "2.4.0-alpha.1"
23
23
  };
@@ -1 +1 @@
1
- {"name":"@ant-design/icons-svg","version":"4.3.1","sideEffects":false,"description":"Abstract nodes for ant design icons.","contributors":["HeskeyBaozi <hezhiyu233@foxmail.com>"],"homepage":"https://github.com/ant-design/ant-design-icons/","license":"MIT","main":"lib/index.js","module":"es/index.js","files":["lib","es","inline-svg","inline-namespaced-svg"],"repository":{"type":"git","url":"git+https://github.com/ant-design/ant-design-icons.git"},"scripts":{"g":"npm run generate","generate":"cross-env NODE_ENV=production gulp --require ts-node/register/transpile-only","type-check":"tsc --noEmit","build:es":"tsc --project tsconfig.build.json --module esnext --outDir es","build:lib":"tsc --project tsconfig.build.json --module commonjs --outDir lib","build":"cross-env NODE_ENV=production && npm run build:es && npm run build:lib","test":"cross-env NODE_ENV=test && npm run test:unit","test:unit":"jest","prepublishOnly":"npm run test && npm run g && npm run build && npm run type-check && npm run digest","digest":"node scripts/digest.js"},"bugs":{"url":"https://github.com/ant-design/ant-design-icons/issues"},"devDependencies":{"@babel/core":"^7.8.6","@babel/preset-env":"^7.8.6","@babel/preset-typescript":"^7.8.3","@rgrove/parse-xml":"^2.0.2","@types/gulp":"^4.0.6","@types/gulp-concat":"^0.0.32","@types/gulp-rename":"^0.0.33","@types/jest":"^25.1.3","@types/lodash.camelcase":"^4.3.6","@types/lodash.template":"^4.4.6","@types/lodash.upperfirst":"^4.3.6","@types/node":"^13.9.3","@types/ramda":"^0.26.43","@types/rgrove__parse-xml":"^1.1.0","@types/svgo":"^1.3.1","@types/through2":"^2.0.34","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","babel-jest":"^25.1.0","cross-env":"^7.0.0","del":"^5.1.0","eslint":"^7.16.0","globby":"^11.0.0","gulp":"^4.0.2","gulp-clone":"^2.0.1","gulp-concat":"^2.6.1","gulp-header":"^2.0.9","gulp-rename":"^2.0.0","jest":"^25.1.0","lodash.camelcase":"^4.3.0","lodash.template":"^4.5.0","lodash.upperfirst":"^4.3.1","ramda":"^0.27.0","svgo":"^1.3.2","through2":"^3.0.1","ts-node":"^8.6.2","typescript":"^4.0.0","vinyl":"^2.2.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_lastModified":"2026-08-28T08:17:50.549Z"}
1
+ {"name":"@ant-design/icons-svg","version":"4.3.1","sideEffects":false,"description":"Abstract nodes for ant design icons.","contributors":["HeskeyBaozi <hezhiyu233@foxmail.com>"],"homepage":"https://github.com/ant-design/ant-design-icons/","license":"MIT","main":"lib/index.js","module":"es/index.js","files":["lib","es","inline-svg","inline-namespaced-svg"],"repository":{"type":"git","url":"git+https://github.com/ant-design/ant-design-icons.git"},"scripts":{"g":"npm run generate","generate":"cross-env NODE_ENV=production gulp --require ts-node/register/transpile-only","type-check":"tsc --noEmit","build:es":"tsc --project tsconfig.build.json --module esnext --outDir es","build:lib":"tsc --project tsconfig.build.json --module commonjs --outDir lib","build":"cross-env NODE_ENV=production && npm run build:es && npm run build:lib","test":"cross-env NODE_ENV=test && npm run test:unit","test:unit":"jest","prepublishOnly":"npm run test && npm run g && npm run build && npm run type-check && npm run digest","digest":"node scripts/digest.js"},"bugs":{"url":"https://github.com/ant-design/ant-design-icons/issues"},"devDependencies":{"@babel/core":"^7.8.6","@babel/preset-env":"^7.8.6","@babel/preset-typescript":"^7.8.3","@rgrove/parse-xml":"^2.0.2","@types/gulp":"^4.0.6","@types/gulp-concat":"^0.0.32","@types/gulp-rename":"^0.0.33","@types/jest":"^25.1.3","@types/lodash.camelcase":"^4.3.6","@types/lodash.template":"^4.4.6","@types/lodash.upperfirst":"^4.3.6","@types/node":"^13.9.3","@types/ramda":"^0.26.43","@types/rgrove__parse-xml":"^1.1.0","@types/svgo":"^1.3.1","@types/through2":"^2.0.34","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","babel-jest":"^25.1.0","cross-env":"^7.0.0","del":"^5.1.0","eslint":"^7.16.0","globby":"^11.0.0","gulp":"^4.0.2","gulp-clone":"^2.0.1","gulp-concat":"^2.6.1","gulp-header":"^2.0.9","gulp-rename":"^2.0.0","jest":"^25.1.0","lodash.camelcase":"^4.3.0","lodash.template":"^4.5.0","lodash.upperfirst":"^4.3.1","ramda":"^0.27.0","svgo":"^1.3.2","through2":"^3.0.1","ts-node":"^8.6.2","typescript":"^4.0.0","vinyl":"^2.2.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_lastModified":"2026-09-02T04:18:23.841Z"}
@@ -1 +1 @@
1
- {"name":"acorn","description":"ECMAScript parser","homepage":"https://github.com/acornjs/acorn","main":"dist/acorn.js","types":"dist/acorn.d.ts","module":"dist/acorn.mjs","exports":{".":[{"import":"./dist/acorn.mjs","require":"./dist/acorn.js","default":"./dist/acorn.js"},"./dist/acorn.js"],"./package.json":"./package.json"},"version":"8.11.2","engines":{"node":">=0.4.0"},"maintainers":[{"name":"Marijn Haverbeke","email":"marijnh@gmail.com","web":"https://marijnhaverbeke.nl"},{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"https://rreverser.com/"},{"name":"Adrian Heine","web":"http://adrianheine.de"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn.git"},"license":"MIT","scripts":{"prepare":"cd ..; npm run build:main"},"bin":{"acorn":"./bin/acorn"},"_lastModified":"2026-08-28T08:17:51.677Z"}
1
+ {"name":"acorn","description":"ECMAScript parser","homepage":"https://github.com/acornjs/acorn","main":"dist/acorn.js","types":"dist/acorn.d.ts","module":"dist/acorn.mjs","exports":{".":[{"import":"./dist/acorn.mjs","require":"./dist/acorn.js","default":"./dist/acorn.js"},"./dist/acorn.js"],"./package.json":"./package.json"},"version":"8.11.2","engines":{"node":">=0.4.0"},"maintainers":[{"name":"Marijn Haverbeke","email":"marijnh@gmail.com","web":"https://marijnhaverbeke.nl"},{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"https://rreverser.com/"},{"name":"Adrian Heine","web":"http://adrianheine.de"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn.git"},"license":"MIT","scripts":{"prepare":"cd ..; npm run build:main"},"bin":{"acorn":"./bin/acorn"},"_lastModified":"2026-09-02T04:18:24.863Z"}
@@ -1 +1 @@
1
- {"name":"acorn-jsx","description":"Modern, fast React.js JSX parser","homepage":"https://github.com/acornjs/acorn-jsx","version":"5.3.2","maintainers":[{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"http://rreverser.com/"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn-jsx"},"license":"MIT","scripts":{"test":"node test/run.js"},"peerDependencies":{"acorn":"^6.0.0 || ^7.0.0 || ^8.0.0"},"devDependencies":{"acorn":"^8.0.1"},"_lastModified":"2026-08-28T08:17:52.141Z"}
1
+ {"name":"acorn-jsx","description":"Modern, fast React.js JSX parser","homepage":"https://github.com/acornjs/acorn-jsx","version":"5.3.2","maintainers":[{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"http://rreverser.com/"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn-jsx"},"license":"MIT","scripts":{"test":"node test/run.js"},"peerDependencies":{"acorn":"^6.0.0 || ^7.0.0 || ^8.0.0"},"devDependencies":{"acorn":"^8.0.1"},"_lastModified":"2026-09-02T04:18:25.302Z"}
@@ -1 +1 @@
1
- {"name":"acorn-walk","description":"ECMAScript (ESTree) AST walker","homepage":"https://github.com/acornjs/acorn","main":"dist/walk.js","types":"dist/walk.d.ts","module":"dist/walk.mjs","exports":{".":[{"import":"./dist/walk.mjs","require":"./dist/walk.js","default":"./dist/walk.js"},"./dist/walk.js"],"./package.json":"./package.json"},"version":"8.3.4","engines":{"node":">=0.4.0"},"dependencies":{"acorn":"^8.11.0"},"maintainers":[{"name":"Marijn Haverbeke","email":"marijnh@gmail.com","web":"https://marijnhaverbeke.nl"},{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"https://rreverser.com/"},{"name":"Adrian Heine","web":"http://adrianheine.de"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn.git"},"scripts":{"prepare":"cd ..; npm run build:walk"},"license":"MIT","_lastModified":"2026-08-28T08:17:52.259Z"}
1
+ {"name":"acorn-walk","description":"ECMAScript (ESTree) AST walker","homepage":"https://github.com/acornjs/acorn","main":"dist/walk.js","types":"dist/walk.d.ts","module":"dist/walk.mjs","exports":{".":[{"import":"./dist/walk.mjs","require":"./dist/walk.js","default":"./dist/walk.js"},"./dist/walk.js"],"./package.json":"./package.json"},"version":"8.3.4","engines":{"node":">=0.4.0"},"dependencies":{"acorn":"^8.11.0"},"maintainers":[{"name":"Marijn Haverbeke","email":"marijnh@gmail.com","web":"https://marijnhaverbeke.nl"},{"name":"Ingvar Stepanyan","email":"me@rreverser.com","web":"https://rreverser.com/"},{"name":"Adrian Heine","web":"http://adrianheine.de"}],"repository":{"type":"git","url":"https://github.com/acornjs/acorn.git"},"scripts":{"prepare":"cd ..; npm run build:walk"},"license":"MIT","_lastModified":"2026-09-02T04:18:25.415Z"}
@@ -1 +1 @@
1
- {"name":"ses","version":"1.14.0","description":"Hardened JavaScript for Fearless Cooperation","keywords":["lockdown","harden","Compartment","assert","security","confinement","isolation","object capabilities","ocaps","secure execution","third-party code","prototype pollution","supply-chain attack","plugin"],"author":"Agoric","license":"Apache-2.0","homepage":"https://github.com/Agoric/SES-shim/tree/master/packages/ses#readme","repository":{"type":"git","url":"git+https://github.com/endojs/endo.git","directory":"packages/ses"},"bugs":{"url":"https://github.com/endojs/endo/issues"},"type":"module","main":"./dist/ses.cjs","module":"./index.js","unpkg":"./dist/ses.umd.js","types":"./types.d.ts","exports":{".":{"import":{"types":"./types.d.ts","xs":"./src-xs/index.js","default":"./index.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/ses.cjs"}},"./lockdown":{"import":{"types":"./types.d.ts","default":"./index.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/ses.cjs"}},"./hermes":{"require":{"types":"./dist/types.d.cts","default":"./dist/ses-hermes.cjs"}},"./tools.js":"./tools.js","./assert-shim.js":"./assert-shim.js","./lockdown-shim.js":{"xs":"./src-xs/lockdown-shim.js","default":"./lockdown-shim.js"},"./compartment-shim.js":{"xs":"./src-xs/compartment-shim.js","default":"./compartment-shim.js"},"./console-shim.js":"./console-shim.js","./package.json":"./package.json"},"scripts":{"build:vanilla":"node scripts/bundle.js","build:hermes":"node scripts/bundle.js hermes","build":"yarn build:vanilla && yarn build:hermes","clean":"rm -rf dist","cover":"c8 ava","demo":"python3 -m http.server","lint":"yarn lint:types && yarn lint:eslint","lint-fix":"eslint --fix .","lint:eslint":"eslint .","lint:types":"tsc","prepare":"npm run clean && npm run build","qt":"ava","test":"tsd && ava","test:hermes":"./scripts/hermes-test.sh","test:xs":"xst dist/ses.umd.js test/_lockdown-safe.js && node scripts/generate-test-xs.js && xst tmp/test-xs.js && rm -rf tmp","postpack":"git clean -fX \"*.d.ts*\" \"*.d.cts*\" \"*.d.mts*\" \"*.tsbuildinfo\""},"dependencies":{"@endo/cache-map":"^1.1.0","@endo/env-options":"^1.1.11","@endo/immutable-arraybuffer":"^1.1.2"},"devDependencies":{"@babel/generator":"^7.26.3","@babel/parser":"~7.26.2","@babel/traverse":"~7.25.9","@babel/types":"~7.26.0","@endo/compartment-mapper":"^1.6.3","@endo/module-source":"^1.3.3","@endo/test262-runner":"^0.1.48","@types/babel__traverse":"^7.20.5","ava":"^6.1.3","babel-eslint":"^10.1.0","c8":"^7.14.0","core-js":"^3.31.0","eslint":"^8.57.1","eslint-config-airbnb-base":"^15.0.0","eslint-config-prettier":"^9.1.0","eslint-plugin-eslint-comments":"^3.2.0","eslint-plugin-import":"^2.31.0","hermes-engine-cli":"^0.12.0","prettier":"^3.5.3","terser":"^5.16.6","tsd":"^0.31.2","typescript":"~5.8.3"},"files":["./*.d.ts","./*.js","./*.map","LICENSE*","SECURITY*","dist","lib","src","tools"],"publishConfig":{"access":"public"},"eslintConfig":{"extends":["plugin:@endo/ses"]},"ava":{"files":["test/**/*.test.*"],"timeout":"2m"},"typeCoverage":{"atLeast":81.17},"gitHead":"9815aea9541f241389d2135c6097a7442bdffa17","_lastModified":"2026-08-28T08:17:51.228Z"}
1
+ {"name":"ses","version":"1.14.0","description":"Hardened JavaScript for Fearless Cooperation","keywords":["lockdown","harden","Compartment","assert","security","confinement","isolation","object capabilities","ocaps","secure execution","third-party code","prototype pollution","supply-chain attack","plugin"],"author":"Agoric","license":"Apache-2.0","homepage":"https://github.com/Agoric/SES-shim/tree/master/packages/ses#readme","repository":{"type":"git","url":"git+https://github.com/endojs/endo.git","directory":"packages/ses"},"bugs":{"url":"https://github.com/endojs/endo/issues"},"type":"module","main":"./dist/ses.cjs","module":"./index.js","unpkg":"./dist/ses.umd.js","types":"./types.d.ts","exports":{".":{"import":{"types":"./types.d.ts","xs":"./src-xs/index.js","default":"./index.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/ses.cjs"}},"./lockdown":{"import":{"types":"./types.d.ts","default":"./index.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/ses.cjs"}},"./hermes":{"require":{"types":"./dist/types.d.cts","default":"./dist/ses-hermes.cjs"}},"./tools.js":"./tools.js","./assert-shim.js":"./assert-shim.js","./lockdown-shim.js":{"xs":"./src-xs/lockdown-shim.js","default":"./lockdown-shim.js"},"./compartment-shim.js":{"xs":"./src-xs/compartment-shim.js","default":"./compartment-shim.js"},"./console-shim.js":"./console-shim.js","./package.json":"./package.json"},"scripts":{"build:vanilla":"node scripts/bundle.js","build:hermes":"node scripts/bundle.js hermes","build":"yarn build:vanilla && yarn build:hermes","clean":"rm -rf dist","cover":"c8 ava","demo":"python3 -m http.server","lint":"yarn lint:types && yarn lint:eslint","lint-fix":"eslint --fix .","lint:eslint":"eslint .","lint:types":"tsc","prepare":"npm run clean && npm run build","qt":"ava","test":"tsd && ava","test:hermes":"./scripts/hermes-test.sh","test:xs":"xst dist/ses.umd.js test/_lockdown-safe.js && node scripts/generate-test-xs.js && xst tmp/test-xs.js && rm -rf tmp","postpack":"git clean -fX \"*.d.ts*\" \"*.d.cts*\" \"*.d.mts*\" \"*.tsbuildinfo\""},"dependencies":{"@endo/cache-map":"^1.1.0","@endo/env-options":"^1.1.11","@endo/immutable-arraybuffer":"^1.1.2"},"devDependencies":{"@babel/generator":"^7.26.3","@babel/parser":"~7.26.2","@babel/traverse":"~7.25.9","@babel/types":"~7.26.0","@endo/compartment-mapper":"^1.6.3","@endo/module-source":"^1.3.3","@endo/test262-runner":"^0.1.48","@types/babel__traverse":"^7.20.5","ava":"^6.1.3","babel-eslint":"^10.1.0","c8":"^7.14.0","core-js":"^3.31.0","eslint":"^8.57.1","eslint-config-airbnb-base":"^15.0.0","eslint-config-prettier":"^9.1.0","eslint-plugin-eslint-comments":"^3.2.0","eslint-plugin-import":"^2.31.0","hermes-engine-cli":"^0.12.0","prettier":"^3.5.3","terser":"^5.16.6","tsd":"^0.31.2","typescript":"~5.8.3"},"files":["./*.d.ts","./*.js","./*.map","LICENSE*","SECURITY*","dist","lib","src","tools"],"publishConfig":{"access":"public"},"eslintConfig":{"extends":["plugin:@endo/ses"]},"ava":{"files":["test/**/*.test.*"],"timeout":"2m"},"typeCoverage":{"atLeast":81.17},"gitHead":"9815aea9541f241389d2135c6097a7442bdffa17","_lastModified":"2026-09-02T04:18:24.444Z"}
@@ -1 +1 @@
1
- {"name":"zod","version":"4.3.5","type":"module","license":"MIT","author":"Colin McDonnell <zod@colinhacks.com>","description":"TypeScript-first schema declaration and validation library with static type inference","homepage":"https://zod.dev","llms":"https://zod.dev/llms.txt","llmsFull":"https://zod.dev/llms-full.txt","mcpServer":"https://mcp.inkeep.com/zod/mcp","funding":"https://github.com/sponsors/colinhacks","sideEffects":false,"files":["src","**/*.js","**/*.mjs","**/*.cjs","**/*.d.ts","**/*.d.mts","**/*.d.cts","**/package.json"],"keywords":["typescript","schema","validation","type","inference"],"main":"./index.cjs","types":"./index.d.cts","module":"./index.js","zshy":{"exports":{"./package.json":"./package.json",".":"./src/index.ts","./mini":"./src/mini/index.ts","./locales":"./src/locales/index.ts","./v3":"./src/v3/index.ts","./v4":"./src/v4/index.ts","./v4-mini":"./src/v4-mini/index.ts","./v4/mini":"./src/v4/mini/index.ts","./v4/core":"./src/v4/core/index.ts","./v4/locales":"./src/v4/locales/index.ts","./v4/locales/*":"./src/v4/locales/*"},"conditions":{"@zod/source":"src"}},"exports":{"./package.json":"./package.json",".":{"@zod/source":"./src/index.ts","types":"./index.d.cts","import":"./index.js","require":"./index.cjs"},"./mini":{"@zod/source":"./src/mini/index.ts","types":"./mini/index.d.cts","import":"./mini/index.js","require":"./mini/index.cjs"},"./locales":{"@zod/source":"./src/locales/index.ts","types":"./locales/index.d.cts","import":"./locales/index.js","require":"./locales/index.cjs"},"./v3":{"@zod/source":"./src/v3/index.ts","types":"./v3/index.d.cts","import":"./v3/index.js","require":"./v3/index.cjs"},"./v4":{"@zod/source":"./src/v4/index.ts","types":"./v4/index.d.cts","import":"./v4/index.js","require":"./v4/index.cjs"},"./v4-mini":{"@zod/source":"./src/v4-mini/index.ts","types":"./v4-mini/index.d.cts","import":"./v4-mini/index.js","require":"./v4-mini/index.cjs"},"./v4/mini":{"@zod/source":"./src/v4/mini/index.ts","types":"./v4/mini/index.d.cts","import":"./v4/mini/index.js","require":"./v4/mini/index.cjs"},"./v4/core":{"@zod/source":"./src/v4/core/index.ts","types":"./v4/core/index.d.cts","import":"./v4/core/index.js","require":"./v4/core/index.cjs"},"./v4/locales":{"@zod/source":"./src/v4/locales/index.ts","types":"./v4/locales/index.d.cts","import":"./v4/locales/index.js","require":"./v4/locales/index.cjs"},"./v4/locales/*":{"@zod/source":"./src/v4/locales/*","types":"./v4/locales/*","import":"./v4/locales/*","require":"./v4/locales/*"}},"repository":{"type":"git","url":"git+https://github.com/colinhacks/zod.git"},"bugs":{"url":"https://github.com/colinhacks/zod/issues"},"support":{"backing":{"npm-funding":true}},"scripts":{"clean":"git clean -xdf . -e node_modules","build":"zshy --project tsconfig.build.json","postbuild":"tsx ../../scripts/write-stub-package-jsons.ts && pnpm biome check --write .","test:watch":"pnpm vitest","test":"pnpm vitest run","prepublishOnly":"tsx ../../scripts/check-versions.ts"},"_lastModified":"2026-08-28T08:17:53.458Z"}
1
+ {"name":"zod","version":"4.3.5","type":"module","license":"MIT","author":"Colin McDonnell <zod@colinhacks.com>","description":"TypeScript-first schema declaration and validation library with static type inference","homepage":"https://zod.dev","llms":"https://zod.dev/llms.txt","llmsFull":"https://zod.dev/llms-full.txt","mcpServer":"https://mcp.inkeep.com/zod/mcp","funding":"https://github.com/sponsors/colinhacks","sideEffects":false,"files":["src","**/*.js","**/*.mjs","**/*.cjs","**/*.d.ts","**/*.d.mts","**/*.d.cts","**/package.json"],"keywords":["typescript","schema","validation","type","inference"],"main":"./index.cjs","types":"./index.d.cts","module":"./index.js","zshy":{"exports":{"./package.json":"./package.json",".":"./src/index.ts","./mini":"./src/mini/index.ts","./locales":"./src/locales/index.ts","./v3":"./src/v3/index.ts","./v4":"./src/v4/index.ts","./v4-mini":"./src/v4-mini/index.ts","./v4/mini":"./src/v4/mini/index.ts","./v4/core":"./src/v4/core/index.ts","./v4/locales":"./src/v4/locales/index.ts","./v4/locales/*":"./src/v4/locales/*"},"conditions":{"@zod/source":"src"}},"exports":{"./package.json":"./package.json",".":{"@zod/source":"./src/index.ts","types":"./index.d.cts","import":"./index.js","require":"./index.cjs"},"./mini":{"@zod/source":"./src/mini/index.ts","types":"./mini/index.d.cts","import":"./mini/index.js","require":"./mini/index.cjs"},"./locales":{"@zod/source":"./src/locales/index.ts","types":"./locales/index.d.cts","import":"./locales/index.js","require":"./locales/index.cjs"},"./v3":{"@zod/source":"./src/v3/index.ts","types":"./v3/index.d.cts","import":"./v3/index.js","require":"./v3/index.cjs"},"./v4":{"@zod/source":"./src/v4/index.ts","types":"./v4/index.d.cts","import":"./v4/index.js","require":"./v4/index.cjs"},"./v4-mini":{"@zod/source":"./src/v4-mini/index.ts","types":"./v4-mini/index.d.cts","import":"./v4-mini/index.js","require":"./v4-mini/index.cjs"},"./v4/mini":{"@zod/source":"./src/v4/mini/index.ts","types":"./v4/mini/index.d.cts","import":"./v4/mini/index.js","require":"./v4/mini/index.cjs"},"./v4/core":{"@zod/source":"./src/v4/core/index.ts","types":"./v4/core/index.d.cts","import":"./v4/core/index.js","require":"./v4/core/index.cjs"},"./v4/locales":{"@zod/source":"./src/v4/locales/index.ts","types":"./v4/locales/index.d.cts","import":"./v4/locales/index.js","require":"./v4/locales/index.cjs"},"./v4/locales/*":{"@zod/source":"./src/v4/locales/*","types":"./v4/locales/*","import":"./v4/locales/*","require":"./v4/locales/*"}},"repository":{"type":"git","url":"git+https://github.com/colinhacks/zod.git"},"bugs":{"url":"https://github.com/colinhacks/zod/issues"},"support":{"backing":{"npm-funding":true}},"scripts":{"clean":"git clean -xdf . -e node_modules","build":"zshy --project tsconfig.build.json","postbuild":"tsx ../../scripts/write-stub-package-jsons.ts && pnpm biome check --write .","test:watch":"pnpm vitest","test":"pnpm vitest run","prepublishOnly":"tsx ../../scripts/check-versions.ts"},"_lastModified":"2026-09-02T04:18:26.540Z"}
@@ -12,6 +12,7 @@ export declare const DEFAULT_MOBILE_UI_LAYOUT_UID = "mobile-layout-model";
12
12
  export declare const DEFAULT_ADMIN_MULTI_PORTAL_UID = "__default_admin__";
13
13
  export declare const DEFAULT_MOBILE_MULTI_PORTAL_UID = "__default_mobile__";
14
14
  export type FlowSurfaceNavigationRequestRoles = readonly string[] | string;
15
+ type FlowSurfacePortalRouteScopeKind = 'layout' | 'portal';
15
16
  export type FlowSurfaceResolvedMultiPortal = {
16
17
  uid: string;
17
18
  title: string;
@@ -22,6 +23,8 @@ export type FlowSurfaceResolvedMultiPortal = {
22
23
  enabled: true;
23
24
  layoutUid: string;
24
25
  layoutType?: string;
26
+ portalType: 'no-code';
27
+ routeScopeKind: FlowSurfacePortalRouteScopeKind;
25
28
  };
26
29
  export type FlowSurfaceNavigationTarget = {
27
30
  kind: 'layout' | 'portal';
@@ -56,6 +59,7 @@ export declare class FlowSurfaceNavigationTargetsService {
56
59
  hasUiLayoutCapability(): boolean;
57
60
  normalizePortalUid(value: unknown): string;
58
61
  listNavigationTargets(currentRoles?: FlowSurfaceNavigationRequestRoles, transaction?: Transaction): Promise<FlowSurfaceNavigationTargetsResult>;
62
+ resolveDefaultPortal(options: Omit<PortalResolveOptions, 'path'>): Promise<FlowSurfaceResolvedMultiPortal>;
59
63
  resolvePortal(portalUidValue: unknown, options: PortalResolveOptions): Promise<FlowSurfaceResolvedMultiPortal>;
60
64
  readRoutePortalUids(routeId: unknown, transaction?: Transaction): Promise<string[]>;
61
65
  assertRouteBelongsToPortal(actionName: string, routeId: unknown, portalUid: string, path: string, transaction?: Transaction): Promise<void>;
@@ -64,7 +68,10 @@ export declare class FlowSurfaceNavigationTargetsService {
64
68
  transaction?: Transaction;
65
69
  }): Promise<void>;
66
70
  private listLayoutTargets;
71
+ private getEnabledPortalInventory;
67
72
  private listAccessiblePortalTargets;
73
+ private comparePortalPriority;
74
+ private getPortalPriority;
68
75
  private canAccessPortal;
69
76
  private collectRouteTreeIds;
70
77
  private removeDefaultLayoutRoutePermissions;
@@ -38,7 +38,6 @@ const DEFAULT_ADMIN_UI_LAYOUT_UID = "admin-layout-model";
38
38
  const DEFAULT_MOBILE_UI_LAYOUT_UID = "mobile-layout-model";
39
39
  const DEFAULT_ADMIN_MULTI_PORTAL_UID = "__default_admin__";
40
40
  const DEFAULT_MOBILE_MULTI_PORTAL_UID = "__default_mobile__";
41
- const RESERVED_MULTI_PORTAL_UIDS = /* @__PURE__ */ new Set([DEFAULT_ADMIN_MULTI_PORTAL_UID, DEFAULT_MOBILE_MULTI_PORTAL_UID]);
42
41
  function readRecordField(record, field) {
43
42
  if (!record || typeof record !== "object") {
44
43
  return void 0;
@@ -60,6 +59,17 @@ function normalizeRoles(currentRoles) {
60
59
  function normalizeRelationRouteId(routeId) {
61
60
  return routeId === null || typeof routeId === "undefined" ? void 0 : routeId;
62
61
  }
62
+ function isDefaultLayoutMultiPortalUid(uid) {
63
+ return uid === DEFAULT_ADMIN_MULTI_PORTAL_UID || uid === DEFAULT_MOBILE_MULTI_PORTAL_UID;
64
+ }
65
+ function getMultiPortalLayoutType(uiLayoutUid) {
66
+ if (uiLayoutUid === DEFAULT_ADMIN_UI_LAYOUT_UID) {
67
+ return "desktop";
68
+ }
69
+ if (uiLayoutUid === DEFAULT_MOBILE_UI_LAYOUT_UID) {
70
+ return "mobile";
71
+ }
72
+ }
63
73
  class FlowSurfaceNavigationTargetsService {
64
74
  constructor(db) {
65
75
  this.db = db;
@@ -85,9 +95,17 @@ class FlowSurfaceNavigationTargetsService {
85
95
  return portalUid || void 0;
86
96
  }
87
97
  async listNavigationTargets(currentRoles, transaction) {
88
- const layoutTargets = await this.listLayoutTargets(transaction);
98
+ var _a;
99
+ let layoutTargets = await this.listLayoutTargets(transaction);
89
100
  const multiPortal = this.hasMultiPortalCapability();
90
- const portalTargets = multiPortal ? await this.listAccessiblePortalTargets(currentRoles, transaction) : [];
101
+ const portalInventory = multiPortal ? await this.getEnabledPortalInventory(currentRoles, transaction) : { accessible: [] };
102
+ const portalTargets = multiPortal ? await this.listAccessiblePortalTargets(portalInventory.accessible, transaction) : [];
103
+ if (multiPortal) {
104
+ layoutTargets = layoutTargets.map(({ default: _default, ...target }) => target);
105
+ if (portalInventory.accessible.length === 1 && ((_a = portalTargets[0]) == null ? void 0 : _a.uid) === readStringField(portalInventory.accessible[0], "uid")) {
106
+ portalTargets[0] = { ...portalTargets[0], default: true };
107
+ }
108
+ }
91
109
  return {
92
110
  version: "1",
93
111
  capabilities: {
@@ -96,6 +114,60 @@ class FlowSurfaceNavigationTargetsService {
96
114
  targets: [...layoutTargets, ...portalTargets]
97
115
  };
98
116
  }
117
+ async resolveDefaultPortal(options) {
118
+ if (!this.hasMultiPortalCapability()) {
119
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} requires the Multi-portal capability`, {
120
+ ruleId: "navigation-portal-unsupported",
121
+ path: "navigation"
122
+ });
123
+ }
124
+ const inventory = await this.getEnabledPortalInventory(options.currentRoles, options.transaction);
125
+ if (!inventory.enabled.length) {
126
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} requires an enabled portal`, {
127
+ ruleId: "navigation-portal-not-found",
128
+ path: "navigation",
129
+ details: {
130
+ uiBuilderAllowed: false,
131
+ adminLayoutFallbackAllowed: false,
132
+ agentInstruction: "Stop this Flow Surfaces write. Ask the user to create and enable a Portal before retrying; do not fall back to the Admin layout."
133
+ }
134
+ });
135
+ }
136
+ if (!inventory.accessible.length) {
137
+ (0, import_errors.throwForbidden)(
138
+ `flowSurfaces ${options.actionName} current roles cannot access an enabled portal`,
139
+ "FLOW_SURFACE_NAVIGATION_PORTAL_FORBIDDEN",
140
+ {
141
+ ruleId: "navigation-portal-forbidden",
142
+ path: "navigation"
143
+ }
144
+ );
145
+ }
146
+ if (inventory.accessible.length > 1) {
147
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} requires an explicit portal selection`, {
148
+ ruleId: "navigation-portal-selection-required",
149
+ path: "navigation",
150
+ details: {
151
+ candidates: inventory.accessible.map((portal) => {
152
+ const uid = readStringField(portal, "uid");
153
+ return {
154
+ uid,
155
+ portalName: readStringField(portal, "portalName") || null,
156
+ title: readStringField(portal, "title") || uid,
157
+ portalType: readStringField(portal, "portalType") || null
158
+ };
159
+ }),
160
+ uiBuilderAllowed: false,
161
+ adminLayoutFallbackAllowed: false,
162
+ agentInstruction: "Stop this Flow Surfaces write and ask the user to select one candidate before retrying; do not choose by Portal type or fall back to the Admin layout."
163
+ }
164
+ });
165
+ }
166
+ return this.resolvePortal(readStringField(inventory.accessible[0], "uid"), {
167
+ ...options,
168
+ path: "navigation"
169
+ });
170
+ }
99
171
  async resolvePortal(portalUidValue, options) {
100
172
  const portalUid = this.normalizePortalUid(portalUidValue);
101
173
  if (!portalUid) {
@@ -111,19 +183,9 @@ class FlowSurfaceNavigationTargetsService {
111
183
  details: { portalUid }
112
184
  });
113
185
  }
114
- if (RESERVED_MULTI_PORTAL_UIDS.has(portalUid)) {
115
- (0, import_errors.throwBadRequest)(
116
- `flowSurfaces ${options.actionName} ${options.path} cannot use reserved default portal uid '${portalUid}'; use layoutUid semantics instead`,
117
- {
118
- ruleId: "navigation-portal-reserved",
119
- path: options.path,
120
- details: { portalUid }
121
- }
122
- );
123
- }
124
186
  const portal = await this.db.getRepository("multiPortals").findOne({
125
187
  filter: { uid: portalUid },
126
- fields: ["uid", "title", "icon", "routeName", "routePath", "authCheck", "enabled", "uiLayoutUid"],
188
+ fields: ["uid", "title", "icon", "portalType", "portalName", "routePath", "authCheck", "enabled", "uiLayoutUid"],
127
189
  transaction: options.transaction
128
190
  });
129
191
  if (!portal) {
@@ -133,6 +195,18 @@ class FlowSurfaceNavigationTargetsService {
133
195
  details: { portalUid }
134
196
  });
135
197
  }
198
+ const routeScopeKind = isDefaultLayoutMultiPortalUid(portalUid) ? "layout" : "portal";
199
+ if (routeScopeKind === "portal" && !await this.canAccessPortal(portalUid, options.currentRoles, options.transaction)) {
200
+ (0, import_errors.throwForbidden)(
201
+ `flowSurfaces ${options.actionName} current roles cannot access portal '${portalUid}'`,
202
+ "FLOW_SURFACE_NAVIGATION_PORTAL_FORBIDDEN",
203
+ {
204
+ ruleId: "navigation-portal-forbidden",
205
+ path: options.path,
206
+ details: { portalUid }
207
+ }
208
+ );
209
+ }
136
210
  if (readRecordField(portal, "enabled") !== true) {
137
211
  (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} portal '${portalUid}' is disabled`, {
138
212
  ruleId: "navigation-portal-disabled",
@@ -140,60 +214,54 @@ class FlowSurfaceNavigationTargetsService {
140
214
  details: { portalUid }
141
215
  });
142
216
  }
217
+ const portalType = readStringField(portal, "portalType");
218
+ if (portalType !== "no-code") {
219
+ if (portalType === "ai") {
220
+ (0, import_errors.throwBadRequest)(
221
+ `flowSurfaces ${options.actionName} cannot use UI Builder or Flow Surfaces for AI Portal '${portalUid}'; stop and implement the request in the Portal source code`,
222
+ {
223
+ ruleId: "navigation-portal-type-unsupported",
224
+ path: options.path,
225
+ details: {
226
+ portalUid,
227
+ portalType,
228
+ portalName: readStringField(portal, "portalName") || null,
229
+ expectedPortalType: "no-code",
230
+ uiBuilderAllowed: false,
231
+ adminLayoutFallbackAllowed: false,
232
+ implementationPath: "ai-portal-source",
233
+ agentInstruction: "Stop this Flow Surfaces write. Run nb portal info <portalName> -j to resolve localPath, then implement the request in that AI Portal source code. Do not retry with an Admin layout."
234
+ }
235
+ }
236
+ );
237
+ }
238
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} portal '${portalUid}' does not support no-code routes`, {
239
+ ruleId: "navigation-portal-type-unsupported",
240
+ path: options.path,
241
+ details: { portalUid, portalType: portalType || null }
242
+ });
243
+ }
143
244
  const layoutUid = readStringField(portal, "uiLayoutUid");
144
- if (!layoutUid || !this.db.getCollection("uiLayouts")) {
145
- (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} portal '${portalUid}' has no available backing UI layout`, {
245
+ const layoutType = getMultiPortalLayoutType(layoutUid);
246
+ if (!layoutUid || !layoutType) {
247
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${options.actionName} portal '${portalUid}' has an unsupported UI layout uid`, {
146
248
  ruleId: "navigation-portal-layout-not-found",
147
249
  path: options.path,
148
250
  details: { portalUid, layoutUid: layoutUid || null }
149
251
  });
150
252
  }
151
- const layout = await this.db.getRepository("uiLayouts").findOne({
152
- filter: { uid: layoutUid },
153
- fields: ["uid", "layoutType", "enabled"],
154
- transaction: options.transaction
155
- });
156
- if (!layout) {
157
- (0, import_errors.throwBadRequest)(
158
- `flowSurfaces ${options.actionName} portal '${portalUid}' backing UI layout '${layoutUid}' does not exist`,
159
- {
160
- ruleId: "navigation-portal-layout-not-found",
161
- path: options.path,
162
- details: { portalUid, layoutUid }
163
- }
164
- );
165
- }
166
- if (readRecordField(layout, "enabled") !== true) {
167
- (0, import_errors.throwBadRequest)(
168
- `flowSurfaces ${options.actionName} portal '${portalUid}' backing UI layout '${layoutUid}' is disabled`,
169
- {
170
- ruleId: "navigation-portal-layout-disabled",
171
- path: options.path,
172
- details: { portalUid, layoutUid }
173
- }
174
- );
175
- }
176
- if (!await this.canAccessPortal(portalUid, options.currentRoles, options.transaction)) {
177
- (0, import_errors.throwForbidden)(
178
- `flowSurfaces ${options.actionName} current roles cannot access portal '${portalUid}'`,
179
- "FLOW_SURFACE_NAVIGATION_PORTAL_FORBIDDEN",
180
- {
181
- ruleId: "navigation-portal-forbidden",
182
- path: options.path,
183
- details: { portalUid }
184
- }
185
- );
186
- }
187
253
  return {
188
254
  uid: portalUid,
189
255
  title: readStringField(portal, "title") || portalUid,
190
256
  icon: readStringField(portal, "icon") || null,
191
- routeName: readStringField(portal, "routeName"),
257
+ routeName: readStringField(portal, "portalName"),
192
258
  routePath: readStringField(portal, "routePath"),
193
259
  authCheck: readRecordField(portal, "authCheck") === true,
194
260
  enabled: true,
195
261
  layoutUid,
196
- layoutType: readStringField(layout, "layoutType")
262
+ layoutType,
263
+ portalType,
264
+ routeScopeKind
197
265
  };
198
266
  }
199
267
  async readRoutePortalUids(routeId, transaction) {
@@ -293,51 +361,53 @@ class FlowSurfaceNavigationTargetsService {
293
361
  return left.uid.localeCompare(right.uid);
294
362
  });
295
363
  }
296
- async listAccessiblePortalTargets(currentRoles, transaction) {
364
+ async getEnabledPortalInventory(currentRoles, transaction) {
365
+ const records = await this.db.getRepository("multiPortals").find({
366
+ filter: { enabled: true },
367
+ fields: ["uid", "title", "icon", "portalType", "portalName", "routePath", "authCheck", "enabled", "uiLayoutUid"],
368
+ sort: ["uid"],
369
+ transaction
370
+ });
371
+ const enabled = records.filter((portal) => {
372
+ const uiLayoutUid = readStringField(portal, "uiLayoutUid");
373
+ return !!getMultiPortalLayoutType(uiLayoutUid);
374
+ });
375
+ enabled.sort(
376
+ (left, right) => String(readStringField(left, "uid") || "").localeCompare(String(readStringField(right, "uid") || ""))
377
+ );
297
378
  const roles = normalizeRoles(currentRoles);
298
- let accessiblePortalUids;
299
- if (!roles.includes("root")) {
300
- if (!roles.length || !this.db.getCollection("rolesMultiPortals")) {
301
- return [];
302
- }
379
+ const isRoot = roles.includes("root");
380
+ const accessiblePortalUids = /* @__PURE__ */ new Set();
381
+ if (!isRoot && roles.length && this.db.getCollection("rolesMultiPortals")) {
303
382
  const grants = await this.db.getRepository("rolesMultiPortals").find({
304
- fields: ["multiPortalUid"],
305
383
  filter: { roleName: roles },
384
+ fields: ["multiPortalUid"],
306
385
  transaction
307
386
  });
308
- accessiblePortalUids = Array.from(
309
- new Set(
310
- grants.map((grant) => readStringField(grant, "multiPortalUid")).filter((portalUid) => !!portalUid)
311
- )
312
- );
313
- if (!accessiblePortalUids.length) {
314
- return [];
387
+ for (const grant of grants) {
388
+ const portalUid = readStringField(grant, "multiPortalUid");
389
+ if (portalUid) {
390
+ accessiblePortalUids.add(portalUid);
391
+ }
315
392
  }
316
393
  }
317
- const accessiblePortalUidSet = accessiblePortalUids ? new Set(accessiblePortalUids) : void 0;
318
- const filter = {
319
- enabled: true,
320
- "uid.$notIn": Array.from(RESERVED_MULTI_PORTAL_UIDS)
321
- };
322
- const portals = await this.db.getRepository("multiPortals").find({
323
- filter,
324
- fields: ["uid", "title", "icon", "routeName", "routePath", "authCheck", "enabled", "uiLayoutUid"],
325
- sort: ["uid"],
326
- transaction
394
+ const accessible = enabled.filter((portal) => {
395
+ const portalUid = readStringField(portal, "uid");
396
+ return !!portalUid && (isRoot || isDefaultLayoutMultiPortalUid(portalUid) || accessiblePortalUids.has(portalUid));
327
397
  });
398
+ return { enabled, accessible };
399
+ }
400
+ async listAccessiblePortalTargets(portals, _transaction) {
328
401
  const targets = [];
329
402
  for (const portal of portals) {
330
403
  const portalUid = readStringField(portal, "uid");
331
404
  const layoutUid = readStringField(portal, "uiLayoutUid");
332
- if (!portalUid || accessiblePortalUidSet && !accessiblePortalUidSet.has(portalUid) || !layoutUid || !this.db.getCollection("uiLayouts")) {
405
+ const portalType = readStringField(portal, "portalType");
406
+ if (!portalUid || portalType !== "no-code") {
333
407
  continue;
334
408
  }
335
- const layout = await this.db.getRepository("uiLayouts").findOne({
336
- filter: { uid: layoutUid, enabled: true },
337
- fields: ["layoutType"],
338
- transaction
339
- });
340
- if (!layout) {
409
+ const layoutType = getMultiPortalLayoutType(layoutUid);
410
+ if (!layoutUid || !layoutType) {
341
411
  continue;
342
412
  }
343
413
  targets.push({
@@ -347,13 +417,31 @@ class FlowSurfaceNavigationTargetsService {
347
417
  title: readStringField(portal, "title") || portalUid,
348
418
  icon: readStringField(portal, "icon") || null,
349
419
  layoutUid,
350
- layoutType: readStringField(layout, "layoutType"),
351
- routeName: readStringField(portal, "routeName"),
420
+ layoutType,
421
+ routeName: readStringField(portal, "portalName"),
352
422
  routePath: readStringField(portal, "routePath"),
353
423
  authCheck: readRecordField(portal, "authCheck") === true
354
424
  });
355
425
  }
356
- return targets;
426
+ return targets.sort((left, right) => this.comparePortalPriority(left, right));
427
+ }
428
+ comparePortalPriority(left, right) {
429
+ const priorityDelta = this.getPortalPriority(left) - this.getPortalPriority(right);
430
+ if (priorityDelta) {
431
+ return priorityDelta;
432
+ }
433
+ return String(readStringField(left, "uid") || "").localeCompare(String(readStringField(right, "uid") || ""));
434
+ }
435
+ getPortalPriority(portal) {
436
+ const uiLayout = readRecordField(portal, "uiLayout");
437
+ const layoutType = readStringField(portal, "layoutType") || readStringField(uiLayout, "layoutType");
438
+ if (readStringField(portal, "uid") === DEFAULT_ADMIN_MULTI_PORTAL_UID) {
439
+ return 0;
440
+ }
441
+ if (layoutType !== "mobile") {
442
+ return 1;
443
+ }
444
+ return 2;
357
445
  }
358
446
  async canAccessPortal(portalUid, currentRoles, transaction) {
359
447
  const roles = normalizeRoles(currentRoles);
@@ -96,6 +96,12 @@ export declare class FlowSurfacesService {
96
96
  private resolveDesktopRouteLayoutUidFilter;
97
97
  private assertNavigationTargetIsExclusive;
98
98
  private readDesktopRouteScope;
99
+ private buildDesktopRouteScopeForPortal;
100
+ private assertAdminLayoutCreateScope;
101
+ private getPortalRouteFilterUid;
102
+ private assertRouteBelongsToResolvedPortal;
103
+ private resolveDefaultDesktopRouteScope;
104
+ private resolveDesktopRouteMatchScope;
99
105
  private resolveRequestedDesktopRouteScope;
100
106
  private resolveExistingDesktopRouteScope;
101
107
  private attachDesktopRouteScopeToRouteAndChildren;
@@ -227,6 +233,7 @@ export declare class FlowSurfacesService {
227
233
  private dispatchPlanOnlyAction;
228
234
  private resolveApplyBlueprintReplaceTargetInfo;
229
235
  private resolveApplyBlueprintCreateNavigationGroup;
236
+ private resolveApplyBlueprintDefaultNavigationTarget;
230
237
  private normalizeApplyBlueprintCreateMobileNavigation;
231
238
  private assertApplyBlueprintCreateNavigationTarget;
232
239
  private resolveApplyBlueprintCreatePageIdentity;
@@ -1204,6 +1204,83 @@ class FlowSurfacesService {
1204
1204
  layoutUids: await this.readDesktopRouteUiLayoutUids(routeId, transaction)
1205
1205
  };
1206
1206
  }
1207
+ buildDesktopRouteScopeForPortal(portal) {
1208
+ return {
1209
+ portalUids: portal.routeScopeKind === "portal" ? [portal.uid] : [],
1210
+ layoutUids: portal.routeScopeKind === "layout" ? [portal.layoutUid] : [],
1211
+ selectedPortal: portal,
1212
+ layoutType: portal.layoutType
1213
+ };
1214
+ }
1215
+ assertAdminLayoutCreateScope(actionName, scope, path) {
1216
+ if (!this.navigationTargets.hasMultiPortalCapability() || scope.selectedPortal || !scope.layoutUids.includes(import_navigation_targets.DEFAULT_ADMIN_UI_LAYOUT_UID)) {
1217
+ return;
1218
+ }
1219
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} cannot create navigation directly in the Admin layout`, {
1220
+ ruleId: "navigation-admin-layout-not-portal-target",
1221
+ path,
1222
+ details: {
1223
+ layoutUid: import_navigation_targets.DEFAULT_ADMIN_UI_LAYOUT_UID,
1224
+ uiBuilderAllowed: false,
1225
+ adminLayoutFallbackAllowed: false,
1226
+ agentInstruction: "Resolve the target Portal first. For a no-code Portal, pass portalUid (navigation.portalUid in applyBlueprint); for an AI Portal, stop this Flow Surfaces write and implement the request in the Portal source code."
1227
+ }
1228
+ });
1229
+ }
1230
+ getPortalRouteFilterUid(portal) {
1231
+ return (portal == null ? void 0 : portal.routeScopeKind) === "portal" ? portal.uid : void 0;
1232
+ }
1233
+ async assertRouteBelongsToResolvedPortal(actionName, route, portal, path, transaction) {
1234
+ if (portal.routeScopeKind === "layout") {
1235
+ await this.assertDesktopRouteBelongsToUiLayout(actionName, route, portal.layoutUid, path, transaction);
1236
+ return;
1237
+ }
1238
+ await this.navigationTargets.assertRouteBelongsToPortal(
1239
+ actionName,
1240
+ this.readRouteField(route, "id"),
1241
+ portal.uid,
1242
+ path,
1243
+ transaction
1244
+ );
1245
+ }
1246
+ async resolveDefaultDesktopRouteScope(actionName, options = {}) {
1247
+ if (this.navigationTargets.hasMultiPortalCapability()) {
1248
+ const portal = await this.navigationTargets.resolveDefaultPortal({
1249
+ actionName,
1250
+ currentRoles: options.currentRoles,
1251
+ transaction: options.transaction
1252
+ });
1253
+ return this.buildDesktopRouteScopeForPortal(portal);
1254
+ }
1255
+ const layoutUids = await this.resolveDefaultDesktopRouteUiLayoutUids(options.transaction);
1256
+ return {
1257
+ portalUids: [],
1258
+ layoutUids,
1259
+ layoutType: layoutUids.length === 1 ? await this.getDesktopRouteUiLayoutTypeByUid(layoutUids[0], options.transaction) : void 0
1260
+ };
1261
+ }
1262
+ async resolveDesktopRouteMatchScope(layoutUid, portalUid, actionName, options = {}) {
1263
+ if (portalUid) {
1264
+ const portal = await this.navigationTargets.resolvePortal(portalUid, {
1265
+ actionName,
1266
+ path: "values.navigation.portalUid",
1267
+ currentRoles: options.currentRoles,
1268
+ transaction: options.transaction
1269
+ });
1270
+ return {
1271
+ layoutUids: portal.routeScopeKind === "layout" ? [portal.layoutUid] : void 0,
1272
+ portalUid: this.getPortalRouteFilterUid(portal)
1273
+ };
1274
+ }
1275
+ if (this.normalizeDesktopRouteLayoutUidFilter(layoutUid).length) {
1276
+ return { layoutUids: layoutUid, portalUid: void 0 };
1277
+ }
1278
+ const defaultScope = await this.resolveDefaultDesktopRouteScope(actionName, options);
1279
+ return {
1280
+ layoutUids: defaultScope.layoutUids.length ? defaultScope.layoutUids : void 0,
1281
+ portalUid: this.getPortalRouteFilterUid(defaultScope.selectedPortal)
1282
+ };
1283
+ }
1207
1284
  async resolveRequestedDesktopRouteScope(values, parentRoute, actionName, options = {}) {
1208
1285
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(values.layoutUid);
1209
1286
  const portalUid = this.navigationTargets.normalizePortalUid(values.portalUid);
@@ -1219,22 +1296,19 @@ class FlowSurfacesService {
1219
1296
  transaction: options.transaction
1220
1297
  });
1221
1298
  if (parentRoute) {
1222
- await this.navigationTargets.assertRouteBelongsToPortal(
1299
+ await this.assertRouteBelongsToResolvedPortal(
1223
1300
  actionName,
1224
- this.readRouteField(parentRoute, "id"),
1225
- portal.uid,
1301
+ parentRoute,
1302
+ portal,
1226
1303
  "values.parentMenuRouteId",
1227
1304
  options.transaction
1228
1305
  );
1229
1306
  }
1230
- return {
1231
- portalUids: [portal.uid],
1232
- layoutUids: [],
1233
- selectedPortal: portal,
1234
- layoutType: portal.layoutType
1235
- };
1307
+ return this.buildDesktopRouteScopeForPortal(portal);
1236
1308
  }
1237
1309
  if (layoutUid) {
1310
+ const scope2 = { portalUids: [], layoutUids: [layoutUid] };
1311
+ this.assertAdminLayoutCreateScope(actionName, scope2, "values.layoutUid");
1238
1312
  await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, "values.layoutUid", options.transaction);
1239
1313
  if (parentRoute) {
1240
1314
  await this.assertDesktopRouteBelongsToUiLayout(
@@ -1246,8 +1320,7 @@ class FlowSurfacesService {
1246
1320
  );
1247
1321
  }
1248
1322
  return {
1249
- portalUids: [],
1250
- layoutUids: [layoutUid],
1323
+ ...scope2,
1251
1324
  layoutType: await this.getDesktopRouteUiLayoutTypeByUid(layoutUid, options.transaction)
1252
1325
  };
1253
1326
  }
@@ -1280,10 +1353,17 @@ class FlowSurfacesService {
1280
1353
  };
1281
1354
  }
1282
1355
  }
1356
+ if (!parentRoute) {
1357
+ return this.resolveDefaultDesktopRouteScope(actionName, options);
1358
+ }
1283
1359
  const layoutUids = await this.resolveInheritedDesktopRouteUiLayoutUids(parentRoute, options.transaction);
1284
- return {
1360
+ const scope = {
1285
1361
  portalUids: [],
1286
- layoutUids,
1362
+ layoutUids
1363
+ };
1364
+ this.assertAdminLayoutCreateScope(actionName, scope, "values.parentMenuRouteId");
1365
+ return {
1366
+ ...scope,
1287
1367
  layoutType: layoutUids.length === 1 ? await this.getDesktopRouteUiLayoutTypeByUid(layoutUids[0], options.transaction) : void 0
1288
1368
  };
1289
1369
  }
@@ -1302,20 +1382,18 @@ class FlowSurfacesService {
1302
1382
  currentRoles: options.currentRoles,
1303
1383
  transaction: options.transaction
1304
1384
  });
1305
- await this.navigationTargets.assertRouteBelongsToPortal(
1385
+ await this.assertRouteBelongsToResolvedPortal(
1306
1386
  actionName,
1307
- this.readRouteField(route, "id"),
1308
- portal.uid,
1387
+ route,
1388
+ portal,
1309
1389
  "values.menuRouteId",
1310
1390
  options.transaction
1311
1391
  );
1312
- return {
1313
- ...routeScope,
1314
- selectedPortal: portal,
1315
- layoutType: portal.layoutType
1316
- };
1392
+ return this.buildDesktopRouteScopeForPortal(portal);
1317
1393
  }
1318
1394
  if (layoutUid) {
1395
+ const scope2 = { ...routeScope, layoutUids: [layoutUid] };
1396
+ this.assertAdminLayoutCreateScope(actionName, scope2, "values.layoutUid");
1319
1397
  await this.assertEnabledDesktopRouteUiLayoutUid(layoutUid, actionName, "values.layoutUid", options.transaction);
1320
1398
  await this.assertDesktopRouteBelongsToUiLayout(
1321
1399
  actionName,
@@ -1325,7 +1403,7 @@ class FlowSurfacesService {
1325
1403
  options.transaction
1326
1404
  );
1327
1405
  return {
1328
- ...routeScope,
1406
+ ...scope2,
1329
1407
  layoutType: await this.getDesktopRouteUiLayoutTypeByUid(layoutUid, options.transaction)
1330
1408
  };
1331
1409
  }
@@ -1355,10 +1433,22 @@ class FlowSurfacesService {
1355
1433
  layoutType: portal.layoutType
1356
1434
  };
1357
1435
  }
1358
- const layoutUids = routeScope.layoutUids.length ? routeScope.layoutUids : await this.ensureDesktopRouteUiLayouts(route, options.transaction);
1359
- return {
1436
+ let layoutUids = routeScope.layoutUids;
1437
+ if (!layoutUids.length) {
1438
+ const parentRouteId = this.readRouteField(route, "parentId");
1439
+ const parentRoute = import_lodash.default.isNil(parentRouteId) ? null : await this.findMenuRouteById(parentRouteId, options.transaction);
1440
+ layoutUids = await this.resolveInheritedDesktopRouteUiLayoutUids(parentRoute, options.transaction);
1441
+ }
1442
+ const scope = {
1360
1443
  portalUids: [],
1361
- layoutUids,
1444
+ layoutUids
1445
+ };
1446
+ this.assertAdminLayoutCreateScope(actionName, scope, "values.menuRouteId");
1447
+ if (!routeScope.layoutUids.length) {
1448
+ await this.setDesktopRouteUiLayouts(this.readRouteField(route, "id"), layoutUids, options.transaction);
1449
+ }
1450
+ return {
1451
+ ...scope,
1362
1452
  layoutType: layoutUids.length === 1 ? await this.getDesktopRouteUiLayoutTypeByUid(layoutUids[0], options.transaction) : void 0
1363
1453
  };
1364
1454
  }
@@ -1518,7 +1608,6 @@ class FlowSurfacesService {
1518
1608
  }
1519
1609
  }
1520
1610
  async createFlowMenuGroup(values, options = {}) {
1521
- var _a;
1522
1611
  const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, options.transaction);
1523
1612
  const parentId = this.readRouteField(parentRoute, "id") ?? null;
1524
1613
  const routeScope = await this.resolveRequestedDesktopRouteScope(values, parentRoute, "createMenu", options);
@@ -1528,7 +1617,7 @@ class FlowSurfacesService {
1528
1617
  title,
1529
1618
  options.transaction,
1530
1619
  routeScope.layoutUids,
1531
- (_a = routeScope.selectedPortal) == null ? void 0 : _a.uid
1620
+ this.getPortalRouteFilterUid(routeScope.selectedPortal)
1532
1621
  );
1533
1622
  if (existingGroups.length === 1) {
1534
1623
  return this.buildMenuResult(existingGroups[0]);
@@ -3569,7 +3658,7 @@ class FlowSurfacesService {
3569
3658
  tabs
3570
3659
  };
3571
3660
  }
3572
- async resolveApplyBlueprintCreateNavigationGroup(document, transaction) {
3661
+ async resolveApplyBlueprintCreateNavigationGroup(document, options = {}) {
3573
3662
  var _a;
3574
3663
  if (document.mode !== "create" || !import_lodash.default.isPlainObject((_a = document.navigation) == null ? void 0 : _a.group)) {
3575
3664
  return document;
@@ -3583,12 +3672,13 @@ class FlowSurfacesService {
3583
3672
  }
3584
3673
  const layoutUid = this.normalizeExplicitDesktopRouteLayoutUid(document.navigation.layoutUid);
3585
3674
  const portalUid = this.navigationTargets.normalizePortalUid(document.navigation.portalUid);
3675
+ const matchScope = await this.resolveDesktopRouteMatchScope(layoutUid, portalUid, "applyBlueprint", options);
3586
3676
  const matchedRoutes = await this.findMenuGroupRoutesByParentIdAndTitle(
3587
3677
  null,
3588
3678
  groupTitle,
3589
- transaction,
3590
- layoutUid || await this.resolveDefaultDesktopRouteUiLayoutUids(transaction),
3591
- portalUid
3679
+ options.transaction,
3680
+ matchScope.layoutUids,
3681
+ matchScope.portalUid
3592
3682
  );
3593
3683
  if (!matchedRoutes.length) {
3594
3684
  return document;
@@ -3614,6 +3704,24 @@ class FlowSurfacesService {
3614
3704
  }
3615
3705
  };
3616
3706
  }
3707
+ async resolveApplyBlueprintDefaultNavigationTarget(document, options = {}) {
3708
+ var _a, _b, _c, _d;
3709
+ if (document.mode !== "create" || ((_a = document.navigation) == null ? void 0 : _a.layoutUid) || ((_b = document.navigation) == null ? void 0 : _b.portalUid) || !import_lodash.default.isUndefined((_d = (_c = document.navigation) == null ? void 0 : _c.group) == null ? void 0 : _d.routeId) || !this.navigationTargets.hasMultiPortalCapability()) {
3710
+ return document;
3711
+ }
3712
+ const portal = await this.navigationTargets.resolveDefaultPortal({
3713
+ actionName: "applyBlueprint",
3714
+ currentRoles: options.currentRoles,
3715
+ transaction: options.transaction
3716
+ });
3717
+ return {
3718
+ ...document,
3719
+ navigation: {
3720
+ ...document.navigation,
3721
+ portalUid: portal.uid
3722
+ }
3723
+ };
3724
+ }
3617
3725
  async normalizeApplyBlueprintCreateMobileNavigation(document, options = {}) {
3618
3726
  var _a, _b;
3619
3727
  if (document.mode !== "create") {
@@ -3669,15 +3777,20 @@ class FlowSurfacesService {
3669
3777
  return;
3670
3778
  }
3671
3779
  const groupRoute2 = await this.assertMenuParentIsGroup(groupRouteId2, options.transaction);
3672
- await this.navigationTargets.assertRouteBelongsToPortal(
3780
+ await this.assertRouteBelongsToResolvedPortal(
3673
3781
  "applyBlueprint",
3674
- this.readRouteField(groupRoute2, "id"),
3675
- portal.uid,
3782
+ groupRoute2,
3783
+ portal,
3676
3784
  "values.navigation.group.routeId",
3677
3785
  options.transaction
3678
3786
  );
3679
3787
  return;
3680
3788
  }
3789
+ this.assertAdminLayoutCreateScope(
3790
+ "applyBlueprint",
3791
+ { portalUids: [], layoutUids: [layoutUid] },
3792
+ "values.navigation.layoutUid"
3793
+ );
3681
3794
  await this.assertEnabledDesktopRouteUiLayoutUid(
3682
3795
  layoutUid,
3683
3796
  "applyBlueprint",
@@ -3700,7 +3813,7 @@ class FlowSurfacesService {
3700
3813
  options.transaction
3701
3814
  );
3702
3815
  }
3703
- async resolveApplyBlueprintCreatePageIdentity(document, transaction) {
3816
+ async resolveApplyBlueprintCreatePageIdentity(document, options = {}) {
3704
3817
  var _a, _b, _c, _d, _e, _f, _g;
3705
3818
  if (document.mode !== "create") {
3706
3819
  return document;
@@ -3713,12 +3826,10 @@ class FlowSurfacesService {
3713
3826
  if (!pageTitle || !hasGroupRouteId && !layoutUid && !portalUid) {
3714
3827
  return document;
3715
3828
  }
3716
- const groupRoute = hasGroupRouteId ? await this.findMenuRouteById(groupRouteId, transaction) : null;
3829
+ const groupRoute = hasGroupRouteId ? await this.findMenuRouteById(groupRouteId, options.transaction) : null;
3830
+ const groupScope = groupRoute && !portalUid ? await this.readDesktopRouteScope(groupRoute, options.transaction) : void 0;
3717
3831
  if (!portalUid && groupRoute) {
3718
- const groupPortalUids = await this.navigationTargets.readRoutePortalUids(
3719
- this.readRouteField(groupRoute, "id"),
3720
- transaction
3721
- );
3832
+ const groupPortalUids = (groupScope == null ? void 0 : groupScope.portalUids) || [];
3722
3833
  if (groupPortalUids.length > 1) {
3723
3834
  (0, import_errors.throwBadRequest)(
3724
3835
  `flowSurfaces applyBlueprint navigation.group.routeId '${groupRouteId}' belongs to multiple portals; pass navigation.portalUid explicitly`,
@@ -3734,13 +3845,23 @@ class FlowSurfacesService {
3734
3845
  }
3735
3846
  portalUid = groupPortalUids[0];
3736
3847
  }
3737
- const layoutUidFilter = portalUid ? void 0 : layoutUid || (groupRoute ? await this.resolveInheritedDesktopRouteUiLayoutUids(groupRoute, transaction) : void 0);
3848
+ const portalMatchScope = portalUid ? await this.resolveDesktopRouteMatchScope(layoutUid, portalUid, "applyBlueprint", options) : void 0;
3849
+ let layoutUidFilter = (portalMatchScope == null ? void 0 : portalMatchScope.layoutUids) || layoutUid;
3850
+ if (!portalMatchScope && !layoutUid && groupRoute) {
3851
+ layoutUidFilter = (groupScope == null ? void 0 : groupScope.layoutUids.length) ? groupScope.layoutUids : await this.resolveInheritedDesktopRouteUiLayoutUids(groupRoute, options.transaction);
3852
+ this.assertAdminLayoutCreateScope(
3853
+ "applyBlueprint",
3854
+ { portalUids: [], layoutUids: this.normalizeDesktopRouteLayoutUidFilter(layoutUidFilter) },
3855
+ "values.navigation.group.routeId"
3856
+ );
3857
+ }
3858
+ const portalUidFilter = portalMatchScope == null ? void 0 : portalMatchScope.portalUid;
3738
3859
  const matchedPages = await this.findFlowPageRoutesByParentIdAndTitle(
3739
3860
  hasGroupRouteId ? groupRouteId : null,
3740
3861
  pageTitle,
3741
- transaction,
3862
+ options.transaction,
3742
3863
  layoutUidFilter,
3743
- portalUid
3864
+ portalUidFilter
3744
3865
  );
3745
3866
  if (!matchedPages.length) {
3746
3867
  return document;
@@ -3769,14 +3890,15 @@ class FlowSurfacesService {
3769
3890
  }
3770
3891
  async prepareApplyBlueprintRequest(values, options = {}, createdKanbanSortFields) {
3771
3892
  var _a;
3772
- const initialDocument = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3893
+ const parsedDocument = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3894
+ const initialDocument = await this.resolveApplyBlueprintDefaultNavigationTarget(parsedDocument, options);
3773
3895
  await this.assertApplyBlueprintCreateNavigationTarget(initialDocument, options);
3774
3896
  const mobileNormalizedDocument = await this.normalizeApplyBlueprintCreateMobileNavigation(initialDocument, options);
3775
3897
  const groupResolvedDocument = await this.resolveApplyBlueprintCreateNavigationGroup(
3776
3898
  mobileNormalizedDocument,
3777
- options.transaction
3899
+ options
3778
3900
  );
3779
- const document = await this.resolveApplyBlueprintCreatePageIdentity(groupResolvedDocument, options.transaction);
3901
+ const document = await this.resolveApplyBlueprintCreatePageIdentity(groupResolvedDocument, options);
3780
3902
  await this.prepareApplyBlueprintKanbanBlocks(document, options.transaction, createdKanbanSortFields);
3781
3903
  const replaceTarget = document.mode === "replace" && document.target ? await this.resolveApplyBlueprintReplaceTargetInfo(document.target.pageSchemaUid, options.transaction) : void 0;
3782
3904
  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) {
@@ -4445,7 +4567,18 @@ class FlowSurfacesService {
4445
4567
  await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("applyBlueprint", values, {
4446
4568
  transaction: options.transaction,
4447
4569
  enabledPackages,
4448
- findMenuGroupRoutesByTitle: (title, transaction, targetLayoutUid, targetPortalUid) => this.findMenuGroupRoutesByTitle(title, transaction, targetLayoutUid, targetPortalUid),
4570
+ findMenuGroupRoutesByTitle: async (title, transaction, targetLayoutUid, targetPortalUid) => {
4571
+ const matchScope = await this.resolveDesktopRouteMatchScope(
4572
+ targetLayoutUid,
4573
+ targetPortalUid,
4574
+ "applyBlueprint",
4575
+ {
4576
+ transaction,
4577
+ currentRoles: options.currentRoles
4578
+ }
4579
+ );
4580
+ return this.findMenuGroupRoutesByTitle(title, transaction, matchScope.layoutUids, matchScope.portalUid);
4581
+ },
4449
4582
  getUiLayoutTypeByUid: (layoutUid2, transaction) => this.getDesktopRouteUiLayoutTypeByUid(layoutUid2, transaction),
4450
4583
  getPortalLayoutTypeByUid: async (targetPortalUid, transaction) => {
4451
4584
  if ((resolvedPortal == null ? void 0 : resolvedPortal.uid) === targetPortalUid) {
@@ -6515,18 +6648,18 @@ class FlowSurfacesService {
6515
6648
  currentRoles: options.currentRoles,
6516
6649
  transaction: options.transaction
6517
6650
  });
6518
- await this.navigationTargets.assertRouteBelongsToPortal(
6651
+ await this.assertRouteBelongsToResolvedPortal(
6519
6652
  "updateMenu",
6520
- this.readRouteField(route, "id"),
6521
- portal.uid,
6653
+ route,
6654
+ portal,
6522
6655
  "values.menuRouteId",
6523
6656
  options.transaction
6524
6657
  );
6525
6658
  if (nextParentRoute) {
6526
- await this.navigationTargets.assertRouteBelongsToPortal(
6659
+ await this.assertRouteBelongsToResolvedPortal(
6527
6660
  "updateMenu",
6528
- this.readRouteField(nextParentRoute, "id"),
6529
- portal.uid,
6661
+ nextParentRoute,
6662
+ portal,
6530
6663
  "values.parentMenuRouteId",
6531
6664
  options.transaction
6532
6665
  );
@@ -621,7 +621,7 @@ const actionDocs = {
621
621
  },
622
622
  listNavigationTargets: {
623
623
  tags: [FLOW_SURFACES_TAG],
624
- summary: "List available UI layouts and role-accessible Multi-portal workspaces",
624
+ summary: "List available UI layouts and role-accessible Multi-portal entries",
625
625
  description: valuesCompatibilityNote(
626
626
  `Returns the default Admin target, enabled UI layouts, and enabled custom Multi-portal targets visible to the current roles. capabilities.multiPortal=false and an empty custom-portal set are normal when the commercial Multi-portal capability is not installed. Reserved default portal records are never returned as custom workspaces; Admin and Mobile use their ordinary layout uids. Call this action only when the user explicitly names a workspace or asks to discover navigation targets. ${FLOW_SURFACES_READ_ACL_NOTE}`
627
627
  ),
@@ -632,7 +632,7 @@ const actionDocs = {
632
632
  tags: [FLOW_SURFACES_TAG],
633
633
  summary: "Apply a page blueprint to create or replace one Modern page",
634
634
  description: valuesCompatibilityNote(
635
- `In create mode, \`navigation.portalUid\` targets one enabled, role-accessible Multi-portal workspace and is mutually exclusive with \`navigation.layoutUid\`. Portal-backed mobile layouts ignore \`navigation.group\`, while desktop-backed portals scope group reuse and duplicate-page identity to that portal. If Multi-portal is unavailable, ordinary requests without \`portalUid\` keep their existing Admin behavior. Accepts one simplified JSON page blueprint and compiles it to internal flow-surface operations. The public blueprint describes page structure (\`create\` or \`replace\`, page metadata, ordered tabs, blocks, fields, actions, inline popups, optional reusable assets) and optional top-level \`reaction.items[]\` for whole-page interaction authoring. Each reaction item targets an explicit local key / bind key produced by the same blueprint run. Only explicitly listed reaction items are written. \`rules: []\` clears the targeted slot. Repeating the same \`(type, target)\` reaction slot in one blueprint is invalid. In \`replace\`, reaction targets always bind to the newly produced blueprint result, not historical nodes from the previous page version; if a slot must exist in the resulting surface, include it explicitly instead of relying on omission. Localized reaction edits on an existing surface should use \`getReactionMeta\` + \`set*Rules\` instead of applying a whole page blueprint again. The request body is that page-document JSON object itself and must not be JSON-stringified. Wrong: \`{ "requestBody": "{\\"version\\":\\"1\\"}" }\`. Internal planning details stay hidden. In \`create\`, \`navigation.layoutUid\` optionally scopes menu group reuse, duplicate page identity checks, and newly created routes to an enabled UI layout such as \`admin-layout-model\` or \`mobile-layout-model\`; when the target layout type is mobile, applyBlueprint ignores \`navigation.group\` and creates a root-level mobile tab page. When \`layoutUid\` is omitted, routes keep the existing admin/default inheritance behavior. In non-mobile \`create\`, \`navigation.group.routeId\` has the highest priority when targeting an existing menu group. If \`routeId\` is present, applyBlueprint ignores \`title\`, \`icon\`, \`tooltip\`, and \`hideInMenu\` on \`navigation.group\`; applyBlueprint create mode does not mutate existing group metadata, so callers should use \`updateMenu\` separately when that is required. When \`routeId\` is omitted and \`navigation.group.title\` is provided, applyBlueprint reuses one existing same-title group in the target layout when it is unique, creates a new group when none exists, and rejects ambiguous multi-match cases. Metadata such as \`icon\`, \`tooltip\`, and \`hideInMenu\` is used only when a new group is created and is ignored when an existing group is reused. \`replace\` uses \`target.pageSchemaUid\`, updates only the explicit page-level fields provided in \`page\`, maps blueprint tabs to existing route-backed tab slots by index, rewrites each slot in order, removes trailing old tabs, and appends extra new tabs when needed. Tab and block keys are optional in the public blueprint; omit them unless custom layout or cross-block targeting needs a stable in-document identifier. \`layout\` is only allowed on tabs and inline popup documents; blocks themselves do not accept a \`layout\` property. Public applyBlueprint blocks do not support generic \`form\`; use \`editForm\` or \`createForm\`. AI employee actions use \`type: "aiEmployee"\` plus public \`settings.username\`, \`workContext\`, \`tasks\`, \`auto\`, and \`style\`; work context may target \`self\` or a same-blueprint block key and is persisted as real Flow Model \`uid\` values. For JS blocks/fields/actions, \`script\` is a non-empty string asset key into \`assets.scripts\`; put inline JS in \`settings.code\` and \`settings.version\`. Direct \`table\` / \`list\` / \`gridCard\` / \`calendar\` / \`kanban\` blocks may omit \`defaultFilter\`; the backend generates one from live metadata with up to 4 scalar/filterable fields. Explicit values must contain at least the smaller of 3 and the collection eligible-field count, and values with more than 4 fields are truncated before persistence. A valid explicit or generated block-level value backfills the default \`filter\` action \`settings.defaultFilter\`; explicit filter-action \`settings.defaultFilter\` still wins. ${TREE_TABLE_RECORD_ACTION_DEFAULTS_NOTE} ${APPLY_BLUEPRINT_TREE_TABLE_TITLE_FIELD_NOTE} Inline popup documents may set \`popup.tryTemplate=true\` to ask the backend for the best compatible popup template before falling back to local popup content. Inline popup documents may also combine \`popup.tryTemplate\` with \`popup.saveAsTemplate={ name, description, local? }\`: a hit binds the matched template immediately and lets later inline popups in the same blueprint reuse that final bound template through \`popup.template={ local, mode }\`, while a miss requires explicit local \`popup.blocks\` so the fallback popup can be saved and reused. Custom \`edit\` popups that provide \`popup.blocks\` must include exactly one \`editForm\` block; that \`editForm\` may omit \`resource\` and then inherits the opener's current-record context. When layout is omitted, applyBlueprint auto-generates a simple top-to-bottom layout. When a \`replace\` run expands a page to multiple tabs while the current page still has \`enableTabs=false\`, callers must set \`page.enableTabs=true\` explicitly. The response hides execution internals and returns only the resolved page target and final surface readback.`
635
+ `In create mode, \`navigation.portalUid\` targets one enabled, role-accessible Multi-portal entry and is mutually exclusive with \`navigation.layoutUid\`. Portal-backed mobile layouts ignore \`navigation.group\`, while desktop-backed portals scope group reuse and duplicate-page identity to that portal. If Multi-portal is unavailable, ordinary requests without \`portalUid\` keep their existing Admin behavior. Accepts one simplified JSON page blueprint and compiles it to internal flow-surface operations. The public blueprint describes page structure (\`create\` or \`replace\`, page metadata, ordered tabs, blocks, fields, actions, inline popups, optional reusable assets) and optional top-level \`reaction.items[]\` for whole-page interaction authoring. Each reaction item targets an explicit local key / bind key produced by the same blueprint run. Only explicitly listed reaction items are written. \`rules: []\` clears the targeted slot. Repeating the same \`(type, target)\` reaction slot in one blueprint is invalid. In \`replace\`, reaction targets always bind to the newly produced blueprint result, not historical nodes from the previous page version; if a slot must exist in the resulting surface, include it explicitly instead of relying on omission. Localized reaction edits on an existing surface should use \`getReactionMeta\` + \`set*Rules\` instead of applying a whole page blueprint again. The request body is that page-document JSON object itself and must not be JSON-stringified. Wrong: \`{ "requestBody": "{\\"version\\":\\"1\\"}" }\`. Internal planning details stay hidden. In \`create\`, \`navigation.layoutUid\` optionally scopes menu group reuse, duplicate page identity checks, and newly created routes to an enabled UI layout such as \`admin-layout-model\` or \`mobile-layout-model\`; when the target layout type is mobile, applyBlueprint ignores \`navigation.group\` and creates a root-level mobile tab page. When \`layoutUid\` is omitted, routes keep the existing admin/default inheritance behavior. In non-mobile \`create\`, \`navigation.group.routeId\` has the highest priority when targeting an existing menu group. If \`routeId\` is present, applyBlueprint ignores \`title\`, \`icon\`, \`tooltip\`, and \`hideInMenu\` on \`navigation.group\`; applyBlueprint create mode does not mutate existing group metadata, so callers should use \`updateMenu\` separately when that is required. When \`routeId\` is omitted and \`navigation.group.title\` is provided, applyBlueprint reuses one existing same-title group in the target layout when it is unique, creates a new group when none exists, and rejects ambiguous multi-match cases. Metadata such as \`icon\`, \`tooltip\`, and \`hideInMenu\` is used only when a new group is created and is ignored when an existing group is reused. \`replace\` uses \`target.pageSchemaUid\`, updates only the explicit page-level fields provided in \`page\`, maps blueprint tabs to existing route-backed tab slots by index, rewrites each slot in order, removes trailing old tabs, and appends extra new tabs when needed. Tab and block keys are optional in the public blueprint; omit them unless custom layout or cross-block targeting needs a stable in-document identifier. \`layout\` is only allowed on tabs and inline popup documents; blocks themselves do not accept a \`layout\` property. Public applyBlueprint blocks do not support generic \`form\`; use \`editForm\` or \`createForm\`. AI employee actions use \`type: "aiEmployee"\` plus public \`settings.username\`, \`workContext\`, \`tasks\`, \`auto\`, and \`style\`; work context may target \`self\` or a same-blueprint block key and is persisted as real Flow Model \`uid\` values. For JS blocks/fields/actions, \`script\` is a non-empty string asset key into \`assets.scripts\`; put inline JS in \`settings.code\` and \`settings.version\`. Direct \`table\` / \`list\` / \`gridCard\` / \`calendar\` / \`kanban\` blocks may omit \`defaultFilter\`; the backend generates one from live metadata with up to 4 scalar/filterable fields. Explicit values must contain at least the smaller of 3 and the collection eligible-field count, and values with more than 4 fields are truncated before persistence. A valid explicit or generated block-level value backfills the default \`filter\` action \`settings.defaultFilter\`; explicit filter-action \`settings.defaultFilter\` still wins. ${TREE_TABLE_RECORD_ACTION_DEFAULTS_NOTE} ${APPLY_BLUEPRINT_TREE_TABLE_TITLE_FIELD_NOTE} Inline popup documents may set \`popup.tryTemplate=true\` to ask the backend for the best compatible popup template before falling back to local popup content. Inline popup documents may also combine \`popup.tryTemplate\` with \`popup.saveAsTemplate={ name, description, local? }\`: a hit binds the matched template immediately and lets later inline popups in the same blueprint reuse that final bound template through \`popup.template={ local, mode }\`, while a miss requires explicit local \`popup.blocks\` so the fallback popup can be saved and reused. Custom \`edit\` popups that provide \`popup.blocks\` must include exactly one \`editForm\` block; that \`editForm\` may omit \`resource\` and then inherits the opener's current-record context. When layout is omitted, applyBlueprint auto-generates a simple top-to-bottom layout. When a \`replace\` run expands a page to multiple tabs while the current page still has \`enableTabs=false\`, callers must set \`page.enableTabs=true\` explicitly. The response hides execution internals and returns only the resolved page target and final surface readback.`
636
636
  ),
637
637
  requestBody: {
638
638
  required: true,
@@ -818,7 +818,7 @@ const actionDocs = {
818
818
  tags: [FLOW_SURFACES_TAG],
819
819
  summary: "Create a group menu or a bindable V2 menu item",
820
820
  description: valuesCompatibilityNote(
821
- 'Creates a FlowSurfaces menu node. `type="group"` creates a menu group. `type="item"` creates a menu item that can be bound to a modern page (v2), and automatically fills in the flowPage route, the default hidden tab route, and the RootPageModel anchor. Optional `layoutUid` scopes the route to an enabled UI layout. Optional `portalUid` scopes it to one enabled, role-accessible Multi-portal workspace and is mutually exclusive with `layoutUid`. Omitted values keep the existing parent/default admin inheritance.'
821
+ 'Creates a FlowSurfaces menu node. `type="group"` creates a menu group. `type="item"` creates a menu item that can be bound to a modern page (v2), and automatically fills in the flowPage route, the default hidden tab route, and the RootPageModel anchor. Optional `layoutUid` scopes the route to an enabled UI layout. Optional `portalUid` scopes it to one enabled, role-accessible Multi-portal entry and is mutually exclusive with `layoutUid`. Omitted values keep the existing parent/default admin inheritance.'
822
822
  ),
823
823
  requestBody: requestBody("FlowSurfaceCreateMenuRequest", import_flow_surfaces.flowSurfaceExamples.createMenu),
824
824
  responses: responses("FlowSurfaceCreateMenuResult")
@@ -827,7 +827,7 @@ const actionDocs = {
827
827
  tags: [FLOW_SURFACES_TAG],
828
828
  summary: "Update menu title/icon/tooltip or move it under another group",
829
829
  description: valuesCompatibilityNote(
830
- "Updates menu display information, or moves a group / item to the top level or under another group. Only `group` and `flowPage` menu nodes are supported. Pass `portalUid` when explicitly targeting a Multi-portal workspace; the route and new parent must already belong to that portal."
830
+ "Updates menu display information, or moves a group / item to the top level or under another group. Only `group` and `flowPage` menu nodes are supported. Pass `portalUid` when explicitly targeting a Multi-portal entry; the route and new parent must already belong to that portal."
831
831
  ),
832
832
  requestBody: requestBody("FlowSurfaceUpdateMenuRequest", import_flow_surfaces.flowSurfaceExamples.updateMenu),
833
833
  responses: responses("FlowSurfaceUpdateMenuResult")
@@ -836,7 +836,7 @@ const actionDocs = {
836
836
  tags: [FLOW_SURFACES_TAG],
837
837
  summary: "Initialize a modern page for an existing bindable menu item",
838
838
  description: valuesCompatibilityNote(
839
- "Initializes a modern page (v2) for an existing bindable menu item through `menuRouteId` first, and fills in the default BlockGridModel. Optional `layoutUid` asserts that the existing menu route belongs to the target UI layout. Optional `portalUid` asserts that it belongs to one enabled, role-accessible Multi-portal workspace and is mutually exclusive with `layoutUid`. Omitted values preserve the route scope and keep default admin compatibility. In compatibility mode, if `menuRouteId` is omitted, the old behavior still applies and a top-level menu plus page will be created automatically. Before initialization, do not call page/tab lifecycle actions such as `addTab`, `updateTab`, `moveTab`, `removeTab`, or `destroyPage`."
839
+ "Initializes a modern page (v2) for an existing bindable menu item through `menuRouteId` first, and fills in the default BlockGridModel. Optional `layoutUid` asserts that the existing menu route belongs to the target UI layout. Optional `portalUid` asserts that it belongs to one enabled, role-accessible Multi-portal entry and is mutually exclusive with `layoutUid`. Omitted values preserve the route scope and keep default admin compatibility. In compatibility mode, if `menuRouteId` is omitted, the old behavior still applies and a top-level menu plus page will be created automatically. Before initialization, do not call page/tab lifecycle actions such as `addTab`, `updateTab`, `moveTab`, `removeTab`, or `destroyPage`."
840
840
  ),
841
841
  requestBody: requestBody("FlowSurfaceCreatePageRequest", import_flow_surfaces.flowSurfaceExamples.createPage),
842
842
  responses: responses("FlowSurfaceCreatePageResult")
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "displayName.zh-CN": "前端流引擎",
5
5
  "description": "",
6
6
  "description.zh-CN": "",
7
- "version": "2.3.0-beta.5",
7
+ "version": "2.4.0-alpha.1",
8
8
  "main": "./dist/server/index.js",
9
9
  "license": "Apache-2.0",
10
10
  "devDependencies": {
@@ -26,5 +26,5 @@
26
26
  "@nocobase/test": "2.x",
27
27
  "@nocobase/utils": "2.x"
28
28
  },
29
- "gitHead": "7047516aba0d14965ddf457b64587103a871b923"
29
+ "gitHead": "82ecd3aed067e4cc98c32e7a0039b6e55cee76a7"
30
30
  }