@pactor-app/runtime 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/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,28 +486,53 @@ 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",
493
+ "pactor.layout.openNavigation": "\u6253\u5F00\u5BFC\u822A",
409
494
  "pactor.layout.closeTab": "\u5173\u95ED {{title}}",
410
495
  "pactor.layout.breadcrumb": "\u9762\u5305\u5C51",
411
496
  "pactor.table.loading": "\u52A0\u8F7D\u4E2D\u2026",
412
497
  "pactor.table.empty": "\u6682\u65E0\u6570\u636E",
413
498
  "pactor.form.required": "\u5FC5\u586B",
414
- "pactor.select.clear": "\u6E05\u9664"
499
+ "pactor.select.clear": "\u6E05\u9664",
500
+ "pactor.alertDialog.confirm": "\u786E\u5B9A",
501
+ "pactor.alertDialog.cancel": "\u53D6\u6D88",
502
+ "pactor.command.empty": "\u65E0\u5339\u914D\u7ED3\u679C",
503
+ "pactor.overlay.close": "\u5173\u95ED"
504
+ },
505
+ "en-US": {
506
+ "pactor.layout.expandSidebar": "Expand sidebar",
507
+ "pactor.layout.collapseSidebar": "Collapse sidebar",
508
+ "pactor.layout.openNavigation": "Open navigation",
509
+ "pactor.layout.closeTab": "Close {{title}}",
510
+ "pactor.layout.breadcrumb": "Breadcrumb",
511
+ "pactor.table.loading": "Loading\u2026",
512
+ "pactor.table.empty": "No data",
513
+ "pactor.form.required": "Required",
514
+ "pactor.select.clear": "Clear",
515
+ "pactor.alertDialog.confirm": "Confirm",
516
+ "pactor.alertDialog.cancel": "Cancel",
517
+ "pactor.command.empty": "No results found",
518
+ "pactor.overlay.close": "Close"
415
519
  }
416
- });
520
+ };
521
+ function createDefaultI18n(locale) {
522
+ return (0, import_core2.createI18nContext)({ locale, messages: defaultMessages });
523
+ }
524
+ var defaultI18n = createDefaultI18n();
417
525
  function useI18n() {
418
526
  const value = (0, import_react.useContext)(I18nReactContext);
419
527
  if (value === null) {
420
528
  return defaultI18n;
421
529
  }
530
+ const fallback = createDefaultI18n(value.locale);
422
531
  return {
423
532
  ...value,
424
533
  t: (key, params) => {
425
534
  const translated = value.t(key, params);
426
- return translated === key ? defaultI18n.t(key, params) : translated;
535
+ return translated === key ? fallback.t(key, params) : translated;
427
536
  }
428
537
  };
429
538
  }
@@ -488,6 +597,45 @@ function registerBuiltinActions(registry) {
488
597
  }
489
598
  return { ok: true };
490
599
  });
600
+ register("call", async (action, ctx) => {
601
+ const name = action.params?.["name"];
602
+ if (typeof name !== "string" || !name) {
603
+ return {
604
+ ok: false,
605
+ error: new Error("[@pactor-app/runtime] call \u52A8\u4F5C\u7F3A\u5C11 name \u53C2\u6570")
606
+ };
607
+ }
608
+ const target = ctx.context.namedActions[name];
609
+ if (target === void 0) {
610
+ return {
611
+ ok: false,
612
+ error: new Error(
613
+ `[@pactor-app/runtime] unknown named action: "${name}"`
614
+ )
615
+ };
616
+ }
617
+ const depth = (callDepth.get(ctx.context) ?? 0) + 1;
618
+ if (depth > MAX_CALL_DEPTH) {
619
+ return {
620
+ ok: false,
621
+ error: new Error(
622
+ `[@pactor-app/runtime] call \u5D4C\u5957\u8D85\u8FC7 ${MAX_CALL_DEPTH} \u5C42\uFF08\u7591\u4F3C\u5FAA\u73AF\u5F15\u7528\uFF09: "${name}"`
623
+ )
624
+ };
625
+ }
626
+ callDepth.set(ctx.context, depth);
627
+ try {
628
+ for (const child of asActions(target)) {
629
+ const result = await ctx.execute(child);
630
+ if (!result.ok) {
631
+ return result;
632
+ }
633
+ }
634
+ return { ok: true };
635
+ } finally {
636
+ callDepth.set(ctx.context, depth - 1);
637
+ }
638
+ });
491
639
  register("parallel", async (action, ctx) => {
492
640
  const results = await Promise.all(
493
641
  asActions(action.params?.["actions"]).map((child) => ctx.execute(child))
@@ -544,6 +692,8 @@ function asActions(value) {
544
692
  }
545
693
  return [];
546
694
  }
695
+ var callDepth = /* @__PURE__ */ new WeakMap();
696
+ var MAX_CALL_DEPTH = 20;
547
697
  function isRecord(value) {
548
698
  return typeof value === "object" && value !== null && !Array.isArray(value);
549
699
  }
@@ -597,7 +747,7 @@ var ActionEngine = class {
597
747
  // Renderer 的 resolveProps)保持原样,由各自执行/渲染时
598
748
  // 按当时的 state 与作用域求值
599
749
  params: resolveDeep(action.params ?? {}, scope, {
600
- isOpaque: (value) => typeof value["action"] === "string" || typeof value["type"] === "string"
750
+ isOpaque: (value) => typeof value["action"] === "string" || typeof value["type"] === "string" || typeof value["component"] === "string"
601
751
  })
602
752
  };
603
753
  const ctx = {
@@ -632,15 +782,57 @@ var ActionEngine = class {
632
782
  };
633
783
 
634
784
  // src/renderer/renderer.tsx
785
+ var import_core3 = require("@pactor-app/core");
635
786
  var import_expression3 = require("@pactor-app/expression");
636
- var import_react3 = require("react");
787
+ var import_schema = require("@pactor-app/schema");
788
+ var import_react4 = require("react");
637
789
 
638
- // src/renderer/renderer-context.ts
790
+ // src/renderer/dsl-source-boundary.tsx
639
791
  var import_react2 = require("react");
640
- var RendererReactContext = (0, import_react2.createContext)(null);
792
+ var import_jsx_runtime = require("react/jsx-runtime");
793
+ function DslSourceBoundary({
794
+ context,
795
+ sourceRef,
796
+ placeholder,
797
+ children
798
+ }) {
799
+ const [state, setState] = (0, import_react2.useState)({ status: "loading" });
800
+ const key = dslSourceKey(sourceRef);
801
+ (0, import_react2.useEffect)(() => {
802
+ let cancelled = false;
803
+ setState({ status: "loading" });
804
+ context.dslSources.resolve(sourceRef).then(
805
+ (content) => {
806
+ if (!cancelled) {
807
+ setState({ status: "ready", content });
808
+ }
809
+ },
810
+ (cause) => {
811
+ if (!cancelled) {
812
+ console.warn("[@pactor-app/runtime] DSL \u52A8\u6001\u5F15\u7528\u89E3\u6790\u5931\u8D25", cause);
813
+ setState({ status: "error" });
814
+ }
815
+ }
816
+ );
817
+ return () => {
818
+ cancelled = true;
819
+ };
820
+ }, [context, key]);
821
+ if (state.status === "ready") {
822
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: children(state.content) });
823
+ }
824
+ if (state.status === "error") {
825
+ return null;
826
+ }
827
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: placeholder ?? null });
828
+ }
829
+
830
+ // src/renderer/renderer-context.ts
831
+ var import_react3 = require("react");
832
+ var RendererReactContext = (0, import_react3.createContext)(null);
641
833
  var RendererContextProvider = RendererReactContext.Provider;
642
834
  function useRenderer() {
643
- const value = (0, import_react2.useContext)(RendererReactContext);
835
+ const value = (0, import_react3.useContext)(RendererReactContext);
644
836
  if (!value) {
645
837
  throw new Error("[@pactor-app/runtime] useRenderer \u5FC5\u987B\u5728 <DslRenderer> \u5185\u4F7F\u7528");
646
838
  }
@@ -648,23 +840,23 @@ function useRenderer() {
648
840
  }
649
841
  function useRuntimeState(path) {
650
842
  const { context } = useRenderer();
651
- return (0, import_react2.useSyncExternalStore)(
843
+ return (0, import_react3.useSyncExternalStore)(
652
844
  (onChange) => context.state.subscribe(onChange),
653
845
  () => path === void 0 ? context.state.getState() : context.state.get(path)
654
846
  );
655
847
  }
656
848
  function useDataSource(name) {
657
849
  const { context } = useRenderer();
658
- return (0, import_react2.useSyncExternalStore)(
850
+ return (0, import_react3.useSyncExternalStore)(
659
851
  (onChange) => context.data.subscribe(onChange),
660
852
  () => context.data.get(name)
661
853
  );
662
854
  }
663
855
 
664
856
  // src/renderer/unsupported.tsx
665
- var import_jsx_runtime = require("react/jsx-runtime");
857
+ var import_jsx_runtime2 = require("react/jsx-runtime");
666
858
  function UnsupportedComponent({ type }) {
667
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
859
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
668
860
  "div",
669
861
  {
670
862
  "data-pactor-unsupported": type,
@@ -684,9 +876,9 @@ function UnsupportedComponent({ type }) {
684
876
  }
685
877
 
686
878
  // src/renderer/renderer.tsx
687
- var import_jsx_runtime2 = require("react/jsx-runtime");
879
+ var import_jsx_runtime3 = require("react/jsx-runtime");
688
880
  function DslRenderer({ context, node, engine, children }) {
689
- (0, import_react3.useSyncExternalStore)(
881
+ (0, import_react4.useSyncExternalStore)(
690
882
  (onChange) => {
691
883
  const offState = context.state.subscribe(onChange);
692
884
  const offData = context.data.subscribe(onChange);
@@ -697,26 +889,65 @@ function DslRenderer({ context, node, engine, children }) {
697
889
  },
698
890
  () => `${context.state.getVersion()}:${context.data.getVersion()}`
699
891
  );
700
- const resolvedEngine = (0, import_react3.useMemo)(
892
+ const resolvedEngine = (0, import_react4.useMemo)(
701
893
  () => engine ?? new ActionEngine(context),
702
894
  [engine, context]
703
895
  );
704
- const value = (0, import_react3.useMemo)(() => {
896
+ const value = (0, import_react4.useMemo)(() => {
705
897
  const root = {
706
898
  context,
707
899
  engine: resolvedEngine,
708
900
  scope: {},
901
+ referenceStack: [],
709
902
  renderChildren: (children2, extraScope) => renderChildrenOf(root, children2, extraScope)
710
903
  };
711
904
  return root;
712
905
  }, [context, resolvedEngine]);
713
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(RendererContextProvider, { value, children: [
906
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(RendererContextProvider, { value, children: [
714
907
  node ? renderNode(node, value, node.id ?? "root") : null,
715
908
  children
716
909
  ] });
717
910
  }
718
911
  function renderNode(node, value, key) {
719
912
  const scope = buildScope(value.context, value.scope);
913
+ if (node.component !== void 0) {
914
+ const name = node.component;
915
+ const stack = value.referenceStack ?? [];
916
+ if (stack.includes(name)) {
917
+ console.warn(
918
+ `[@pactor-app/runtime] \u7EC4\u4EF6\u5F15\u7528\u5FAA\u73AF\uFF1A${[...stack, name].join(" \u2192 ")}`
919
+ );
920
+ return null;
921
+ }
922
+ const subtree = value.context.pageComponents[name];
923
+ if (!subtree) {
924
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnsupportedComponent, { type: `component(${name})` }, key);
925
+ }
926
+ const scoped = {
927
+ ...value,
928
+ referenceStack: [...stack, name]
929
+ };
930
+ scoped.renderChildren = (children, extraScope) => renderChildrenOf(scoped, children, extraScope);
931
+ if ((0, import_core3.isDslSourceRef)(subtree)) {
932
+ const ref = subtree;
933
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
934
+ DslSourceBoundary,
935
+ {
936
+ context: value.context,
937
+ sourceRef: ref,
938
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, scoped, "placeholder") : null,
939
+ children: (content) => {
940
+ const resolved = validateResolvedNode(content, `components.${name}`);
941
+ if (!resolved) {
942
+ return null;
943
+ }
944
+ return renderNode(mergeReferenceNode(resolved, node), scoped, key);
945
+ }
946
+ }
947
+ ) }, key);
948
+ }
949
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: renderNode(mergeReferenceNode(subtree, node), scoped, key) }, key);
950
+ }
720
951
  if (node.when !== void 0) {
721
952
  const expression = typeof node.when === "string" ? node.when : node.when.expression;
722
953
  let visible;
@@ -743,7 +974,7 @@ function renderNode(node, value, key) {
743
974
  const entry = value.context.components.get(node.type);
744
975
  const normalized = normalizeEntry(entry);
745
976
  if (!normalized) {
746
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UnsupportedComponent, { type: node.type }, key);
977
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnsupportedComponent, { type: node.type }, key);
747
978
  }
748
979
  const props = resolveProps(node.props, scope);
749
980
  if (node.events) {
@@ -759,13 +990,13 @@ function renderNode(node, value, key) {
759
990
  props["readOnly"] = true;
760
991
  }
761
992
  if (normalized.withScope) {
762
- return (0, import_react3.createElement)(normalized.component, {
993
+ return (0, import_react4.createElement)(normalized.component, {
763
994
  key,
764
995
  ...props,
765
996
  dslChildren: node.children
766
997
  });
767
998
  }
768
- return (0, import_react3.createElement)(
999
+ return (0, import_react4.createElement)(
769
1000
  normalized.component,
770
1001
  { key, ...props },
771
1002
  renderChildrenOf(value, node.children)
@@ -777,23 +1008,102 @@ function resolveDeniedStrategy(permission) {
777
1008
  }
778
1009
  return "hidden";
779
1010
  }
1011
+ function mergeReferenceNode(definition, reference) {
1012
+ return {
1013
+ ...definition,
1014
+ props: { ...definition.props, ...reference.props },
1015
+ events: { ...definition.events, ...reference.events },
1016
+ when: reference.when ?? definition.when,
1017
+ permission: reference.permission ?? definition.permission,
1018
+ id: reference.id ?? definition.id
1019
+ };
1020
+ }
1021
+ function validateResolvedNode(content, label) {
1022
+ const errors = (0, import_schema.validateDslNode)(content);
1023
+ if (errors.length > 0) {
1024
+ console.warn(
1025
+ `[@pactor-app/runtime] \u52A8\u6001\u5F15\u7528\uFF08${label}\uFF09\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C`,
1026
+ errors
1027
+ );
1028
+ return null;
1029
+ }
1030
+ return content;
1031
+ }
1032
+ function validateResolvedNodes(content, label) {
1033
+ const errors = (0, import_schema.validateDslNodes)(content);
1034
+ if (errors.length > 0) {
1035
+ console.warn(
1036
+ `[@pactor-app/runtime] \u52A8\u6001\u5F15\u7528\uFF08${label}\uFF09\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C`,
1037
+ errors
1038
+ );
1039
+ return null;
1040
+ }
1041
+ return content;
1042
+ }
1043
+ function renderChildItem(value, item, key) {
1044
+ if ((0, import_core3.isDslSourceRef)(item)) {
1045
+ const ref = item;
1046
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1047
+ DslSourceBoundary,
1048
+ {
1049
+ context: value.context,
1050
+ sourceRef: ref,
1051
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, value, "placeholder") : null,
1052
+ children: (content) => {
1053
+ const nodes = validateResolvedNodes(content, "children");
1054
+ if (!nodes) {
1055
+ return null;
1056
+ }
1057
+ return nodes.map(
1058
+ (child, index) => renderChildItem(value, child, (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index)
1059
+ );
1060
+ }
1061
+ },
1062
+ key
1063
+ );
1064
+ }
1065
+ return renderNode(item, value, key);
1066
+ }
780
1067
  function renderChildrenOf(value, children, extraScope) {
781
- if (!children || children.length === 0) {
1068
+ if (!children || Array.isArray(children) && children.length === 0) {
782
1069
  return null;
783
1070
  }
1071
+ if ((0, import_core3.isDslSourceRef)(children)) {
1072
+ const ref = children;
1073
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1074
+ DslSourceBoundary,
1075
+ {
1076
+ context: value.context,
1077
+ sourceRef: ref,
1078
+ placeholder: ref.placeholder ? renderNode(ref.placeholder, value, "placeholder") : null,
1079
+ children: (content) => {
1080
+ const nodes = validateResolvedNodes(content, "children");
1081
+ if (!nodes) {
1082
+ return null;
1083
+ }
1084
+ return nodes.map(
1085
+ (child, index) => renderChildItem(value, child, (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index)
1086
+ );
1087
+ }
1088
+ },
1089
+ "dsl-source"
1090
+ );
1091
+ }
1092
+ const itemKey = (child, index) => (0, import_core3.isDslSourceRef)(child) ? `ref-${index}` : child.id ?? index;
784
1093
  if (!extraScope) {
785
1094
  return children.map(
786
- (child, index) => renderNode(child, value, child.id ?? index)
1095
+ (child, index) => renderChildItem(value, child, itemKey(child, index))
787
1096
  );
788
1097
  }
789
1098
  const scoped = {
790
1099
  context: value.context,
791
1100
  engine: value.engine,
792
1101
  scope: { ...value.scope, ...extraScope },
1102
+ referenceStack: value.referenceStack,
793
1103
  renderChildren: (nested, nestedScope) => renderChildrenOf(scoped, nested, nestedScope)
794
1104
  };
795
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RendererContextProvider, { value: scoped, children: children.map(
796
- (child, index) => renderNode(child, scoped, child.id ?? index)
1105
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RendererContextProvider, { value: scoped, children: children.map(
1106
+ (child, index) => renderChildItem(scoped, child, itemKey(child, index))
797
1107
  ) });
798
1108
  }
799
1109
  function normalizeEntry(entry) {
@@ -816,12 +1126,13 @@ function resolveProps(props, scope) {
816
1126
  return out;
817
1127
  }
818
1128
  function isEmbeddedDslNode(value) {
819
- return typeof value.type === "string";
1129
+ return typeof value.type === "string" || typeof value.component === "string";
820
1130
  }
821
1131
 
822
1132
  // src/page/page-runtime.tsx
823
- var import_schema = require("@pactor-app/schema");
824
- var import_jsx_runtime3 = require("react/jsx-runtime");
1133
+ var import_core4 = require("@pactor-app/core");
1134
+ var import_schema2 = require("@pactor-app/schema");
1135
+ var import_jsx_runtime4 = require("react/jsx-runtime");
825
1136
  var PageRuntimeError = class extends Error {
826
1137
  constructor(errors) {
827
1138
  super(
@@ -832,7 +1143,7 @@ var PageRuntimeError = class extends Error {
832
1143
  }
833
1144
  };
834
1145
  function createPageRuntime(pageDsl, options = {}) {
835
- const validation = (0, import_schema.validatePageDsl)(pageDsl);
1146
+ const validation = (0, import_schema2.validatePageDsl)(pageDsl);
836
1147
  if (!validation.valid) {
837
1148
  throw new PageRuntimeError(validation.errors);
838
1149
  }
@@ -840,7 +1151,9 @@ function createPageRuntime(pageDsl, options = {}) {
840
1151
  ...options,
841
1152
  page: pageDsl.page,
842
1153
  state: pageDsl.state ?? options.state,
843
- dataSources: pageDsl.dataSources ?? options.dataSources
1154
+ dataSources: pageDsl.dataSources ?? options.dataSources,
1155
+ namedActions: pageDsl.actions ?? options.namedActions,
1156
+ pageComponents: pageDsl.components ?? options.pageComponents
844
1157
  });
845
1158
  registerBuiltinActions(context.actions);
846
1159
  const engine = new ActionEngine(context);
@@ -876,10 +1189,7 @@ function createPageRuntime(pageDsl, options = {}) {
876
1189
  return destroyed;
877
1190
  },
878
1191
  render() {
879
- if (!pageDsl.body) {
880
- return null;
881
- }
882
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DslRenderer, { context, node: pageDsl.body, engine });
1192
+ return renderRoots(pageDsl, context, engine);
883
1193
  },
884
1194
  ready: () => triggerWhenAlive("onReady"),
885
1195
  show: () => triggerWhenAlive("onShow"),
@@ -907,17 +1217,68 @@ function PageRuntimeView({
907
1217
  runtime,
908
1218
  children
909
1219
  }) {
910
- const body = runtime.pageDsl.body;
911
- if (!body) {
912
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: children ?? null });
1220
+ return renderRoots(runtime.pageDsl, runtime.context, runtime.engine, children);
1221
+ }
1222
+ function renderRoots(pageDsl, context, engine, children) {
1223
+ if (!pageDsl.body && (0, import_core4.isDslSourceRef)(pageDsl.children)) {
1224
+ const ref = pageDsl.children;
1225
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1226
+ DslSourceBoundary,
1227
+ {
1228
+ context,
1229
+ sourceRef: ref,
1230
+ placeholder: ref.placeholder ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DslRenderer, { context, node: ref.placeholder, engine }) : null,
1231
+ children: (content) => renderResolvedRoots(content, context, engine, children)
1232
+ }
1233
+ );
1234
+ }
1235
+ const roots = (0, import_core4.resolveRootNodes)(pageDsl);
1236
+ if (roots.length === 0) {
1237
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children ?? null });
913
1238
  }
914
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DslRenderer, { context: runtime.context, node: body, engine: runtime.engine, children });
1239
+ return renderRootItems(roots, context, engine, children);
1240
+ }
1241
+ function renderResolvedRoots(content, context, engine, children) {
1242
+ const errors = (0, import_schema2.validateDslNodes)(content);
1243
+ if (errors.length > 0) {
1244
+ console.warn(
1245
+ "[@pactor-app/runtime] \u6839\u7EA7 children \u52A8\u6001\u5F15\u7528\u8FD4\u56DE\u5185\u5BB9\u672A\u901A\u8FC7\u6821\u9A8C",
1246
+ errors
1247
+ );
1248
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children ?? null });
1249
+ }
1250
+ return renderRootItems(content, context, engine, children);
1251
+ }
1252
+ function renderRootItems(items, context, engine, children) {
1253
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: items.map(
1254
+ (item, index) => (0, import_core4.isDslSourceRef)(item) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1255
+ DslSourceBoundary,
1256
+ {
1257
+ context,
1258
+ sourceRef: item,
1259
+ placeholder: item.placeholder ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DslRenderer, { context, node: item.placeholder, engine }) : null,
1260
+ children: (content) => renderResolvedRoots(content, context, engine, children)
1261
+ },
1262
+ dslSourceKey(item)
1263
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1264
+ DslRenderer,
1265
+ {
1266
+ context,
1267
+ node: item,
1268
+ engine,
1269
+ children: index === items.length - 1 ? children : null
1270
+ },
1271
+ item.id ?? index
1272
+ )
1273
+ ) });
915
1274
  }
916
1275
  // Annotate the CommonJS export names for ESM import in node:
917
1276
  0 && (module.exports = {
918
1277
  ActionEngine,
919
1278
  DataStore,
920
1279
  DslRenderer,
1280
+ DslSourceBoundary,
1281
+ DslSourceResolver,
921
1282
  I18nContextProvider,
922
1283
  PageRuntimeError,
923
1284
  PageRuntimeView,
@@ -929,6 +1290,7 @@ function PageRuntimeView({
929
1290
  createFetchHttpAdapter,
930
1291
  createPageRuntime,
931
1292
  createRuntimeContext,
1293
+ dslSourceKey,
932
1294
  registerBuiltinActions,
933
1295
  resolveDeep,
934
1296
  useDataSource,