@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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/index.cjs +680 -0
- package/dist/index.d.cts +278 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.js +672 -0
- package/package.json +78 -0
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
|
+
};
|