@pactor-app/runtime 0.1.0 → 1.0.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.cjs CHANGED
@@ -23,6 +23,8 @@ __export(index_exports, {
23
23
  ActionEngine: () => ActionEngine,
24
24
  DataStore: () => DataStore,
25
25
  DslRenderer: () => DslRenderer,
26
+ DslSourceBoundary: () => DslSourceBoundary,
27
+ DslSourceResolver: () => DslSourceResolver,
26
28
  I18nContextProvider: () => I18nContextProvider,
27
29
  PageRuntimeError: () => PageRuntimeError,
28
30
  PageRuntimeView: () => PageRuntimeView,
@@ -34,6 +36,7 @@ __export(index_exports, {
34
36
  createFetchHttpAdapter: () => createFetchHttpAdapter,
35
37
  createPageRuntime: () => createPageRuntime,
36
38
  createRuntimeContext: () => createRuntimeContext,
39
+ dslSourceKey: () => dslSourceKey,
37
40
  registerBuiltinActions: () => registerBuiltinActions,
38
41
  resolveDeep: () => resolveDeep,
39
42
  useDataSource: () => useDataSource,
@@ -162,6 +165,14 @@ var DataStore = class {
162
165
  }
163
166
  return out;
164
167
  }
168
+ /** 各数据源的加载状态(loading / error),供表达式作用域的 `dataStatus` 键使用 */
169
+ getAllStatus() {
170
+ const out = {};
171
+ for (const [name, status] of this.statuses) {
172
+ out[name] = { loading: status.loading, error: status.error };
173
+ }
174
+ return out;
175
+ }
165
176
  subscribe(listener) {
166
177
  this.listeners.add(listener);
167
178
  return () => {
@@ -188,7 +199,8 @@ var DataStore = class {
188
199
  const previous = this.get(name);
189
200
  this.update(name, { loading: true, error: void 0, data: previous.data });
190
201
  try {
191
- const data = await this.loadByType(config);
202
+ const raw = await this.loadByType(config);
203
+ const data = applyOptionMapping(raw, config);
192
204
  this.update(name, { loading: false, data, error: void 0 });
193
205
  } catch (cause) {
194
206
  this.update(name, {
@@ -254,6 +266,69 @@ var DataStore = class {
254
266
  }
255
267
  }
256
268
  };
269
+ function applyOptionMapping(data, config) {
270
+ if (!config.valueField && !config.labelField && !config.disabledField) {
271
+ return data;
272
+ }
273
+ if (!Array.isArray(data)) {
274
+ return data;
275
+ }
276
+ const valueField = config.valueField ?? "value";
277
+ const labelField = config.labelField ?? "label";
278
+ return data.map((item) => {
279
+ if (item === null || typeof item !== "object") {
280
+ return item;
281
+ }
282
+ const record = item;
283
+ return {
284
+ ...record,
285
+ label: record[labelField],
286
+ value: record[valueField],
287
+ disabled: config.disabledField ? Boolean(record[config.disabledField]) : Boolean(record.disabled)
288
+ };
289
+ });
290
+ }
291
+
292
+ // src/datasource/dsl-source-resolver.ts
293
+ function dslSourceKey(ref) {
294
+ const source = ref.source;
295
+ if (source.type === "service") {
296
+ return `service:${source.service}:${JSON.stringify(source.params ?? {})}`;
297
+ }
298
+ const request = source.request;
299
+ return `http:${(request.method ?? "GET").toUpperCase()}:${request.url}:${JSON.stringify(request.params ?? {})}:${JSON.stringify(request.headers ?? {})}`;
300
+ }
301
+ var DslSourceResolver = class {
302
+ constructor(deps) {
303
+ this.deps = deps;
304
+ this.cache = /* @__PURE__ */ new Map();
305
+ }
306
+ resolve(ref) {
307
+ const key = dslSourceKey(ref);
308
+ let cached = this.cache.get(key);
309
+ if (cached === void 0) {
310
+ cached = this.load(ref);
311
+ this.cache.set(key, cached);
312
+ }
313
+ return cached;
314
+ }
315
+ async load(ref) {
316
+ const scope = this.deps.getScope();
317
+ const source = ref.source;
318
+ if (source.type === "service") {
319
+ const handler = this.deps.services.get(source.service);
320
+ if (!handler) {
321
+ throw new Error(
322
+ `[@pactor-app/runtime] service not registered: "${source.service}"`
323
+ );
324
+ }
325
+ const params = resolveDeep(source.params ?? {}, scope);
326
+ return handler(params);
327
+ }
328
+ const request = resolveDeep(source.request, scope);
329
+ return this.deps.http.request(request);
330
+ }
331
+ };
257
332
 
258
333
  // src/context/scope.ts
259
334
  function buildScope(context, local) {
@@ -263,6 +338,7 @@ function buildScope(context, local) {
263
338
  state: context.state.getState(),
264
339
  form: context.state.get("form"),
265
340
  data: context.data.getAllData(),
341
+ dataStatus: context.data.getAllStatus(),
266
342
  route: context.route,
267
343
  user: context.user,
268
344
  params: context.route.params,
@@ -372,6 +448,11 @@ function createRuntimeContext(options = {}) {
372
448
  http,
373
449
  getScope: () => buildScope(context)
374
450
  });
451
+ const dslSources = new DslSourceResolver({
452
+ services,
453
+ http,
454
+ getScope: () => buildScope(context)
455
+ });
375
456
  context = {
376
457
  app: options.app ?? {},
377
458
  page: options.page ?? {},
@@ -384,9 +465,12 @@ function createRuntimeContext(options = {}) {
384
465
  actions,
385
466
  forms,
386
467
  actionResults: {},
468
+ namedActions: options.namedActions ?? {},
469
+ pageComponents: options.pageComponents ?? {},
387
470
  permission,
388
471
  capabilities,
389
472
  http,
473
+ dslSources,
390
474
  locale: options.locale ?? "zh-CN",
391
475
  i18n: (0, import_core.createI18nContext)({
392
476
  locale: options.locale,
@@ -402,8 +486,8 @@ var import_core2 = require("@pactor-app/core");
402
486
  var import_react = require("react");
403
487
  var I18nReactContext = (0, import_react.createContext)(null);
404
488
  var I18nContextProvider = I18nReactContext.Provider;
405
- var defaultI18n = (0, import_core2.createI18nContext)({
406
- messages: {
489
+ var defaultMessages = {
490
+ "zh-CN": {
407
491
  "pactor.layout.expandSidebar": "\u5C55\u5F00\u4FA7\u8FB9\u680F",
408
492
  "pactor.layout.collapseSidebar": "\u6536\u8D77\u4FA7\u8FB9\u680F",
409
493
  "pactor.layout.closeTab": "\u5173\u95ED {{title}}",
@@ -411,19 +495,42 @@ var defaultI18n = (0, import_core2.createI18nContext)({
411
495
  "pactor.table.loading": "\u52A0\u8F7D\u4E2D\u2026",
412
496
  "pactor.table.empty": "\u6682\u65E0\u6570\u636E",
413
497
  "pactor.form.required": "\u5FC5\u586B",
414
- "pactor.select.clear": "\u6E05\u9664"
498
+ "pactor.select.clear": "\u6E05\u9664",
499
+ "pactor.alertDialog.confirm": "\u786E\u5B9A",
500
+ "pactor.alertDialog.cancel": "\u53D6\u6D88",
501
+ "pactor.command.empty": "\u65E0\u5339\u914D\u7ED3\u679C",
502
+ "pactor.overlay.close": "\u5173\u95ED"
503
+ },
504
+ "en-US": {
505
+ "pactor.layout.expandSidebar": "Expand sidebar",
506
+ "pactor.layout.collapseSidebar": "Collapse sidebar",
507
+ "pactor.layout.closeTab": "Close {{title}}",
508
+ "pactor.layout.breadcrumb": "Breadcrumb",
509
+ "pactor.table.loading": "Loading\u2026",
510
+ "pactor.table.empty": "No data",
511
+ "pactor.form.required": "Required",
512
+ "pactor.select.clear": "Clear",
513
+ "pactor.alertDialog.confirm": "Confirm",
514
+ "pactor.alertDialog.cancel": "Cancel",
515
+ "pactor.command.empty": "No results found",
516
+ "pactor.overlay.close": "Close"
415
517
  }
416
- });
518
+ };
519
+ function createDefaultI18n(locale) {
520
+ return (0, import_core2.createI18nContext)({ locale, messages: defaultMessages });
521
+ }
522
+ var defaultI18n = createDefaultI18n();
417
523
  function useI18n() {
418
524
  const value = (0, import_react.useContext)(I18nReactContext);
419
525
  if (value === null) {
420
526
  return defaultI18n;
421
527
  }
528
+ const fallback = createDefaultI18n(value.locale);
422
529
  return {
423
530
  ...value,
424
531
  t: (key, params) => {
425
532
  const translated = value.t(key, params);
426
- return translated === key ? defaultI18n.t(key, params) : translated;
533
+ return translated === key ? fallback.t(key, params) : translated;
427
534
  }
428
535
  };
429
536
  }
@@ -488,6 +595,45 @@ function registerBuiltinActions(registry) {
488
595
  }
489
596
  return { ok: true };
490
597
  });
598
+ register("call", async (action, ctx) => {
599
+ const name = action.params?.["name"];
600
+ if (typeof name !== "string" || !name) {
601
+ return {
602
+ ok: false,
603
+ error: new Error("[@pactor-app/runtime] call \u52A8\u4F5C\u7F3A\u5C11 name \u53C2\u6570")
604
+ };
605
+ }
606
+ const target = ctx.context.namedActions[name];
607
+ if (target === void 0) {
608
+ return {
609
+ ok: false,
610
+ error: new Error(
611
+ `[@pactor-app/runtime] unknown named action: "${name}"`
612
+ )
613
+ };
614
+ }
615
+ const depth = (callDepth.get(ctx.context) ?? 0) + 1;
616
+ if (depth > MAX_CALL_DEPTH) {
617
+ return {
618
+ ok: false,
619
+ error: new Error(
620
+ `[@pactor-app/runtime] call \u5D4C\u5957\u8D85\u8FC7 ${MAX_CALL_DEPTH} \u5C42\uFF08\u7591\u4F3C\u5FAA\u73AF\u5F15\u7528\uFF09: "${name}"`
621
+ )
622
+ };
623
+ }
624
+ callDepth.set(ctx.context, depth);
625
+ try {
626
+ for (const child of asActions(target)) {
627
+ const result = await ctx.execute(child);
628
+ if (!result.ok) {
629
+ return result;
630
+ }
631
+ }
632
+ return { ok: true };
633
+ } finally {
634
+ callDepth.set(ctx.context, depth - 1);
635
+ }
636
+ });
491
637
  register("parallel", async (action, ctx) => {
492
638
  const results = await Promise.all(
493
639
  asActions(action.params?.["actions"]).map((child) => ctx.execute(child))
@@ -544,6 +690,8 @@ function asActions(value) {
544
690
  }
545
691
  return [];
546
692
  }
693
+ var callDepth = /* @__PURE__ */ new WeakMap();
694
+ var MAX_CALL_DEPTH = 20;
547
695
  function isRecord(value) {
548
696
  return typeof value === "object" && value !== null && !Array.isArray(value);
549
697
  }
@@ -597,7 +745,7 @@ var ActionEngine = class {
597
745
  // Renderer 的 resolveProps)保持原样,由各自执行/渲染时
598
746
  // 按当时的 state 与作用域求值
599
747
  params: resolveDeep(action.params ?? {}, scope, {
600
- isOpaque: (value) => typeof value["action"] === "string" || typeof value["type"] === "string"
748
+ isOpaque: (value) => typeof value["action"] === "string" || typeof value["type"] === "string" || typeof value["component"] === "string"
601
749
  })
602
750
  };
603
751
  const ctx = {
@@ -632,15 +780,57 @@ var ActionEngine = class {
632
780
  };
633
781
 
634
782
  // src/renderer/renderer.tsx
783
+ var import_core3 = require("@pactor-app/core");
635
784
  var import_expression3 = require("@pactor-app/expression");
636
- var import_react3 = require("react");
785
+ var import_schema = require("@pactor-app/schema");
786
+ var import_react4 = require("react");
637
787
 
638
- // src/renderer/renderer-context.ts
788
+ // src/renderer/dsl-source-boundary.tsx
639
789
  var import_react2 = require("react");
640
- var RendererReactContext = (0, import_react2.createContext)(null);
790
+ var import_jsx_runtime = require("react/jsx-runtime");
791
+ function DslSourceBoundary({
792
+ context,
793
+ sourceRef,
794
+ placeholder,
795
+ children
796
+ }) {
797
+ const [state, setState] = (0, import_react2.useState)({ status: "loading" });
798
+ const key = dslSourceKey(sourceRef);
799
+ (0, import_react2.useEffect)(() => {
800
+ let cancelled = false;
801
+ setState({ status: "loading" });
802
+ context.dslSources.resolve(sourceRef).then(
803
+ (content) => {
804
+ if (!cancelled) {
805
+ setState({ status: "ready", content });
806
+ }
807
+ },
808
+ (cause) => {
809
+ if (!cancelled) {
810
+ console.warn("[@pactor-app/runtime] DSL \u52A8\u6001\u5F15\u7528\u89E3\u6790\u5931\u8D25", cause);
811
+ setState({ status: "error" });
812
+ }
813
+ }
814
+ );
815
+ return () => {
816
+ cancelled = true;
817
+ };
818
+ }, [context, key]);
819
+ if (state.status === "ready") {
820
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: children(state.content) });
821
+ }
822
+ if (state.status === "error") {
823
+ return null;
824
+ }
825
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: placeholder ?? null });
826
+ }
827
+
828
+ // src/renderer/renderer-context.ts
829
+ var import_react3 = require("react");
830
+ var RendererReactContext = (0, import_react3.createContext)(null);
641
831
  var RendererContextProvider = RendererReactContext.Provider;
642
832
  function useRenderer() {
643
- const value = (0, import_react2.useContext)(RendererReactContext);
833
+ const value = (0, import_react3.useContext)(RendererReactContext);
644
834
  if (!value) {
645
835
  throw new Error("[@pactor-app/runtime] useRenderer \u5FC5\u987B\u5728 <DslRenderer> \u5185\u4F7F\u7528");
646
836
  }
@@ -648,23 +838,23 @@ function useRenderer() {
648
838
  }
649
839
  function useRuntimeState(path) {
650
840
  const { context } = useRenderer();
651
- return (0, import_react2.useSyncExternalStore)(
841
+ return (0, import_react3.useSyncExternalStore)(
652
842
  (onChange) => context.state.subscribe(onChange),
653
843
  () => path === void 0 ? context.state.getState() : context.state.get(path)
654
844
  );
655
845
  }
656
846
  function useDataSource(name) {
657
847
  const { context } = useRenderer();
658
- return (0, import_react2.useSyncExternalStore)(
848
+ return (0, import_react3.useSyncExternalStore)(
659
849
  (onChange) => context.data.subscribe(onChange),
660
850
  () => context.data.get(name)
661
851
  );
662
852
  }
663
853
 
664
854
  // src/renderer/unsupported.tsx
665
- var import_jsx_runtime = require("react/jsx-runtime");
855
+ var import_jsx_runtime2 = require("react/jsx-runtime");
666
856
  function UnsupportedComponent({ type }) {
667
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
857
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
668
858
  "div",
669
859
  {
670
860
  "data-pactor-unsupported": type,
@@ -684,9 +874,9 @@ function UnsupportedComponent({ type }) {
684
874
  }
685
875
 
686
876
  // src/renderer/renderer.tsx
687
- var import_jsx_runtime2 = require("react/jsx-runtime");
877
+ var import_jsx_runtime3 = require("react/jsx-runtime");
688
878
  function DslRenderer({ context, node, engine, children }) {
689
- (0, import_react3.useSyncExternalStore)(
879
+ (0, import_react4.useSyncExternalStore)(
690
880
  (onChange) => {
691
881
  const offState = context.state.subscribe(onChange);
692
882
  const offData = context.data.subscribe(onChange);
@@ -697,26 +887,65 @@ function DslRenderer({ context, node, engine, children }) {
697
887
  },
698
888
  () => `${context.state.getVersion()}:${context.data.getVersion()}`
699
889
  );
700
- const resolvedEngine = (0, import_react3.useMemo)(
890
+ const resolvedEngine = (0, import_react4.useMemo)(
701
891
  () => engine ?? new ActionEngine(context),
702
892
  [engine, context]
703
893
  );
704
- const value = (0, import_react3.useMemo)(() => {
894
+ const value = (0, import_react4.useMemo)(() => {
705
895
  const root = {
706
896
  context,
707
897
  engine: resolvedEngine,
708
898
  scope: {},
899
+ referenceStack: [],
709
900
  renderChildren: (children2, extraScope) => renderChildrenOf(root, children2, extraScope)
710
901
  };
711
902
  return root;
712
903
  }, [context, resolvedEngine]);
713
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(RendererContextProvider, { value, children: [
904
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(RendererContextProvider, { value, children: [
714
905
  node ? renderNode(node, value, node.id ?? "root") : null,
715
906
  children
716
907
  ] });
717
908
  }
718
909
  function renderNode(node, value, key) {
719
910
  const scope = buildScope(value.context, value.scope);
911
+ if (node.component !== void 0) {
912
+ const name = node.component;
913
+ const stack = value.referenceStack ?? [];
914
+ if (stack.includes(name)) {
915
+ console.warn(
916
+ `[@pactor-app/runtime] \u7EC4\u4EF6\u5F15\u7528\u5FAA\u73AF\uFF1A${[...stack, name].join(" \u2192 ")}`
917
+ );
918
+ return null;
919
+ }
920
+ const subtree = value.context.pageComponents[name];
921
+ if (!subtree) {
922
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnsupportedComponent, { type: `component(${name})` }, key);
923
+ }
924
+ const scoped = {
925
+ ...value,
926
+ referenceStack: [...stack, name]
927
+ };
928
+ scoped.renderChildren = (children, extraScope) => renderChildrenOf(scoped, children, extraScope);
929
+ if ((0, import_core3.isDslSourceRef)(subtree)) {
930
+ const ref = subtree;
931
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
932
+ DslSourceBoundary,
933
+ {
934
+ context: value.context,
935
+ sourceRef: ref,
936
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, scoped, "placeholder") : null,
937
+ children: (content) => {
938
+ const resolved = validateResolvedNode(content, `components.${name}`);
939
+ if (!resolved) {
940
+ return null;
941
+ }
942
+ return renderNode(mergeReferenceNode(resolved, node), scoped, key);
943
+ }
944
+ }
945
+ ) }, key);
946
+ }
947
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: renderNode(mergeReferenceNode(subtree, node), scoped, key) }, key);
948
+ }
720
949
  if (node.when !== void 0) {
721
950
  const expression = typeof node.when === "string" ? node.when : node.when.expression;
722
951
  let visible;
@@ -743,7 +972,7 @@ function renderNode(node, value, key) {
743
972
  const entry = value.context.components.get(node.type);
744
973
  const normalized = normalizeEntry(entry);
745
974
  if (!normalized) {
746
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UnsupportedComponent, { type: node.type }, key);
975
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnsupportedComponent, { type: node.type }, key);
747
976
  }
748
977
  const props = resolveProps(node.props, scope);
749
978
  if (node.events) {
@@ -759,13 +988,13 @@ function renderNode(node, value, key) {
759
988
  props["readOnly"] = true;
760
989
  }
761
990
  if (normalized.withScope) {
762
- return (0, import_react3.createElement)(normalized.component, {
991
+ return (0, import_react4.createElement)(normalized.component, {
763
992
  key,
764
993
  ...props,
765
994
  dslChildren: node.children
766
995
  });
767
996
  }
768
- return (0, import_react3.createElement)(
997
+ return (0, import_react4.createElement)(
769
998
  normalized.component,
770
999
  { key, ...props },
771
1000
  renderChildrenOf(value, node.children)
@@ -777,23 +1006,102 @@ function resolveDeniedStrategy(permission) {
777
1006
  }
778
1007
  return "hidden";
779
1008
  }
1009
+ function mergeReferenceNode(definition, reference) {
1010
+ return {
1011
+ ...definition,
1012
+ props: { ...definition.props, ...reference.props },
1013
+ events: { ...definition.events, ...reference.events },
1014
+ when: reference.when ?? definition.when,
1015
+ permission: reference.permission ?? definition.permission,
1016
+ id: reference.id ?? definition.id
1017
+ };
1018
+ }
1019
+ function validateResolvedNode(content, label) {
1020
+ const errors = (0, import_schema.validateDslNode)(content);
1021
+ if (errors.length > 0) {
1022
+ console.warn(
1023
+ `[@pactor-app/runtime] \u52A8\u6001\u5F15\u7528\uFF08${label}\uFF09\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C`,
1024
+ errors
1025
+ );
1026
+ return null;
1027
+ }
1028
+ return content;
1029
+ }
1030
+ function validateResolvedNodes(content, label) {
1031
+ const errors = (0, import_schema.validateDslNodes)(content);
1032
+ if (errors.length > 0) {
1033
+ console.warn(
1034
+ `[@pactor-app/runtime] \u52A8\u6001\u5F15\u7528\uFF08${label}\uFF09\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C`,
1035
+ errors
1036
+ );
1037
+ return null;
1038
+ }
1039
+ return content;
1040
+ }
1041
+ function renderChildItem(value, item, key) {
1042
+ if ((0, import_core3.isDslSourceRef)(item)) {
1043
+ const ref = item;
1044
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1045
+ DslSourceBoundary,
1046
+ {
1047
+ context: value.context,
1048
+ sourceRef: ref,
1049
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, value, "placeholder") : null,
1050
+ children: (content) => {
1051
+ const nodes = validateResolvedNodes(content, "children");
1052
+ if (!nodes) {
1053
+ return null;
1054
+ }
1055
+ return nodes.map(
1056
+ (child, index) => renderChildItem(value, child, (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index)
1057
+ );
1058
+ }
1059
+ },
1060
+ key
1061
+ );
1062
+ }
1063
+ return renderNode(item, value, key);
1064
+ }
780
1065
  function renderChildrenOf(value, children, extraScope) {
781
- if (!children || children.length === 0) {
1066
+ if (!children || Array.isArray(children) && children.length === 0) {
782
1067
  return null;
783
1068
  }
1069
+ if ((0, import_core3.isDslSourceRef)(children)) {
1070
+ const ref = children;
1071
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1072
+ DslSourceBoundary,
1073
+ {
1074
+ context: value.context,
1075
+ sourceRef: ref,
1076
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, value, "placeholder") : null,
1077
+ children: (content) => {
1078
+ const nodes = validateResolvedNodes(content, "children");
1079
+ if (!nodes) {
1080
+ return null;
1081
+ }
1082
+ return nodes.map(
1083
+ (child, index) => renderChildItem(value, child, (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index)
1084
+ );
1085
+ }
1086
+ },
1087
+ "dsl-source"
1088
+ );
1089
+ }
1090
+ const itemKey = (child, index) => (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index;
784
1091
  if (!extraScope) {
785
1092
  return children.map(
786
- (child, index) => renderNode(child, value, child.id ?? index)
1093
+ (child, index) => renderChildItem(value, child, itemKey(child, index))
787
1094
  );
788
1095
  }
789
1096
  const scoped = {
790
1097
  context: value.context,
791
1098
  engine: value.engine,
792
1099
  scope: { ...value.scope, ...extraScope },
1100
+ referenceStack: value.referenceStack,
793
1101
  renderChildren: (nested, nestedScope) => renderChildrenOf(scoped, nested, nestedScope)
794
1102
  };
795
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RendererContextProvider, { value: scoped, children: children.map(
796
- (child, index) => renderNode(child, scoped, child.id ?? index)
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: children.map(
1104
+ (child, index) => renderChildItem(scoped, child, itemKey(child, index))
797
1105
  ) });
798
1106
  }
799
1107
  function normalizeEntry(entry) {
@@ -816,12 +1124,13 @@ function resolveProps(props, scope) {
816
1124
  return out;
817
1125
  }
818
1126
  function isEmbeddedDslNode(value) {
819
- return typeof value.type === "string";
1127
+ return typeof value.type === "string" || typeof value.component === "string";
820
1128
  }
821
1129
 
822
1130
  // src/page/page-runtime.tsx
823
- var import_schema = require("@pactor-app/schema");
824
- var import_jsx_runtime3 = require("react/jsx-runtime");
1131
+ var import_core4 = require("@pactor-app/core");
1132
+ var import_schema2 = require("@pactor-app/schema");
1133
+ var import_jsx_runtime4 = require("react/jsx-runtime");
825
1134
  var PageRuntimeError = class extends Error {
826
1135
  constructor(errors) {
827
1136
  super(
@@ -832,7 +1141,7 @@ var PageRuntimeError = class extends Error {
832
1141
  }
833
1142
  };
834
1143
  function createPageRuntime(pageDsl, options = {}) {
835
- const validation = (0, import_schema.validatePageDsl)(pageDsl);
1144
+ const validation = (0, import_schema2.validatePageDsl)(pageDsl);
836
1145
  if (!validation.valid) {
837
1146
  throw new PageRuntimeError(validation.errors);
838
1147
  }
@@ -840,7 +1149,9 @@ function createPageRuntime(pageDsl, options = {}) {
840
1149
  ...options,
841
1150
  page: pageDsl.page,
842
1151
  state: pageDsl.state ?? options.state,
843
- dataSources: pageDsl.dataSources ?? options.dataSources
1152
+ dataSources: pageDsl.dataSources ?? options.dataSources,
1153
+ namedActions: pageDsl.actions ?? options.namedActions,
1154
+ pageComponents: pageDsl.components ?? options.pageComponents
844
1155
  });
845
1156
  registerBuiltinActions(context.actions);
846
1157
  const engine = new ActionEngine(context);
@@ -876,10 +1187,7 @@ function createPageRuntime(pageDsl, options = {}) {
876
1187
  return destroyed;
877
1188
  },
878
1189
  render() {
879
- if (!pageDsl.body) {
880
- return null;
881
- }
882
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DslRenderer, { context, node: pageDsl.body, engine });
1190
+ return renderRoots(pageDsl, context, engine);
883
1191
  },
884
1192
  ready: () => triggerWhenAlive("onReady"),
885
1193
  show: () => triggerWhenAlive("onShow"),
@@ -907,17 +1215,68 @@ function PageRuntimeView({
907
1215
  runtime,
908
1216
  children
909
1217
  }) {
910
- const body = runtime.pageDsl.body;
911
- if (!body) {
912
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: children ?? null });
1218
+ return renderRoots(runtime.pageDsl, runtime.context, runtime.engine, children);
1219
+ }
1220
+ function renderRoots(pageDsl, context, engine, children) {
1221
+ if (!pageDsl.body && (0, import_core4.isDslSourceRef)(pageDsl.children)) {
1222
+ const ref = pageDsl.children;
1223
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1224
+ DslSourceBoundary,
1225
+ {
1226
+ context,
1227
+ sourceRef: ref,
1228
+ placeholder: ref.placeholder ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DslRenderer, { context, node: ref.placeholder, engine }) : null,
1229
+ children: (content) => renderResolvedRoots(content, context, engine, children)
1230
+ }
1231
+ );
1232
+ }
1233
+ const roots = (0, import_core4.resolveRootNodes)(pageDsl);
1234
+ if (roots.length === 0) {
1235
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children ?? null });
913
1236
  }
914
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DslRenderer, { context: runtime.context, node: body, engine: runtime.engine, children });
1237
+ return renderRootItems(roots, context, engine, children);
1238
+ }
1239
+ function renderResolvedRoots(content, context, engine, children) {
1240
+ const errors = (0, import_schema2.validateDslNodes)(content);
1241
+ if (errors.length > 0) {
1242
+ console.warn(
1243
+ "[@pactor-app/runtime] \u6839\u7EA7 children \u52A8\u6001\u5F15\u7528\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C",
1244
+ errors
1245
+ );
1246
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children ?? null });
1247
+ }
1248
+ return renderRootItems(content, context, engine, children);
1249
+ }
1250
+ function renderRootItems(items, context, engine, children) {
1251
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: items.map(
1252
+ (item, index) => (0, import_core4.isDslSourceRef)(item) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1253
+ DslSourceBoundary,
1254
+ {
1255
+ context,
1256
+ sourceRef: item,
1257
+ placeholder: item.placeholder ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DslRenderer, { context, node: item.placeholder, engine }) : null,
1258
+ children: (content) => renderResolvedRoots(content, context, engine, children)
1259
+ },
1260
+ dslSourceKey(item)
1261
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1262
+ DslRenderer,
1263
+ {
1264
+ context,
1265
+ node: item,
1266
+ engine,
1267
+ children: index === items.length - 1 ? children : null
1268
+ },
1269
+ item.id ?? index
1270
+ )
1271
+ ) });
915
1272
  }
916
1273
  // Annotate the CommonJS export names for ESM import in node:
917
1274
  0 && (module.exports = {
918
1275
  ActionEngine,
919
1276
  DataStore,
920
1277
  DslRenderer,
1278
+ DslSourceBoundary,
1279
+ DslSourceResolver,
921
1280
  I18nContextProvider,
922
1281
  PageRuntimeError,
923
1282
  PageRuntimeView,
@@ -929,6 +1288,7 @@ function PageRuntimeView({
929
1288
  createFetchHttpAdapter,
930
1289
  createPageRuntime,
931
1290
  createRuntimeContext,
1291
+ dslSourceKey,
932
1292
  registerBuiltinActions,
933
1293
  resolveDeep,
934
1294
  useDataSource,