@pactor-app/app 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,6 +67,25 @@ app.mount('#root');
67
67
  - 未命中路径渲染 404,命中但无权限渲染 403(菜单项 `permission` 配置);
68
68
  - `overlay.open` 的 page 类型 overlay 经 loader 加载页面 DSL,子运行时共享应用级 registries。
69
69
 
70
+ ## 嵌入式使用(PactorPage / createPactorPage)
71
+
72
+ 存量项目(已有自己的路由,或 Next.js 等框架)中局部嵌入 DSL 页面,不接管路由:
73
+
74
+ ```tsx
75
+ import { createPactorPage } from '@pactor-app/app';
76
+ import '@pactor-app/ui/styles.css';
77
+
78
+ // 工厂:组装一次(registries / 交互层 / i18n),多页面实例共享
79
+ const Page = createPactorPage({ loader, services, permissions: ['customer:view'] });
80
+
81
+ <Page pageId="customer-detail" route={{ params: { id } }} />
82
+ // 或直接传 PageDsl:<Page page={pageDsl} />
83
+ ```
84
+
85
+ - 交互层(confirm / message / overlay)与整应用模式一致;多实例状态互相隔离
86
+ - 跳转为自然导航:`Link` 组件渲染原生 `<a href>`,`navigate` 动作退化为 `location.assign`——不注入宿主路由对象
87
+ - `route.params` / `route.query` 经 props 纯数据注入(如 Next.js 的 `searchParams`)
88
+
70
89
  ## 文档
71
90
 
72
91
  完整文档见 <https://github.com/426-330/pactor/tree/main/docs>(`pnpm docs:dev` 本地启动文档站)。
package/dist/index.cjs CHANGED
@@ -22,21 +22,22 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Application: () => Application,
24
24
  DslPageLoader: () => DslPageLoader,
25
+ PactorPage: () => PactorPage,
25
26
  PageErrorBoundary: () => PageErrorBoundary,
26
27
  ReactRouterBinding: () => ReactRouterBinding,
27
- createPactorApplication: () => createPactorApplication
28
+ createPactorApplication: () => createPactorApplication,
29
+ createPactorPage: () => createPactorPage,
30
+ createRuntimeHttpAdapter: () => createRuntimeHttpAdapter,
31
+ wrapHttpAdapter: () => wrapHttpAdapter
28
32
  });
29
33
  module.exports = __toCommonJS(index_exports);
30
34
 
31
35
  // src/application.ts
32
- var import_core2 = require("@pactor-app/core");
33
- var import_components = require("@pactor-app/ui/components");
34
- var import_interaction = require("@pactor-app/ui/interaction");
35
36
  var import_layout2 = require("@pactor-app/ui/layout");
36
37
  var import_permission = require("@pactor-app/permission");
37
38
  var import_resource = require("@pactor-app/resource");
38
39
  var import_router2 = require("@pactor-app/router");
39
- var import_runtime3 = require("@pactor-app/runtime");
40
+ var import_runtime4 = require("@pactor-app/runtime");
40
41
  var import_react5 = require("react");
41
42
  var import_client = require("react-dom/client");
42
43
 
@@ -374,6 +375,94 @@ function ForbiddenPage() {
374
375
  );
375
376
  }
376
377
 
378
+ // src/assembly.ts
379
+ var import_core2 = require("@pactor-app/core");
380
+ var import_components = require("@pactor-app/ui/components");
381
+ var import_interaction = require("@pactor-app/ui/interaction");
382
+ var import_runtime3 = require("@pactor-app/runtime");
383
+ function upsert(registry, name, item) {
384
+ if (registry.has(name)) {
385
+ registry.unregister(name);
386
+ }
387
+ registry.register(name, item);
388
+ }
389
+ function upsertAll(registry, entries) {
390
+ for (const [name, item] of Object.entries(entries ?? {})) {
391
+ upsert(registry, name, item);
392
+ }
393
+ }
394
+ function createRuntimeHttpAdapter(authTokenProvider) {
395
+ const base = (0, import_runtime3.createFetchHttpAdapter)();
396
+ if (authTokenProvider === void 0) {
397
+ return base;
398
+ }
399
+ return {
400
+ async request(config) {
401
+ const token = await authTokenProvider();
402
+ if (token === void 0 || token === "") {
403
+ return base.request(config);
404
+ }
405
+ return base.request({
406
+ ...config,
407
+ headers: { ...config.headers, authorization: `Bearer ${token}` }
408
+ });
409
+ }
410
+ };
411
+ }
412
+ function wrapHttpAdapter(base, hooks) {
413
+ if (!hooks) {
414
+ return base;
415
+ }
416
+ return {
417
+ async request(config) {
418
+ const finalConfig = hooks.request ? await hooks.request(config) : config;
419
+ const data = await base.request(finalConfig);
420
+ return hooks.response ? hooks.response(data) : data;
421
+ }
422
+ };
423
+ }
424
+ function createPactorAssembly(options) {
425
+ const overlayManager = new import_interaction.OverlayManager();
426
+ const uiBridge = (0, import_interaction.createUiBridge)();
427
+ const interactionKit = options.interactionKit ?? options.ui?.interactionKit ?? {
428
+ Provider: import_interaction.ShadcnInteractionProvider,
429
+ OverlayHost: import_interaction.ShadcnOverlayHost
430
+ };
431
+ const components = (options.componentRegistry ?? options.ui?.componentRegistry)?.() ?? (0, import_components.createShadcnComponentRegistry)();
432
+ upsertAll(components, options.components);
433
+ const actions = new import_core2.Registry("ActionRegistry");
434
+ (0, import_interaction.registerInteractionActions)(actions, overlayManager, uiBridge);
435
+ upsert(actions, "navigate", (action) => {
436
+ const to = action.params?.["to"];
437
+ if (typeof to !== "string" || to === "") {
438
+ return {
439
+ ok: false,
440
+ error: new Error("[@pactor-app/app] navigate \u52A8\u4F5C\u7F3A\u5C11 params.to\uFF08string\uFF09")
441
+ };
442
+ }
443
+ options.navigate(to);
444
+ return { ok: true };
445
+ });
446
+ upsertAll(actions, options.actions);
447
+ const services = new import_core2.Registry("ServiceRegistry");
448
+ upsertAll(services, options.services);
449
+ const capabilities = new import_core2.Registry("CapabilityRegistry");
450
+ upsertAll(capabilities, options.capabilities);
451
+ return {
452
+ components,
453
+ actions,
454
+ services,
455
+ capabilities,
456
+ overlayManager,
457
+ uiBridge,
458
+ interactionKit,
459
+ httpAdapter: wrapHttpAdapter(
460
+ createRuntimeHttpAdapter(options.authTokenProvider),
461
+ options.httpAdapter
462
+ )
463
+ };
464
+ }
465
+
377
466
  // src/application.ts
378
467
  var ReactRouterBinding = class {
379
468
  constructor() {
@@ -419,17 +508,6 @@ var ReactRouterBinding = class {
419
508
  function isRecord(value) {
420
509
  return typeof value === "object" && value !== null && !Array.isArray(value);
421
510
  }
422
- function upsert(registry, name, item) {
423
- if (registry.has(name)) {
424
- registry.unregister(name);
425
- }
426
- registry.register(name, item);
427
- }
428
- function upsertAll(registry, entries) {
429
- for (const [name, item] of Object.entries(entries ?? {})) {
430
- upsert(registry, name, item);
431
- }
432
- }
433
511
  function createResourceHttpAdapter(authTokenProvider) {
434
512
  const base = (0, import_resource.createFetchAdapter)();
435
513
  if (authTokenProvider === void 0) {
@@ -446,24 +524,6 @@ function createResourceHttpAdapter(authTokenProvider) {
446
524
  });
447
525
  };
448
526
  }
449
- function createRuntimeHttpAdapter(authTokenProvider) {
450
- const base = (0, import_runtime3.createFetchHttpAdapter)();
451
- if (authTokenProvider === void 0) {
452
- return base;
453
- }
454
- return {
455
- async request(config) {
456
- const token = await authTokenProvider();
457
- if (token === void 0 || token === "") {
458
- return base.request(config);
459
- }
460
- return base.request({
461
- ...config,
462
- headers: { ...config.headers, authorization: `Bearer ${token}` }
463
- });
464
- }
465
- };
466
- }
467
527
  var MENU_TYPES = /* @__PURE__ */ new Set(["group", "page", "link"]);
468
528
  function sanitizeMenus(raw) {
469
529
  if (!Array.isArray(raw)) {
@@ -520,42 +580,29 @@ function createPactorApplication(options) {
520
580
  }
521
581
  const { appId } = options;
522
582
  const routerOptions = options.router ?? {};
523
- const overlayManager = new import_interaction.OverlayManager();
524
- const uiBridge = (0, import_interaction.createUiBridge)();
525
583
  const permissionStore = new import_permission.PermissionStore();
526
584
  const layoutRegistry = (options.layoutRegistry ?? options.ui?.layoutRegistry)?.() ?? (0, import_layout2.createShadcnLayoutRegistry)();
527
- const interactionKit = options.interactionKit ?? options.ui?.interactionKit ?? {
528
- Provider: import_interaction.ShadcnInteractionProvider,
529
- OverlayHost: import_interaction.ShadcnOverlayHost
530
- };
531
585
  const navigation = new ReactRouterBinding();
586
+ const assembly = createPactorAssembly({
587
+ componentRegistry: options.componentRegistry,
588
+ ui: options.ui,
589
+ interactionKit: options.interactionKit,
590
+ components: options.components,
591
+ actions: options.actions,
592
+ services: options.services,
593
+ capabilities: options.capabilities,
594
+ authTokenProvider: options.authTokenProvider,
595
+ httpAdapter: options.httpAdapter,
596
+ navigate: (to) => navigation.push(to)
597
+ });
598
+ const { components, actions, services, capabilities, httpAdapter } = assembly;
599
+ const { overlayManager, uiBridge, interactionKit } = assembly;
532
600
  const loader = options.loader ?? new import_resource.HttpDslResourceLoader({
533
601
  baseUrl: options.api?.baseURL,
534
602
  adapter: createResourceHttpAdapter(options.authTokenProvider)
535
603
  });
536
- const httpAdapter = createRuntimeHttpAdapter(options.authTokenProvider);
537
604
  const permissionAdapter = (0, import_permission.createPermissionAdapter)(permissionStore);
538
605
  const routerAdapter = (0, import_router2.createRouterAdapter)(navigation);
539
- const components = (options.componentRegistry ?? options.ui?.componentRegistry)?.() ?? (0, import_components.createShadcnComponentRegistry)();
540
- upsertAll(components, options.components);
541
- const actions = new import_core2.Registry("ActionRegistry");
542
- (0, import_interaction.registerInteractionActions)(actions, overlayManager, uiBridge);
543
- upsert(actions, "navigate", (action) => {
544
- const to = action.params?.["to"];
545
- if (typeof to !== "string" || to === "") {
546
- return {
547
- ok: false,
548
- error: new Error("[@pactor-app/app] navigate \u52A8\u4F5C\u7F3A\u5C11 params.to\uFF08string\uFF09")
549
- };
550
- }
551
- navigation.push(to);
552
- return { ok: true };
553
- });
554
- upsertAll(actions, options.actions);
555
- const services = new import_core2.Registry("ServiceRegistry");
556
- upsertAll(services, options.services);
557
- const capabilities = new import_core2.Registry("CapabilityRegistry");
558
- upsertAll(capabilities, options.capabilities);
559
606
  let session = null;
560
607
  const handle = {
561
608
  appId,
@@ -606,7 +653,7 @@ function createPactorApplication(options) {
606
653
  return next;
607
654
  },
608
655
  createPageRuntime(pageDsl, route) {
609
- return (0, import_runtime3.createPageRuntime)(pageDsl, {
656
+ return (0, import_runtime4.createPageRuntime)(pageDsl, {
610
657
  components,
611
658
  actions,
612
659
  services,
@@ -670,11 +717,161 @@ function createPactorApplication(options) {
670
717
  }
671
718
  };
672
719
  }
720
+
721
+ // src/embedded.tsx
722
+ var import_core3 = require("@pactor-app/core");
723
+ var import_permission2 = require("@pactor-app/permission");
724
+ var import_runtime5 = require("@pactor-app/runtime");
725
+ var import_react6 = require("react");
726
+ var import_jsx_runtime5 = require("react/jsx-runtime");
727
+ function createPactorPage(options = {}) {
728
+ const assembly = createPactorAssembly({
729
+ ...options,
730
+ // 嵌入模式:navigate 退化为自然跳转(设计决策 D2,不注入宿主路由对象)
731
+ navigate: (to) => {
732
+ if (typeof window !== "undefined") {
733
+ window.location.assign(to);
734
+ }
735
+ }
736
+ });
737
+ let permission;
738
+ if (options.permission !== void 0) {
739
+ permission = options.permission;
740
+ } else if (options.permissions !== void 0) {
741
+ const store = new import_permission2.PermissionStore();
742
+ store.setPermissions(options.permissions);
743
+ permission = (0, import_permission2.createPermissionAdapter)(store);
744
+ } else {
745
+ permission = (0, import_runtime5.createDefaultPermissionAdapter)();
746
+ }
747
+ const i18n = (0, import_core3.createI18nContext)({
748
+ locale: options.locale,
749
+ defaultLocale: options.defaultLocale,
750
+ messages: options.messages
751
+ });
752
+ function createRuntime(pageDsl, route) {
753
+ return (0, import_runtime5.createPageRuntime)(pageDsl, {
754
+ components: assembly.components,
755
+ actions: assembly.actions,
756
+ services: assembly.services,
757
+ capabilities: assembly.capabilities,
758
+ permission,
759
+ http: assembly.httpAdapter,
760
+ route: { params: route?.params ?? {}, query: route?.query ?? {} },
761
+ locale: options.locale ?? "zh-CN",
762
+ defaultLocale: options.defaultLocale ?? "zh-CN",
763
+ messages: options.messages ?? {}
764
+ });
765
+ }
766
+ return function ConfiguredPactorPage(props) {
767
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EmbeddedPage, { assembly, options, i18n, createRuntime, resolvePage: options.loader ? resolvePage : void 0, ...props });
768
+ };
769
+ async function resolvePage(pageId) {
770
+ if (!options.loader) {
771
+ throw new Error("[@pactor-app/app] PactorPage: \u6309 pageId \u52A0\u8F7D\u9700\u8981 options.loader");
772
+ }
773
+ const resource = await options.loader.loadPage(pageId);
774
+ return resource.schema;
775
+ }
776
+ }
777
+ function PactorPage(props) {
778
+ const { pageId, pageDsl, route, ...options } = props;
779
+ const factoryRef = (0, import_react6.useRef)(null);
780
+ if (factoryRef.current === null) {
781
+ factoryRef.current = createPactorPage(options);
782
+ }
783
+ const ConfiguredPage = factoryRef.current;
784
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConfiguredPage, { pageId, pageDsl, route });
785
+ }
786
+ function EmbeddedPage({
787
+ assembly,
788
+ options,
789
+ i18n,
790
+ createRuntime,
791
+ resolvePage,
792
+ pageId,
793
+ pageDsl,
794
+ route
795
+ }) {
796
+ const [state, setState] = (0, import_react6.useState)({ status: "loading" });
797
+ const [retryTick, setRetryTick] = (0, import_react6.useState)(0);
798
+ const onError = options.onError;
799
+ (0, import_react6.useEffect)(() => {
800
+ let cancelled = false;
801
+ let runtime = null;
802
+ void (async () => {
803
+ try {
804
+ let resolved = pageDsl;
805
+ if (resolved === void 0) {
806
+ if (pageId === void 0) {
807
+ throw new Error("[@pactor-app/app] PactorPage: pageId \u4E0E pageDsl \u5FC5\u4F20\u5176\u4E00");
808
+ }
809
+ if (resolvePage === void 0) {
810
+ throw new Error("[@pactor-app/app] PactorPage: \u6309 pageId \u52A0\u8F7D\u9700\u8981 options.loader");
811
+ }
812
+ resolved = await resolvePage(pageId);
813
+ } else if ((0, import_core3.isDslSourceRef)(resolved)) {
814
+ const resolver = new import_runtime5.DslSourceResolver({
815
+ services: assembly.services,
816
+ http: assembly.httpAdapter,
817
+ getScope: () => ({
818
+ route: { params: route?.params ?? {}, query: route?.query ?? {} }
819
+ })
820
+ });
821
+ resolved = await resolver.resolve(resolved);
822
+ }
823
+ if (cancelled) {
824
+ return;
825
+ }
826
+ runtime = createRuntime(resolved, route);
827
+ setState({ status: "ready", runtime });
828
+ void runtime.ready();
829
+ void runtime.show();
830
+ } catch (cause) {
831
+ if (!cancelled) {
832
+ const error = cause instanceof Error ? cause : new Error(String(cause));
833
+ onError?.(error);
834
+ setState({ status: "error", error });
835
+ }
836
+ }
837
+ })();
838
+ return () => {
839
+ cancelled = true;
840
+ if (runtime !== null && !runtime.destroyed) {
841
+ void runtime.hide();
842
+ void runtime.destroy();
843
+ }
844
+ };
845
+ }, [pageId, pageDsl, retryTick]);
846
+ const InteractionProvider = assembly.interactionKit.Provider;
847
+ const OverlayHost = assembly.interactionKit.OverlayHost;
848
+ let content;
849
+ if (state.status === "loading") {
850
+ content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-app-page-loading", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BootstrapSpinner, { style: { minHeight: 0, padding: 48 } }) });
851
+ } else if (state.status === "error") {
852
+ content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
853
+ ResultView,
854
+ {
855
+ className: "pactor-app-page-load-error",
856
+ title: "\u9875\u9762\u52A0\u8F7D\u5931\u8D25",
857
+ subTitle: state.error.message,
858
+ extra: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PrimaryButton, { onClick: () => setRetryTick((n) => n + 1), children: "\u91CD\u8BD5" })
859
+ }
860
+ );
861
+ } else {
862
+ content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_runtime5.PageRuntimeView, { runtime: state.runtime, children: resolvePage !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OverlayHost, { manager: assembly.overlayManager, resolvePage }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OverlayHost, { manager: assembly.overlayManager }) }) });
863
+ }
864
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_runtime5.I18nContextProvider, { value: i18n, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InteractionProvider, { bridge: assembly.uiBridge, children: content }) });
865
+ }
673
866
  // Annotate the CommonJS export names for ESM import in node:
674
867
  0 && (module.exports = {
675
868
  Application,
676
869
  DslPageLoader,
870
+ PactorPage,
677
871
  PageErrorBoundary,
678
872
  ReactRouterBinding,
679
- createPactorApplication
873
+ createPactorApplication,
874
+ createPactorPage,
875
+ createRuntimeHttpAdapter,
876
+ wrapHttpAdapter
680
877
  });
package/dist/index.d.cts CHANGED
@@ -1,13 +1,61 @@
1
- import { I18nMessages, PageDsl, Registry } from '@pactor-app/core';
2
- import { OverlayManager, UiBridge, InteractionProviderProps, OverlayHostProps } from '@pactor-app/ui/interaction';
1
+ import { Registry, I18nMessages, PageDsl, DslSourceRef } from '@pactor-app/core';
2
+ import { InteractionProviderProps, OverlayHostProps, OverlayManager, UiBridge } from '@pactor-app/ui/interaction';
3
3
  import { LayoutRegistry, AdminLayoutConfig } from '@pactor-app/ui/layout';
4
4
  import { PermissionStore } from '@pactor-app/permission';
5
5
  import { BootstrapResource, DslResourceLoader } from '@pactor-app/resource';
6
6
  import { NavigationBridge, NavigationLocation, NavigationListener, MenuRegistry, RouteContext } from '@pactor-app/router';
7
- import { PageRuntime, ActionHandler, ServiceHandler } from '@pactor-app/runtime';
7
+ import { HttpRequestConfig, ActionHandler, ServiceHandler, HttpAdapter, PageRuntime, PermissionAdapter } from '@pactor-app/runtime';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode, Component, ErrorInfo } from 'react';
10
10
 
11
+ /** 创建页面运行时 HTTP 出口适配器:注入 Authorization 头 */
12
+ declare function createRuntimeHttpAdapter(authTokenProvider: AuthTokenProvider | undefined): HttpAdapter;
13
+ /**
14
+ * HTTP 钩子(组装层):对页面运行时所有 HTTP 出口做统一加工。
15
+ * - `request`:发出前加工请求配置(如注入 token、改写 URL、追加公共 headers)
16
+ * - `response`:收到后加工返回数据(如把后端包裹结构 `{ code, data }`
17
+ * 归一化为 Pactor 需要的内容)
18
+ * 两个钩子均可异步;缺省时直通。
19
+ */
20
+ interface PactorHttpHooks {
21
+ request?: (config: HttpRequestConfig) => HttpRequestConfig | Promise<HttpRequestConfig>;
22
+ response?: (data: unknown) => unknown | Promise<unknown>;
23
+ }
24
+ /** 把 PactorHttpHooks 包装到 HttpAdapter 上(无钩子则原样返回) */
25
+ declare function wrapHttpAdapter(base: HttpAdapter, hooks: PactorHttpHooks | undefined): HttpAdapter;
26
+ /**
27
+ * 组装内核选项(spec: embedded-page-view / application-bootstrap):
28
+ * 整应用模式与嵌入模式共享的装配输入。
29
+ */
30
+ interface PactorAssemblyOptions {
31
+ /** 组件注册表工厂(槽位合并:显式选项 > ui 预设 > shadcn 默认) */
32
+ componentRegistry?: () => Registry<unknown>;
33
+ /** UI kit 预设(三槽位) */
34
+ ui?: UiKit;
35
+ /** 交互层组件对(缺省 shadcn 版) */
36
+ interactionKit?: {
37
+ Provider: ComponentType<InteractionProviderProps>;
38
+ OverlayHost: ComponentType<OverlayHostProps>;
39
+ };
40
+ /** 业务组件扩展(同名覆盖内置) */
41
+ components?: Record<string, unknown>;
42
+ /** 业务动作扩展(同名覆盖内置/交互动作) */
43
+ actions?: Record<string, ActionHandler>;
44
+ /** 业务服务扩展 */
45
+ services?: Record<string, ServiceHandler>;
46
+ /** 业务能力扩展 */
47
+ capabilities?: Record<string, unknown>;
48
+ /** 认证令牌:注入页面 HTTP 出口 */
49
+ authTokenProvider?: AuthTokenProvider;
50
+ /** HTTP 钩子:加工页面运行时所有 HTTP 出口(request 改配置 / response 改数据) */
51
+ httpAdapter?: PactorHttpHooks;
52
+ /**
53
+ * DSL `navigate` 动作的跳转实现(两种形态的差异点):
54
+ * 整应用模式 = 导航桥 push(react-router);嵌入模式 = location.assign
55
+ */
56
+ navigate: (to: string) => void;
57
+ }
58
+
11
59
  /** 认证令牌提供者:同步或异步返回 Bearer token(无令牌返回 undefined) */
12
60
  type AuthTokenProvider = () => string | undefined | Promise<string | undefined>;
13
61
  /** 路由配置(V1 支持 browser + basename;hash 预留) */
@@ -89,6 +137,8 @@ interface PactorApplicationOptions {
89
137
  headerExtra?: ReactNode;
90
138
  /** 认证令牌:注入 DSL 资源请求与页面 HTTP 出口(Authorization: Bearer) */
91
139
  authTokenProvider?: AuthTokenProvider;
140
+ /** HTTP 钩子:加工页面运行时所有 HTTP 出口(request 改配置 / response 改数据) */
141
+ httpAdapter?: PactorHttpHooks;
92
142
  /** 当前语言代码(优先级高于 bootstrap.locale 与浏览器语言) */
93
143
  locale?: string;
94
144
  /** 回退语言代码(缺省 'zh-CN') */
@@ -275,4 +325,58 @@ declare class PageErrorBoundary extends Component<PageErrorBoundaryProps, PageEr
275
325
  render(): ReactNode;
276
326
  }
277
327
 
278
- export { Application, type ApplicationProps, type AuthTokenProvider, DslPageLoader, type DslPageLoaderProps, type PactorAppSession, type PactorApplication, type PactorApplicationHandle, type PactorApplicationOptions, type PactorRouterOptions, PageErrorBoundary, ReactRouterBinding, type UiKit, createPactorApplication };
328
+ /**
329
+ * 嵌入式页面 props(spec: embedded-page-view):
330
+ * `pageId`(经 loader 加载)与 `pageDsl`(直接传入或动态引用)二选一。
331
+ */
332
+ interface PactorPageProps {
333
+ /** 页面 id(经 options.loader 加载) */
334
+ pageId?: string;
335
+ /**
336
+ * 直接传入的 PageDsl(不经 loader),或 DslSourceRef 动态引用——
337
+ * 经 service/http 通道远端加载 PageDsl,校验通过后创建运行时;
338
+ * 加载失败走错误态与 onError,重试重新请求
339
+ */
340
+ pageDsl?: PageDsl | DslSourceRef;
341
+ /** 路由作用域注入:DSL 内 `${route.params.x}` / `${route.query.x}` 可读 */
342
+ route?: {
343
+ params?: Record<string, unknown>;
344
+ query?: Record<string, unknown>;
345
+ };
346
+ }
347
+ /**
348
+ * 嵌入式页面工厂选项(设计决策 D1/D2):整应用模式的组装选项子集
349
+ * (无路由、无外壳布局);route 上下文经 props 纯数据注入。
350
+ */
351
+ interface PactorPageOptions extends Omit<PactorAssemblyOptions, 'navigate'> {
352
+ /** 资源加载器(按 pageId 加载页面时需要) */
353
+ loader?: DslResourceLoader;
354
+ /** 权限码列表(缺省全部放行;细粒度控制可传 permission 适配器) */
355
+ permissions?: string[];
356
+ /** 权限适配器(优先于 permissions) */
357
+ permission?: PermissionAdapter;
358
+ /** 认证令牌(注入页面 HTTP 出口) */
359
+ authTokenProvider?: AuthTokenProvider;
360
+ /** 当前语言代码(缺省 'zh-CN') */
361
+ locale?: string;
362
+ /** 回退语言代码(缺省 'zh-CN') */
363
+ defaultLocale?: string;
364
+ /** 翻译消息表 */
365
+ messages?: I18nMessages;
366
+ /** 页面加载/校验失败回调 */
367
+ onError?: (error: Error) => void;
368
+ }
369
+ /**
370
+ * 嵌入式页面工厂(spec: embedded-page-view):返回预配置的 PactorPage 组件。
371
+ * 组装一次(registries / 交互层 / i18n),每个组件实例自持独立
372
+ * PageRuntime(多实例隔离)。
373
+ */
374
+ declare function createPactorPage(options?: PactorPageOptions): ComponentType<PactorPageProps>;
375
+ /**
376
+ * PactorPage 组件(spec: embedded-page-view):嵌入式 DSL 页面。
377
+ * 直接使用时把工厂选项一并作为 props 传入(首次渲染时组装一次);
378
+ * 需要复用同一组装配时优先用 `createPactorPage(options)` 工厂。
379
+ */
380
+ declare function PactorPage(props: PactorPageProps & PactorPageOptions): react.JSX.Element;
381
+
382
+ export { Application, type ApplicationProps, type AuthTokenProvider, DslPageLoader, type DslPageLoaderProps, type PactorAppSession, type PactorApplication, type PactorApplicationHandle, type PactorApplicationOptions, type PactorHttpHooks, PactorPage, type PactorPageOptions, type PactorPageProps, type PactorRouterOptions, PageErrorBoundary, ReactRouterBinding, type UiKit, createPactorApplication, createPactorPage, createRuntimeHttpAdapter, wrapHttpAdapter };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,61 @@
1
- import { I18nMessages, PageDsl, Registry } from '@pactor-app/core';
2
- import { OverlayManager, UiBridge, InteractionProviderProps, OverlayHostProps } from '@pactor-app/ui/interaction';
1
+ import { Registry, I18nMessages, PageDsl, DslSourceRef } from '@pactor-app/core';
2
+ import { InteractionProviderProps, OverlayHostProps, OverlayManager, UiBridge } from '@pactor-app/ui/interaction';
3
3
  import { LayoutRegistry, AdminLayoutConfig } from '@pactor-app/ui/layout';
4
4
  import { PermissionStore } from '@pactor-app/permission';
5
5
  import { BootstrapResource, DslResourceLoader } from '@pactor-app/resource';
6
6
  import { NavigationBridge, NavigationLocation, NavigationListener, MenuRegistry, RouteContext } from '@pactor-app/router';
7
- import { PageRuntime, ActionHandler, ServiceHandler } from '@pactor-app/runtime';
7
+ import { HttpRequestConfig, ActionHandler, ServiceHandler, HttpAdapter, PageRuntime, PermissionAdapter } from '@pactor-app/runtime';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode, Component, ErrorInfo } from 'react';
10
10
 
11
+ /** 创建页面运行时 HTTP 出口适配器:注入 Authorization 头 */
12
+ declare function createRuntimeHttpAdapter(authTokenProvider: AuthTokenProvider | undefined): HttpAdapter;
13
+ /**
14
+ * HTTP 钩子(组装层):对页面运行时所有 HTTP 出口做统一加工。
15
+ * - `request`:发出前加工请求配置(如注入 token、改写 URL、追加公共 headers)
16
+ * - `response`:收到后加工返回数据(如把后端包裹结构 `{ code, data }`
17
+ * 归一化为 Pactor 需要的内容)
18
+ * 两个钩子均可异步;缺省时直通。
19
+ */
20
+ interface PactorHttpHooks {
21
+ request?: (config: HttpRequestConfig) => HttpRequestConfig | Promise<HttpRequestConfig>;
22
+ response?: (data: unknown) => unknown | Promise<unknown>;
23
+ }
24
+ /** 把 PactorHttpHooks 包装到 HttpAdapter 上(无钩子则原样返回) */
25
+ declare function wrapHttpAdapter(base: HttpAdapter, hooks: PactorHttpHooks | undefined): HttpAdapter;
26
+ /**
27
+ * 组装内核选项(spec: embedded-page-view / application-bootstrap):
28
+ * 整应用模式与嵌入模式共享的装配输入。
29
+ */
30
+ interface PactorAssemblyOptions {
31
+ /** 组件注册表工厂(槽位合并:显式选项 > ui 预设 > shadcn 默认) */
32
+ componentRegistry?: () => Registry<unknown>;
33
+ /** UI kit 预设(三槽位) */
34
+ ui?: UiKit;
35
+ /** 交互层组件对(缺省 shadcn 版) */
36
+ interactionKit?: {
37
+ Provider: ComponentType<InteractionProviderProps>;
38
+ OverlayHost: ComponentType<OverlayHostProps>;
39
+ };
40
+ /** 业务组件扩展(同名覆盖内置) */
41
+ components?: Record<string, unknown>;
42
+ /** 业务动作扩展(同名覆盖内置/交互动作) */
43
+ actions?: Record<string, ActionHandler>;
44
+ /** 业务服务扩展 */
45
+ services?: Record<string, ServiceHandler>;
46
+ /** 业务能力扩展 */
47
+ capabilities?: Record<string, unknown>;
48
+ /** 认证令牌:注入页面 HTTP 出口 */
49
+ authTokenProvider?: AuthTokenProvider;
50
+ /** HTTP 钩子:加工页面运行时所有 HTTP 出口(request 改配置 / response 改数据) */
51
+ httpAdapter?: PactorHttpHooks;
52
+ /**
53
+ * DSL `navigate` 动作的跳转实现(两种形态的差异点):
54
+ * 整应用模式 = 导航桥 push(react-router);嵌入模式 = location.assign
55
+ */
56
+ navigate: (to: string) => void;
57
+ }
58
+
11
59
  /** 认证令牌提供者:同步或异步返回 Bearer token(无令牌返回 undefined) */
12
60
  type AuthTokenProvider = () => string | undefined | Promise<string | undefined>;
13
61
  /** 路由配置(V1 支持 browser + basename;hash 预留) */
@@ -89,6 +137,8 @@ interface PactorApplicationOptions {
89
137
  headerExtra?: ReactNode;
90
138
  /** 认证令牌:注入 DSL 资源请求与页面 HTTP 出口(Authorization: Bearer) */
91
139
  authTokenProvider?: AuthTokenProvider;
140
+ /** HTTP 钩子:加工页面运行时所有 HTTP 出口(request 改配置 / response 改数据) */
141
+ httpAdapter?: PactorHttpHooks;
92
142
  /** 当前语言代码(优先级高于 bootstrap.locale 与浏览器语言) */
93
143
  locale?: string;
94
144
  /** 回退语言代码(缺省 'zh-CN') */
@@ -275,4 +325,58 @@ declare class PageErrorBoundary extends Component<PageErrorBoundaryProps, PageEr
275
325
  render(): ReactNode;
276
326
  }
277
327
 
278
- export { Application, type ApplicationProps, type AuthTokenProvider, DslPageLoader, type DslPageLoaderProps, type PactorAppSession, type PactorApplication, type PactorApplicationHandle, type PactorApplicationOptions, type PactorRouterOptions, PageErrorBoundary, ReactRouterBinding, type UiKit, createPactorApplication };
328
+ /**
329
+ * 嵌入式页面 props(spec: embedded-page-view):
330
+ * `pageId`(经 loader 加载)与 `pageDsl`(直接传入或动态引用)二选一。
331
+ */
332
+ interface PactorPageProps {
333
+ /** 页面 id(经 options.loader 加载) */
334
+ pageId?: string;
335
+ /**
336
+ * 直接传入的 PageDsl(不经 loader),或 DslSourceRef 动态引用——
337
+ * 经 service/http 通道远端加载 PageDsl,校验通过后创建运行时;
338
+ * 加载失败走错误态与 onError,重试重新请求
339
+ */
340
+ pageDsl?: PageDsl | DslSourceRef;
341
+ /** 路由作用域注入:DSL 内 `${route.params.x}` / `${route.query.x}` 可读 */
342
+ route?: {
343
+ params?: Record<string, unknown>;
344
+ query?: Record<string, unknown>;
345
+ };
346
+ }
347
+ /**
348
+ * 嵌入式页面工厂选项(设计决策 D1/D2):整应用模式的组装选项子集
349
+ * (无路由、无外壳布局);route 上下文经 props 纯数据注入。
350
+ */
351
+ interface PactorPageOptions extends Omit<PactorAssemblyOptions, 'navigate'> {
352
+ /** 资源加载器(按 pageId 加载页面时需要) */
353
+ loader?: DslResourceLoader;
354
+ /** 权限码列表(缺省全部放行;细粒度控制可传 permission 适配器) */
355
+ permissions?: string[];
356
+ /** 权限适配器(优先于 permissions) */
357
+ permission?: PermissionAdapter;
358
+ /** 认证令牌(注入页面 HTTP 出口) */
359
+ authTokenProvider?: AuthTokenProvider;
360
+ /** 当前语言代码(缺省 'zh-CN') */
361
+ locale?: string;
362
+ /** 回退语言代码(缺省 'zh-CN') */
363
+ defaultLocale?: string;
364
+ /** 翻译消息表 */
365
+ messages?: I18nMessages;
366
+ /** 页面加载/校验失败回调 */
367
+ onError?: (error: Error) => void;
368
+ }
369
+ /**
370
+ * 嵌入式页面工厂(spec: embedded-page-view):返回预配置的 PactorPage 组件。
371
+ * 组装一次(registries / 交互层 / i18n),每个组件实例自持独立
372
+ * PageRuntime(多实例隔离)。
373
+ */
374
+ declare function createPactorPage(options?: PactorPageOptions): ComponentType<PactorPageProps>;
375
+ /**
376
+ * PactorPage 组件(spec: embedded-page-view):嵌入式 DSL 页面。
377
+ * 直接使用时把工厂选项一并作为 props 传入(首次渲染时组装一次);
378
+ * 需要复用同一组装配时优先用 `createPactorPage(options)` 工厂。
379
+ */
380
+ declare function PactorPage(props: PactorPageProps & PactorPageOptions): react.JSX.Element;
381
+
382
+ export { Application, type ApplicationProps, type AuthTokenProvider, DslPageLoader, type DslPageLoaderProps, type PactorAppSession, type PactorApplication, type PactorApplicationHandle, type PactorApplicationOptions, type PactorHttpHooks, PactorPage, type PactorPageOptions, type PactorPageProps, type PactorRouterOptions, PageErrorBoundary, ReactRouterBinding, type UiKit, createPactorApplication, createPactorPage, createRuntimeHttpAdapter, wrapHttpAdapter };
package/dist/index.js CHANGED
@@ -1,13 +1,4 @@
1
1
  // src/application.ts
2
- import { Registry } from "@pactor-app/core";
3
- import { createShadcnComponentRegistry } from "@pactor-app/ui/components";
4
- import {
5
- createUiBridge,
6
- OverlayManager,
7
- registerInteractionActions,
8
- ShadcnInteractionProvider,
9
- ShadcnOverlayHost
10
- } from "@pactor-app/ui/interaction";
11
2
  import {
12
3
  createShadcnLayoutRegistry
13
4
  } from "@pactor-app/ui/layout";
@@ -21,7 +12,6 @@ import {
21
12
  MenuRegistry
22
13
  } from "@pactor-app/router";
23
14
  import {
24
- createFetchHttpAdapter,
25
15
  createPageRuntime
26
16
  } from "@pactor-app/runtime";
27
17
  import { createElement } from "react";
@@ -367,6 +357,102 @@ function ForbiddenPage() {
367
357
  );
368
358
  }
369
359
 
360
+ // src/assembly.ts
361
+ import { Registry } from "@pactor-app/core";
362
+ import { createShadcnComponentRegistry } from "@pactor-app/ui/components";
363
+ import {
364
+ createUiBridge,
365
+ OverlayManager,
366
+ registerInteractionActions,
367
+ ShadcnInteractionProvider,
368
+ ShadcnOverlayHost
369
+ } from "@pactor-app/ui/interaction";
370
+ import {
371
+ createFetchHttpAdapter
372
+ } from "@pactor-app/runtime";
373
+ function upsert(registry, name, item) {
374
+ if (registry.has(name)) {
375
+ registry.unregister(name);
376
+ }
377
+ registry.register(name, item);
378
+ }
379
+ function upsertAll(registry, entries) {
380
+ for (const [name, item] of Object.entries(entries ?? {})) {
381
+ upsert(registry, name, item);
382
+ }
383
+ }
384
+ function createRuntimeHttpAdapter(authTokenProvider) {
385
+ const base = createFetchHttpAdapter();
386
+ if (authTokenProvider === void 0) {
387
+ return base;
388
+ }
389
+ return {
390
+ async request(config) {
391
+ const token = await authTokenProvider();
392
+ if (token === void 0 || token === "") {
393
+ return base.request(config);
394
+ }
395
+ return base.request({
396
+ ...config,
397
+ headers: { ...config.headers, authorization: `Bearer ${token}` }
398
+ });
399
+ }
400
+ };
401
+ }
402
+ function wrapHttpAdapter(base, hooks) {
403
+ if (!hooks) {
404
+ return base;
405
+ }
406
+ return {
407
+ async request(config) {
408
+ const finalConfig = hooks.request ? await hooks.request(config) : config;
409
+ const data = await base.request(finalConfig);
410
+ return hooks.response ? hooks.response(data) : data;
411
+ }
412
+ };
413
+ }
414
+ function createPactorAssembly(options) {
415
+ const overlayManager = new OverlayManager();
416
+ const uiBridge = createUiBridge();
417
+ const interactionKit = options.interactionKit ?? options.ui?.interactionKit ?? {
418
+ Provider: ShadcnInteractionProvider,
419
+ OverlayHost: ShadcnOverlayHost
420
+ };
421
+ const components = (options.componentRegistry ?? options.ui?.componentRegistry)?.() ?? createShadcnComponentRegistry();
422
+ upsertAll(components, options.components);
423
+ const actions = new Registry("ActionRegistry");
424
+ registerInteractionActions(actions, overlayManager, uiBridge);
425
+ upsert(actions, "navigate", (action) => {
426
+ const to = action.params?.["to"];
427
+ if (typeof to !== "string" || to === "") {
428
+ return {
429
+ ok: false,
430
+ error: new Error("[@pactor-app/app] navigate \u52A8\u4F5C\u7F3A\u5C11 params.to\uFF08string\uFF09")
431
+ };
432
+ }
433
+ options.navigate(to);
434
+ return { ok: true };
435
+ });
436
+ upsertAll(actions, options.actions);
437
+ const services = new Registry("ServiceRegistry");
438
+ upsertAll(services, options.services);
439
+ const capabilities = new Registry("CapabilityRegistry");
440
+ upsertAll(capabilities, options.capabilities);
441
+ return {
442
+ components,
443
+ actions,
444
+ services,
445
+ capabilities,
446
+ overlayManager,
447
+ uiBridge,
448
+ interactionKit,
449
+ httpAdapter: wrapHttpAdapter(
450
+ createRuntimeHttpAdapter(options.authTokenProvider),
451
+ options.httpAdapter
452
+ )
453
+ };
454
+ }
455
+
370
456
  // src/application.ts
371
457
  var ReactRouterBinding = class {
372
458
  constructor() {
@@ -412,17 +498,6 @@ var ReactRouterBinding = class {
412
498
  function isRecord(value) {
413
499
  return typeof value === "object" && value !== null && !Array.isArray(value);
414
500
  }
415
- function upsert(registry, name, item) {
416
- if (registry.has(name)) {
417
- registry.unregister(name);
418
- }
419
- registry.register(name, item);
420
- }
421
- function upsertAll(registry, entries) {
422
- for (const [name, item] of Object.entries(entries ?? {})) {
423
- upsert(registry, name, item);
424
- }
425
- }
426
501
  function createResourceHttpAdapter(authTokenProvider) {
427
502
  const base = createFetchAdapter();
428
503
  if (authTokenProvider === void 0) {
@@ -439,24 +514,6 @@ function createResourceHttpAdapter(authTokenProvider) {
439
514
  });
440
515
  };
441
516
  }
442
- function createRuntimeHttpAdapter(authTokenProvider) {
443
- const base = createFetchHttpAdapter();
444
- if (authTokenProvider === void 0) {
445
- return base;
446
- }
447
- return {
448
- async request(config) {
449
- const token = await authTokenProvider();
450
- if (token === void 0 || token === "") {
451
- return base.request(config);
452
- }
453
- return base.request({
454
- ...config,
455
- headers: { ...config.headers, authorization: `Bearer ${token}` }
456
- });
457
- }
458
- };
459
- }
460
517
  var MENU_TYPES = /* @__PURE__ */ new Set(["group", "page", "link"]);
461
518
  function sanitizeMenus(raw) {
462
519
  if (!Array.isArray(raw)) {
@@ -513,42 +570,29 @@ function createPactorApplication(options) {
513
570
  }
514
571
  const { appId } = options;
515
572
  const routerOptions = options.router ?? {};
516
- const overlayManager = new OverlayManager();
517
- const uiBridge = createUiBridge();
518
573
  const permissionStore = new PermissionStore();
519
574
  const layoutRegistry = (options.layoutRegistry ?? options.ui?.layoutRegistry)?.() ?? createShadcnLayoutRegistry();
520
- const interactionKit = options.interactionKit ?? options.ui?.interactionKit ?? {
521
- Provider: ShadcnInteractionProvider,
522
- OverlayHost: ShadcnOverlayHost
523
- };
524
575
  const navigation = new ReactRouterBinding();
576
+ const assembly = createPactorAssembly({
577
+ componentRegistry: options.componentRegistry,
578
+ ui: options.ui,
579
+ interactionKit: options.interactionKit,
580
+ components: options.components,
581
+ actions: options.actions,
582
+ services: options.services,
583
+ capabilities: options.capabilities,
584
+ authTokenProvider: options.authTokenProvider,
585
+ httpAdapter: options.httpAdapter,
586
+ navigate: (to) => navigation.push(to)
587
+ });
588
+ const { components, actions, services, capabilities, httpAdapter } = assembly;
589
+ const { overlayManager, uiBridge, interactionKit } = assembly;
525
590
  const loader = options.loader ?? new HttpDslResourceLoader({
526
591
  baseUrl: options.api?.baseURL,
527
592
  adapter: createResourceHttpAdapter(options.authTokenProvider)
528
593
  });
529
- const httpAdapter = createRuntimeHttpAdapter(options.authTokenProvider);
530
594
  const permissionAdapter = createPermissionAdapter(permissionStore);
531
595
  const routerAdapter = createRouterAdapter(navigation);
532
- const components = (options.componentRegistry ?? options.ui?.componentRegistry)?.() ?? createShadcnComponentRegistry();
533
- upsertAll(components, options.components);
534
- const actions = new Registry("ActionRegistry");
535
- registerInteractionActions(actions, overlayManager, uiBridge);
536
- upsert(actions, "navigate", (action) => {
537
- const to = action.params?.["to"];
538
- if (typeof to !== "string" || to === "") {
539
- return {
540
- ok: false,
541
- error: new Error("[@pactor-app/app] navigate \u52A8\u4F5C\u7F3A\u5C11 params.to\uFF08string\uFF09")
542
- };
543
- }
544
- navigation.push(to);
545
- return { ok: true };
546
- });
547
- upsertAll(actions, options.actions);
548
- const services = new Registry("ServiceRegistry");
549
- upsertAll(services, options.services);
550
- const capabilities = new Registry("CapabilityRegistry");
551
- upsertAll(capabilities, options.capabilities);
552
596
  let session = null;
553
597
  const handle = {
554
598
  appId,
@@ -663,10 +707,170 @@ function createPactorApplication(options) {
663
707
  }
664
708
  };
665
709
  }
710
+
711
+ // src/embedded.tsx
712
+ import { createI18nContext as createI18nContext2, isDslSourceRef } from "@pactor-app/core";
713
+ import { createPermissionAdapter as createPermissionAdapter2, PermissionStore as PermissionStore2 } from "@pactor-app/permission";
714
+ import {
715
+ createDefaultPermissionAdapter,
716
+ createPageRuntime as createPageRuntime2,
717
+ DslSourceResolver,
718
+ I18nContextProvider as I18nContextProvider2,
719
+ PageRuntimeView as PageRuntimeView2
720
+ } from "@pactor-app/runtime";
721
+ import {
722
+ useEffect as useEffect3,
723
+ useRef,
724
+ useState as useState3
725
+ } from "react";
726
+ import { jsx as jsx5 } from "react/jsx-runtime";
727
+ function createPactorPage(options = {}) {
728
+ const assembly = createPactorAssembly({
729
+ ...options,
730
+ // 嵌入模式:navigate 退化为自然跳转(设计决策 D2,不注入宿主路由对象)
731
+ navigate: (to) => {
732
+ if (typeof window !== "undefined") {
733
+ window.location.assign(to);
734
+ }
735
+ }
736
+ });
737
+ let permission;
738
+ if (options.permission !== void 0) {
739
+ permission = options.permission;
740
+ } else if (options.permissions !== void 0) {
741
+ const store = new PermissionStore2();
742
+ store.setPermissions(options.permissions);
743
+ permission = createPermissionAdapter2(store);
744
+ } else {
745
+ permission = createDefaultPermissionAdapter();
746
+ }
747
+ const i18n = createI18nContext2({
748
+ locale: options.locale,
749
+ defaultLocale: options.defaultLocale,
750
+ messages: options.messages
751
+ });
752
+ function createRuntime(pageDsl, route) {
753
+ return createPageRuntime2(pageDsl, {
754
+ components: assembly.components,
755
+ actions: assembly.actions,
756
+ services: assembly.services,
757
+ capabilities: assembly.capabilities,
758
+ permission,
759
+ http: assembly.httpAdapter,
760
+ route: { params: route?.params ?? {}, query: route?.query ?? {} },
761
+ locale: options.locale ?? "zh-CN",
762
+ defaultLocale: options.defaultLocale ?? "zh-CN",
763
+ messages: options.messages ?? {}
764
+ });
765
+ }
766
+ return function ConfiguredPactorPage(props) {
767
+ return /* @__PURE__ */ jsx5(EmbeddedPage, { assembly, options, i18n, createRuntime, resolvePage: options.loader ? resolvePage : void 0, ...props });
768
+ };
769
+ async function resolvePage(pageId) {
770
+ if (!options.loader) {
771
+ throw new Error("[@pactor-app/app] PactorPage: \u6309 pageId \u52A0\u8F7D\u9700\u8981 options.loader");
772
+ }
773
+ const resource = await options.loader.loadPage(pageId);
774
+ return resource.schema;
775
+ }
776
+ }
777
+ function PactorPage(props) {
778
+ const { pageId, pageDsl, route, ...options } = props;
779
+ const factoryRef = useRef(null);
780
+ if (factoryRef.current === null) {
781
+ factoryRef.current = createPactorPage(options);
782
+ }
783
+ const ConfiguredPage = factoryRef.current;
784
+ return /* @__PURE__ */ jsx5(ConfiguredPage, { pageId, pageDsl, route });
785
+ }
786
+ function EmbeddedPage({
787
+ assembly,
788
+ options,
789
+ i18n,
790
+ createRuntime,
791
+ resolvePage,
792
+ pageId,
793
+ pageDsl,
794
+ route
795
+ }) {
796
+ const [state, setState] = useState3({ status: "loading" });
797
+ const [retryTick, setRetryTick] = useState3(0);
798
+ const onError = options.onError;
799
+ useEffect3(() => {
800
+ let cancelled = false;
801
+ let runtime = null;
802
+ void (async () => {
803
+ try {
804
+ let resolved = pageDsl;
805
+ if (resolved === void 0) {
806
+ if (pageId === void 0) {
807
+ throw new Error("[@pactor-app/app] PactorPage: pageId \u4E0E pageDsl \u5FC5\u4F20\u5176\u4E00");
808
+ }
809
+ if (resolvePage === void 0) {
810
+ throw new Error("[@pactor-app/app] PactorPage: \u6309 pageId \u52A0\u8F7D\u9700\u8981 options.loader");
811
+ }
812
+ resolved = await resolvePage(pageId);
813
+ } else if (isDslSourceRef(resolved)) {
814
+ const resolver = new DslSourceResolver({
815
+ services: assembly.services,
816
+ http: assembly.httpAdapter,
817
+ getScope: () => ({
818
+ route: { params: route?.params ?? {}, query: route?.query ?? {} }
819
+ })
820
+ });
821
+ resolved = await resolver.resolve(resolved);
822
+ }
823
+ if (cancelled) {
824
+ return;
825
+ }
826
+ runtime = createRuntime(resolved, route);
827
+ setState({ status: "ready", runtime });
828
+ void runtime.ready();
829
+ void runtime.show();
830
+ } catch (cause) {
831
+ if (!cancelled) {
832
+ const error = cause instanceof Error ? cause : new Error(String(cause));
833
+ onError?.(error);
834
+ setState({ status: "error", error });
835
+ }
836
+ }
837
+ })();
838
+ return () => {
839
+ cancelled = true;
840
+ if (runtime !== null && !runtime.destroyed) {
841
+ void runtime.hide();
842
+ void runtime.destroy();
843
+ }
844
+ };
845
+ }, [pageId, pageDsl, retryTick]);
846
+ const InteractionProvider = assembly.interactionKit.Provider;
847
+ const OverlayHost = assembly.interactionKit.OverlayHost;
848
+ let content;
849
+ if (state.status === "loading") {
850
+ content = /* @__PURE__ */ jsx5("div", { className: "pactor-app-page-loading", children: /* @__PURE__ */ jsx5(BootstrapSpinner, { style: { minHeight: 0, padding: 48 } }) });
851
+ } else if (state.status === "error") {
852
+ content = /* @__PURE__ */ jsx5(
853
+ ResultView,
854
+ {
855
+ className: "pactor-app-page-load-error",
856
+ title: "\u9875\u9762\u52A0\u8F7D\u5931\u8D25",
857
+ subTitle: state.error.message,
858
+ extra: /* @__PURE__ */ jsx5(PrimaryButton, { onClick: () => setRetryTick((n) => n + 1), children: "\u91CD\u8BD5" })
859
+ }
860
+ );
861
+ } else {
862
+ content = /* @__PURE__ */ jsx5(PageErrorBoundary, { children: /* @__PURE__ */ jsx5(PageRuntimeView2, { runtime: state.runtime, children: resolvePage !== void 0 ? /* @__PURE__ */ jsx5(OverlayHost, { manager: assembly.overlayManager, resolvePage }) : /* @__PURE__ */ jsx5(OverlayHost, { manager: assembly.overlayManager }) }) });
863
+ }
864
+ return /* @__PURE__ */ jsx5(I18nContextProvider2, { value: i18n, children: /* @__PURE__ */ jsx5(InteractionProvider, { bridge: assembly.uiBridge, children: content }) });
865
+ }
666
866
  export {
667
867
  Application,
668
868
  DslPageLoader,
869
+ PactorPage,
669
870
  PageErrorBoundary,
670
871
  ReactRouterBinding,
671
- createPactorApplication
872
+ createPactorApplication,
873
+ createPactorPage,
874
+ createRuntimeHttpAdapter,
875
+ wrapHttpAdapter
672
876
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pactor-app/app",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "Pactor application: one-stop composition layer (createPactorApplication) wiring loader, bootstrap, registries, router, layout, interaction and page runtimes",
5
5
  "keywords": [
6
6
  "pactor",
@@ -46,14 +46,14 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@pactor-app/core": "0.1.0",
50
- "@pactor-app/expression": "0.1.0",
51
- "@pactor-app/resource": "0.1.0",
52
- "@pactor-app/router": "0.1.0",
53
- "@pactor-app/schema": "0.1.0",
54
- "@pactor-app/ui": "0.1.0",
55
- "@pactor-app/runtime": "0.1.0",
56
- "@pactor-app/permission": "0.1.0"
49
+ "@pactor-app/core": "1.1.0",
50
+ "@pactor-app/expression": "1.1.0",
51
+ "@pactor-app/runtime": "1.1.0",
52
+ "@pactor-app/schema": "1.1.0",
53
+ "@pactor-app/permission": "1.1.0",
54
+ "@pactor-app/resource": "1.1.0",
55
+ "@pactor-app/ui": "1.1.0",
56
+ "@pactor-app/router": "1.1.0"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "react": ">=18",