@pactor-app/app 0.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/dist/index.js ADDED
@@ -0,0 +1,672 @@
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
+ import {
12
+ createShadcnLayoutRegistry
13
+ } from "@pactor-app/ui/layout";
14
+ import { createPermissionAdapter, PermissionStore } from "@pactor-app/permission";
15
+ import {
16
+ createFetchAdapter,
17
+ HttpDslResourceLoader
18
+ } from "@pactor-app/resource";
19
+ import {
20
+ createRouterAdapter,
21
+ MenuRegistry
22
+ } from "@pactor-app/router";
23
+ import {
24
+ createFetchHttpAdapter,
25
+ createPageRuntime
26
+ } from "@pactor-app/runtime";
27
+ import { createElement } from "react";
28
+ import { createRoot } from "react-dom/client";
29
+
30
+ // src/app-shell.tsx
31
+ import { createI18nContext } from "@pactor-app/core";
32
+ import { resolveLayout } from "@pactor-app/ui/layout";
33
+ import { resolveRoute } from "@pactor-app/router";
34
+ import { DynamicDslRoute, useReactRouterBridge } from "@pactor-app/router/react";
35
+ import { I18nContextProvider } from "@pactor-app/runtime";
36
+ import { useEffect as useEffect2, useState as useState2 } from "react";
37
+ import {
38
+ BrowserRouter,
39
+ HashRouter,
40
+ Route,
41
+ Routes,
42
+ useLocation
43
+ } from "react-router-dom";
44
+
45
+ // src/feedback.tsx
46
+ import { jsx, jsxs } from "react/jsx-runtime";
47
+ var centerStyle = {
48
+ minHeight: "100vh",
49
+ display: "flex",
50
+ alignItems: "center",
51
+ justifyContent: "center"
52
+ };
53
+ function BootstrapSpinner({
54
+ style
55
+ }) {
56
+ return /* @__PURE__ */ jsx("div", { className: "pactor-app-spin", style: { ...centerStyle, ...style }, children: /* @__PURE__ */ jsx(
57
+ "svg",
58
+ {
59
+ width: "32",
60
+ height: "32",
61
+ viewBox: "0 0 32 32",
62
+ role: "status",
63
+ "aria-label": "\u52A0\u8F7D\u4E2D",
64
+ children: /* @__PURE__ */ jsx(
65
+ "circle",
66
+ {
67
+ cx: "16",
68
+ cy: "16",
69
+ r: "12",
70
+ fill: "none",
71
+ stroke: "#1677ff",
72
+ strokeWidth: "3",
73
+ strokeLinecap: "round",
74
+ strokeDasharray: "56",
75
+ strokeDashoffset: "28",
76
+ children: /* @__PURE__ */ jsx(
77
+ "animateTransform",
78
+ {
79
+ attributeName: "transform",
80
+ type: "rotate",
81
+ from: "0 16 16",
82
+ to: "360 16 16",
83
+ dur: "0.8s",
84
+ repeatCount: "indefinite"
85
+ }
86
+ )
87
+ }
88
+ )
89
+ }
90
+ ) });
91
+ }
92
+ function ResultView({
93
+ className,
94
+ title,
95
+ subTitle,
96
+ extra
97
+ }) {
98
+ return /* @__PURE__ */ jsx("div", { className, style: { ...centerStyle, padding: 24 }, children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", maxWidth: 480 }, children: [
99
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 24, fontWeight: 600, marginBottom: 8 }, children: title }),
100
+ subTitle !== void 0 && /* @__PURE__ */ jsx("div", { style: { color: "rgba(0, 0, 0, 0.45)", marginBottom: 16 }, children: subTitle }),
101
+ extra
102
+ ] }) });
103
+ }
104
+ function PrimaryButton({
105
+ onClick,
106
+ children
107
+ }) {
108
+ return /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ onClick: () => onClick?.(),
113
+ style: {
114
+ padding: "6px 16px",
115
+ border: "none",
116
+ borderRadius: 6,
117
+ background: "#1677ff",
118
+ color: "#fff",
119
+ fontSize: 14,
120
+ cursor: "pointer"
121
+ },
122
+ children
123
+ }
124
+ );
125
+ }
126
+
127
+ // src/page-loader.tsx
128
+ import { PageRuntimeView } from "@pactor-app/runtime";
129
+ import { useEffect, useState } from "react";
130
+
131
+ // src/error-boundary.tsx
132
+ import { Component } from "react";
133
+ import { jsx as jsx2 } from "react/jsx-runtime";
134
+ var PageErrorBoundary = class extends Component {
135
+ constructor() {
136
+ super(...arguments);
137
+ this.state = { error: null };
138
+ }
139
+ static getDerivedStateFromError(error) {
140
+ return { error };
141
+ }
142
+ componentDidCatch(error, info) {
143
+ console.error("[@pactor-app/app] \u9875\u9762\u6E32\u67D3\u5F02\u5E38", error, info.componentStack);
144
+ }
145
+ render() {
146
+ const { error } = this.state;
147
+ if (error !== null) {
148
+ return /* @__PURE__ */ jsx2(
149
+ ResultView,
150
+ {
151
+ className: "pactor-app-page-render-error",
152
+ title: "\u9875\u9762\u6E32\u67D3\u5F02\u5E38",
153
+ subTitle: error.message,
154
+ extra: /* @__PURE__ */ jsx2(PrimaryButton, { onClick: () => this.setState({ error: null }), children: "\u91CD\u8BD5" })
155
+ }
156
+ );
157
+ }
158
+ return this.props.children;
159
+ }
160
+ };
161
+
162
+ // src/page-loader.tsx
163
+ import { jsx as jsx3 } from "react/jsx-runtime";
164
+ function DslPageLoader({ app, pageId, route }) {
165
+ const [state, setState] = useState({ status: "loading" });
166
+ const [retryTick, setRetryTick] = useState(0);
167
+ useEffect(() => {
168
+ let cancelled = false;
169
+ let runtime = null;
170
+ void (async () => {
171
+ try {
172
+ const pageDsl = await app.resolvePage(pageId);
173
+ if (cancelled) {
174
+ return;
175
+ }
176
+ runtime = app.createPageRuntime(pageDsl, route);
177
+ setState({ status: "ready", runtime });
178
+ void runtime.ready();
179
+ void runtime.show();
180
+ } catch (cause) {
181
+ if (!cancelled) {
182
+ setState({
183
+ status: "error",
184
+ error: cause instanceof Error ? cause : new Error(String(cause))
185
+ });
186
+ }
187
+ }
188
+ })();
189
+ return () => {
190
+ cancelled = true;
191
+ if (runtime !== null && !runtime.destroyed) {
192
+ void runtime.hide();
193
+ void runtime.destroy();
194
+ }
195
+ };
196
+ }, [app, pageId, retryTick]);
197
+ if (state.status === "loading") {
198
+ return /* @__PURE__ */ jsx3("div", { className: "pactor-app-page-loading", children: /* @__PURE__ */ jsx3(BootstrapSpinner, { style: { minHeight: 0, padding: 48 } }) });
199
+ }
200
+ if (state.status === "error") {
201
+ return /* @__PURE__ */ jsx3(
202
+ ResultView,
203
+ {
204
+ className: "pactor-app-page-load-error",
205
+ title: "\u9875\u9762\u52A0\u8F7D\u5931\u8D25",
206
+ subTitle: state.error.message,
207
+ extra: /* @__PURE__ */ jsx3(PrimaryButton, { onClick: () => setRetryTick((n) => n + 1), children: "\u91CD\u8BD5" })
208
+ }
209
+ );
210
+ }
211
+ const OverlayHostView = app.interactionKit.OverlayHost;
212
+ return /* @__PURE__ */ jsx3(PageErrorBoundary, { children: /* @__PURE__ */ jsx3(PageRuntimeView, { runtime: state.runtime, children: /* @__PURE__ */ jsx3(
213
+ OverlayHostView,
214
+ {
215
+ manager: app.overlayManager,
216
+ resolvePage: app.resolvePage
217
+ }
218
+ ) }) });
219
+ }
220
+
221
+ // src/app-shell.tsx
222
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
223
+ function Application({ app }) {
224
+ const [state, setState] = useState2({ status: "loading" });
225
+ const [retryTick, setRetryTick] = useState2(0);
226
+ useEffect2(() => {
227
+ let cancelled = false;
228
+ void app.loadSession().then(
229
+ (session) => {
230
+ if (!cancelled) {
231
+ setState({ status: "ready", session });
232
+ }
233
+ },
234
+ (cause) => {
235
+ if (!cancelled) {
236
+ setState({
237
+ status: "error",
238
+ error: cause instanceof Error ? cause : new Error(String(cause))
239
+ });
240
+ }
241
+ }
242
+ );
243
+ return () => {
244
+ cancelled = true;
245
+ };
246
+ }, [app, retryTick]);
247
+ if (state.status === "loading") {
248
+ return /* @__PURE__ */ jsx4("div", { className: "pactor-app-bootstrap-loading", children: /* @__PURE__ */ jsx4(BootstrapSpinner, {}) });
249
+ }
250
+ if (state.status === "error") {
251
+ return /* @__PURE__ */ jsx4(
252
+ ResultView,
253
+ {
254
+ className: "pactor-app-bootstrap-error",
255
+ title: "\u5E94\u7528\u542F\u52A8\u5931\u8D25",
256
+ subTitle: state.error.message,
257
+ extra: /* @__PURE__ */ jsx4(PrimaryButton, { onClick: () => setRetryTick((n) => n + 1), children: "\u91CD\u8BD5" })
258
+ }
259
+ );
260
+ }
261
+ return /* @__PURE__ */ jsx4(AppShell, { app, session: state.session });
262
+ }
263
+ function AppShell({
264
+ app,
265
+ session
266
+ }) {
267
+ const Router = app.routerOptions.type === "hash" ? HashRouter : BrowserRouter;
268
+ const InteractionKit = app.interactionKit.Provider;
269
+ const Providers = app.providers;
270
+ const i18n = createI18nContext({
271
+ locale: session.locale,
272
+ defaultLocale: session.defaultLocale,
273
+ messages: session.messages
274
+ });
275
+ const shell = /* @__PURE__ */ jsx4(I18nContextProvider, { value: i18n, children: /* @__PURE__ */ jsx4(InteractionKit, { bridge: app.uiBridge, children: /* @__PURE__ */ jsx4(ShellLayout, { app, session }) }) });
276
+ return /* @__PURE__ */ jsxs2(Router, { basename: app.routerOptions.basename, children: [
277
+ /* @__PURE__ */ jsx4(RouterBridgeSync, { binding: app.navigation }),
278
+ Providers !== void 0 ? /* @__PURE__ */ jsx4(Providers, { children: shell }) : shell
279
+ ] });
280
+ }
281
+ function RouterBridgeSync({ binding }) {
282
+ const bridge = useReactRouterBridge();
283
+ const location = useLocation();
284
+ useEffect2(() => {
285
+ binding.bind(bridge, {
286
+ pathname: location.pathname,
287
+ search: location.search
288
+ });
289
+ }, [bridge, binding]);
290
+ useEffect2(() => {
291
+ binding.sync({ pathname: location.pathname, search: location.search });
292
+ }, [binding, location.pathname, location.search]);
293
+ return null;
294
+ }
295
+ function ShellLayout({
296
+ app,
297
+ session
298
+ }) {
299
+ const location = useLocation();
300
+ const route = resolveRoute(session.menuRegistry, {
301
+ pathname: location.pathname,
302
+ search: location.search
303
+ });
304
+ const LayoutComponent = resolveLayout(app.layoutRegistry, session.layoutType);
305
+ const outlet = /* @__PURE__ */ jsx4(Routes, { children: /* @__PURE__ */ jsx4(
306
+ Route,
307
+ {
308
+ path: "*",
309
+ element: /* @__PURE__ */ jsx4(
310
+ DynamicDslRoute,
311
+ {
312
+ registry: session.menuRegistry,
313
+ permissionChecker: app.permissionStore,
314
+ fallback: /* @__PURE__ */ jsx4(NotFoundPage, {}),
315
+ forbidden: /* @__PURE__ */ jsx4(ForbiddenPage, {}),
316
+ render: ({ pageId, route: pageRoute }) => /* @__PURE__ */ jsx4(
317
+ DslPageLoader,
318
+ {
319
+ app,
320
+ pageId,
321
+ route: pageRoute
322
+ },
323
+ pageLoaderKey(pageId, pageRoute)
324
+ )
325
+ }
326
+ )
327
+ }
328
+ ) });
329
+ if (session.layoutType === "admin") {
330
+ return /* @__PURE__ */ jsx4(
331
+ LayoutComponent,
332
+ {
333
+ config: session.layoutConfig,
334
+ brand: session.brand,
335
+ registry: session.menuRegistry,
336
+ bridge: app.navigation,
337
+ route: route ?? void 0,
338
+ permissionChecker: app.permissionStore,
339
+ headerExtra: app.headerExtra,
340
+ children: outlet
341
+ }
342
+ );
343
+ }
344
+ return /* @__PURE__ */ jsx4(LayoutComponent, { brand: session.brand, children: outlet });
345
+ }
346
+ function pageLoaderKey(pageId, route) {
347
+ return `${pageId}|${JSON.stringify(route.params)}|${JSON.stringify(route.query)}`;
348
+ }
349
+ function NotFoundPage() {
350
+ return /* @__PURE__ */ jsx4(
351
+ ResultView,
352
+ {
353
+ className: "pactor-app-not-found",
354
+ title: "404",
355
+ subTitle: "\u9875\u9762\u4E0D\u5B58\u5728\u6216\u5C1A\u672A\u6CE8\u518C"
356
+ }
357
+ );
358
+ }
359
+ function ForbiddenPage() {
360
+ return /* @__PURE__ */ jsx4(
361
+ ResultView,
362
+ {
363
+ className: "pactor-app-forbidden",
364
+ title: "403",
365
+ subTitle: "\u6CA1\u6709\u8BBF\u95EE\u8BE5\u9875\u9762\u7684\u6743\u9650"
366
+ }
367
+ );
368
+ }
369
+
370
+ // src/application.ts
371
+ var ReactRouterBinding = class {
372
+ constructor() {
373
+ this.location = { pathname: "/", search: "" };
374
+ this.listeners = /* @__PURE__ */ new Set();
375
+ }
376
+ /** 绑定真实桥接与初始 location(RouterBridgeSync 挂载时调用) */
377
+ bind(delegate, initial) {
378
+ this.delegate = delegate;
379
+ this.location = initial;
380
+ }
381
+ /** location 变化同步并通知订阅者 */
382
+ sync(location) {
383
+ this.location = location;
384
+ for (const listener of [...this.listeners]) {
385
+ listener(location);
386
+ }
387
+ }
388
+ getLocation() {
389
+ return this.location;
390
+ }
391
+ push(to) {
392
+ if (this.delegate === void 0) {
393
+ console.warn(`[@pactor-app/app] \u8DEF\u7531\u5C1A\u672A\u6302\u8F7D\uFF0Cpush("${to}") \u5DF2\u5FFD\u7565`);
394
+ return;
395
+ }
396
+ this.delegate.push(to);
397
+ }
398
+ replace(to) {
399
+ if (this.delegate === void 0) {
400
+ console.warn(`[@pactor-app/app] \u8DEF\u7531\u5C1A\u672A\u6302\u8F7D\uFF0Creplace("${to}") \u5DF2\u5FFD\u7565`);
401
+ return;
402
+ }
403
+ this.delegate.replace(to);
404
+ }
405
+ subscribe(listener) {
406
+ this.listeners.add(listener);
407
+ return () => {
408
+ this.listeners.delete(listener);
409
+ };
410
+ }
411
+ };
412
+ function isRecord(value) {
413
+ return typeof value === "object" && value !== null && !Array.isArray(value);
414
+ }
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
+ function createResourceHttpAdapter(authTokenProvider) {
427
+ const base = createFetchAdapter();
428
+ if (authTokenProvider === void 0) {
429
+ return base;
430
+ }
431
+ return async (request) => {
432
+ const token = await authTokenProvider();
433
+ if (token === void 0 || token === "") {
434
+ return base(request);
435
+ }
436
+ return base({
437
+ ...request,
438
+ headers: { ...request.headers, authorization: `Bearer ${token}` }
439
+ });
440
+ };
441
+ }
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
+ var MENU_TYPES = /* @__PURE__ */ new Set(["group", "page", "link"]);
461
+ function sanitizeMenus(raw) {
462
+ if (!Array.isArray(raw)) {
463
+ return [];
464
+ }
465
+ const menus = [];
466
+ for (const item of raw) {
467
+ if (!isRecord(item) || typeof item["id"] !== "string") {
468
+ continue;
469
+ }
470
+ const menu = item;
471
+ menus.push({
472
+ ...menu,
473
+ title: typeof item["title"] === "string" ? item["title"] : menu.id,
474
+ type: MENU_TYPES.has(menu.type) ? menu.type : "page",
475
+ children: item["children"] !== void 0 ? sanitizeMenus(item["children"]) : void 0
476
+ });
477
+ }
478
+ return menus;
479
+ }
480
+ var LAYOUT_MODES = ["top-side", "top-bottom", "side-full", "two-column"];
481
+ var LAYOUT_BREAKPOINTS = ["xs", "sm", "md", "lg", "xl", "xxl"];
482
+ function pickLayoutConfig(layout) {
483
+ const config = {};
484
+ for (const key of [
485
+ "header",
486
+ "sidebar",
487
+ "tabs",
488
+ "breadcrumb",
489
+ "footer",
490
+ "fixedHeader",
491
+ "fixedSider",
492
+ "collapsible",
493
+ "collapsed"
494
+ ]) {
495
+ const value = layout?.[key];
496
+ if (typeof value === "boolean") {
497
+ config[key] = value;
498
+ }
499
+ }
500
+ const mode = layout?.["mode"];
501
+ if (typeof mode === "string" && LAYOUT_MODES.includes(mode)) {
502
+ config.mode = mode;
503
+ }
504
+ const breakpoint = layout?.["breakpoint"];
505
+ if (typeof breakpoint === "string" && LAYOUT_BREAKPOINTS.includes(breakpoint)) {
506
+ config.breakpoint = breakpoint;
507
+ }
508
+ return config;
509
+ }
510
+ function createPactorApplication(options) {
511
+ if (typeof options.appId !== "string" || options.appId === "") {
512
+ throw new Error("[@pactor-app/app] createPactorApplication: appId \u5FC5\u586B");
513
+ }
514
+ const { appId } = options;
515
+ const routerOptions = options.router ?? {};
516
+ const overlayManager = new OverlayManager();
517
+ const uiBridge = createUiBridge();
518
+ const permissionStore = new PermissionStore();
519
+ const layoutRegistry = (options.layoutRegistry ?? options.ui?.layoutRegistry)?.() ?? createShadcnLayoutRegistry();
520
+ const interactionKit = options.interactionKit ?? options.ui?.interactionKit ?? {
521
+ Provider: ShadcnInteractionProvider,
522
+ OverlayHost: ShadcnOverlayHost
523
+ };
524
+ const navigation = new ReactRouterBinding();
525
+ const loader = options.loader ?? new HttpDslResourceLoader({
526
+ baseUrl: options.api?.baseURL,
527
+ adapter: createResourceHttpAdapter(options.authTokenProvider)
528
+ });
529
+ const httpAdapter = createRuntimeHttpAdapter(options.authTokenProvider);
530
+ const permissionAdapter = createPermissionAdapter(permissionStore);
531
+ 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
+ let session = null;
553
+ const handle = {
554
+ appId,
555
+ overlayManager,
556
+ uiBridge,
557
+ permissionStore,
558
+ layoutRegistry,
559
+ interactionKit,
560
+ routerOptions,
561
+ providers: options.providers,
562
+ headerExtra: options.headerExtra,
563
+ navigation,
564
+ async loadSession() {
565
+ const bootstrap = await loader.loadBootstrap();
566
+ permissionStore.setPermissions(
567
+ Array.isArray(bootstrap.permissions) ? bootstrap.permissions.filter(
568
+ (code) => typeof code === "string"
569
+ ) : []
570
+ );
571
+ const menuRegistry = new MenuRegistry(sanitizeMenus(bootstrap.menus));
572
+ const layout = isRecord(bootstrap.layout) ? bootstrap.layout : void 0;
573
+ const layoutType = options.layout ?? (typeof layout?.["type"] === "string" ? layout["type"] : void 0) ?? "admin";
574
+ const appRecord = {
575
+ id: appId,
576
+ ...isRecord(bootstrap.app) ? bootstrap.app : {}
577
+ };
578
+ const brand = options.brand ?? {
579
+ title: typeof appRecord["name"] === "string" ? appRecord["name"] : appId,
580
+ logo: void 0
581
+ };
582
+ const browserLocale = typeof navigator !== "undefined" && typeof navigator.language === "string" ? navigator.language : "zh-CN";
583
+ const locale = options.locale ?? (typeof bootstrap.locale === "string" ? bootstrap.locale : void 0) ?? browserLocale;
584
+ const defaultLocale = options.defaultLocale ?? (typeof bootstrap.defaultLocale === "string" ? bootstrap.defaultLocale : void 0) ?? "zh-CN";
585
+ const messages = options.messages ?? (isRecord(bootstrap.messages) ? bootstrap.messages : void 0) ?? {};
586
+ const next = {
587
+ bootstrap,
588
+ menuRegistry,
589
+ layoutType,
590
+ layoutConfig: pickLayoutConfig(layout),
591
+ brand,
592
+ user: isRecord(bootstrap.user) ? bootstrap.user : {},
593
+ app: appRecord,
594
+ locale,
595
+ defaultLocale,
596
+ messages
597
+ };
598
+ session = next;
599
+ return next;
600
+ },
601
+ createPageRuntime(pageDsl, route) {
602
+ return createPageRuntime(pageDsl, {
603
+ components,
604
+ actions,
605
+ services,
606
+ capabilities,
607
+ permission: permissionAdapter,
608
+ router: routerAdapter,
609
+ http: httpAdapter,
610
+ route: { params: route.params, query: route.query },
611
+ user: session?.user ?? {},
612
+ app: session?.app ?? { id: appId },
613
+ locale: session?.locale ?? "zh-CN",
614
+ defaultLocale: session?.defaultLocale ?? "zh-CN",
615
+ messages: session?.messages ?? {}
616
+ });
617
+ },
618
+ async resolvePage(pageId) {
619
+ const resource = await loader.loadPage(pageId);
620
+ return resource.schema;
621
+ }
622
+ };
623
+ let root = null;
624
+ return {
625
+ appId,
626
+ mount(selector) {
627
+ const target = document.querySelector(selector);
628
+ if (target === null) {
629
+ throw new Error(`[@pactor-app/app] mount \u76EE\u6807\u5143\u7D20\u4E0D\u5B58\u5728: ${selector}`);
630
+ }
631
+ if (root !== null) {
632
+ root.unmount();
633
+ root = null;
634
+ }
635
+ root = createRoot(target);
636
+ root.render(createElement(Application, { app: handle }));
637
+ },
638
+ unmount() {
639
+ if (root !== null) {
640
+ root.unmount();
641
+ root = null;
642
+ }
643
+ },
644
+ registerComponent(name, component) {
645
+ upsert(components, name, component);
646
+ },
647
+ registerAction(name, handler) {
648
+ upsert(actions, name, handler);
649
+ },
650
+ registerService(name, handler) {
651
+ upsert(services, name, handler);
652
+ },
653
+ registerCapability(name, capability) {
654
+ upsert(capabilities, name, capability);
655
+ },
656
+ setLocale(locale, messages) {
657
+ if (session === null) {
658
+ console.warn("[@pactor-app/app] setLocale \u5728 bootstrap \u5B8C\u6210\u524D\u8C03\u7528\u5DF2\u5FFD\u7565");
659
+ return;
660
+ }
661
+ session = { ...session, locale, messages: messages ?? session.messages };
662
+ window.location.reload();
663
+ }
664
+ };
665
+ }
666
+ export {
667
+ Application,
668
+ DslPageLoader,
669
+ PageErrorBoundary,
670
+ ReactRouterBinding,
671
+ createPactorApplication
672
+ };