@json-render/solid 0.17.0 → 0.19.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.mjs CHANGED
@@ -195,7 +195,7 @@ import { insert as _$insert } from "solid-js/web";
195
195
  import { addEventListener as _$addEventListener } from "solid-js/web";
196
196
  import { createComponent as _$createComponent4 } from "solid-js/web";
197
197
  import { createContext as createContext4, useContext as useContext4, createSignal as createSignal3 } from "solid-js";
198
- import { resolveAction, executeAction } from "@json-render/core";
198
+ import { resolveAction, executeAction, nextActionDispatchId, notifyActionDispatch, notifyActionSettle } from "@json-render/core";
199
199
 
200
200
  // src/contexts/validation.tsx
201
201
  import { createComponent as _$createComponent3 } from "solid-js/web";
@@ -431,146 +431,172 @@ function ActionProvider(props) {
431
431
  };
432
432
  const execute = async (binding) => {
433
433
  const resolved = resolveAction(binding, getSnapshot());
434
- if (resolved.action === "setState" && resolved.params) {
435
- const statePath = resolved.params.statePath;
436
- const value = resolved.params.value;
437
- if (statePath) {
438
- set(statePath, value);
439
- }
440
- return;
441
- }
442
- if (resolved.action === "pushState" && resolved.params) {
443
- const statePath = resolved.params.statePath;
444
- const rawValue = resolved.params.value;
445
- if (statePath) {
446
- const resolvedValue = deepResolveValue(rawValue, get);
447
- const arr = get(statePath) ?? [];
448
- set(statePath, [...arr, resolvedValue]);
449
- const clearStatePath = resolved.params.clearStatePath;
450
- if (clearStatePath) {
451
- set(clearStatePath, "");
434
+ const dispatchId = nextActionDispatchId();
435
+ const dispatchedAt = Date.now();
436
+ notifyActionDispatch({
437
+ id: dispatchId,
438
+ name: resolved.action,
439
+ params: resolved.params,
440
+ at: dispatchedAt
441
+ });
442
+ let __ok = true;
443
+ let __error = void 0;
444
+ try {
445
+ if (resolved.action === "setState" && resolved.params) {
446
+ const statePath = resolved.params.statePath;
447
+ const value = resolved.params.value;
448
+ if (statePath) {
449
+ set(statePath, value);
452
450
  }
451
+ return;
453
452
  }
454
- return;
455
- }
456
- if (resolved.action === "removeState" && resolved.params) {
457
- const statePath = resolved.params.statePath;
458
- const index = resolved.params.index;
459
- if (statePath !== void 0 && index !== void 0) {
460
- const arr = get(statePath) ?? [];
461
- set(statePath, arr.filter((_, i) => i !== index));
462
- }
463
- return;
464
- }
465
- if (resolved.action === "push" && resolved.params) {
466
- const screen = resolved.params.screen;
467
- if (screen) {
468
- const currentScreen = get("/currentScreen");
469
- const navStack = get("/navStack") ?? [];
470
- if (currentScreen) {
471
- set("/navStack", [...navStack, currentScreen]);
472
- } else {
473
- set("/navStack", [...navStack, ""]);
453
+ if (resolved.action === "pushState" && resolved.params) {
454
+ const statePath = resolved.params.statePath;
455
+ const rawValue = resolved.params.value;
456
+ if (statePath) {
457
+ const resolvedValue = deepResolveValue(rawValue, get);
458
+ const arr = get(statePath) ?? [];
459
+ set(statePath, [...arr, resolvedValue]);
460
+ const clearStatePath = resolved.params.clearStatePath;
461
+ if (clearStatePath) {
462
+ set(clearStatePath, "");
463
+ }
474
464
  }
475
- set("/currentScreen", screen);
465
+ return;
476
466
  }
477
- return;
478
- }
479
- if (resolved.action === "pop") {
480
- const navStack = get("/navStack") ?? [];
481
- if (navStack.length > 0) {
482
- const previousScreen = navStack[navStack.length - 1];
483
- set("/navStack", navStack.slice(0, -1));
484
- if (previousScreen) {
485
- set("/currentScreen", previousScreen);
486
- } else {
487
- set("/currentScreen", void 0);
467
+ if (resolved.action === "removeState" && resolved.params) {
468
+ const statePath = resolved.params.statePath;
469
+ const index = resolved.params.index;
470
+ if (statePath !== void 0 && index !== void 0) {
471
+ const arr = get(statePath) ?? [];
472
+ set(statePath, arr.filter((_, i) => i !== index));
488
473
  }
474
+ return;
489
475
  }
490
- return;
491
- }
492
- if (resolved.action === "validateForm") {
493
- const validateAll = validation?.validateAll;
494
- if (!validateAll) {
495
- console.warn("validateForm action was dispatched but no ValidationProvider is connected. Ensure ValidationProvider is rendered inside the provider tree.");
476
+ if (resolved.action === "push" && resolved.params) {
477
+ const screen = resolved.params.screen;
478
+ if (screen) {
479
+ const currentScreen = get("/currentScreen");
480
+ const navStack = get("/navStack") ?? [];
481
+ if (currentScreen) {
482
+ set("/navStack", [...navStack, currentScreen]);
483
+ } else {
484
+ set("/navStack", [...navStack, ""]);
485
+ }
486
+ set("/currentScreen", screen);
487
+ }
496
488
  return;
497
489
  }
498
- const valid = validateAll();
499
- const errors = {};
500
- for (const [path, fs] of Object.entries(validation.fieldStates)) {
501
- if (fs.result && !fs.result.valid) {
502
- errors[path] = fs.result.errors;
490
+ if (resolved.action === "pop") {
491
+ const navStack = get("/navStack") ?? [];
492
+ if (navStack.length > 0) {
493
+ const previousScreen = navStack[navStack.length - 1];
494
+ set("/navStack", navStack.slice(0, -1));
495
+ if (previousScreen) {
496
+ set("/currentScreen", previousScreen);
497
+ } else {
498
+ set("/currentScreen", void 0);
499
+ }
503
500
  }
501
+ return;
504
502
  }
505
- const statePath = resolved.params?.statePath || "/formValidation";
506
- set(statePath, {
507
- valid,
508
- errors
509
- });
510
- return;
511
- }
512
- const handler = handlers()[resolved.action];
513
- if (!handler) {
514
- console.warn(`No handler registered for action: ${resolved.action}`);
515
- return;
516
- }
517
- if (resolved.confirm) {
518
- return new Promise((resolve, reject) => {
519
- setPendingConfirmation({
520
- action: resolved,
521
- handler,
522
- resolve: () => {
523
- setPendingConfirmation(null);
524
- resolve();
525
- },
526
- reject: () => {
527
- setPendingConfirmation(null);
528
- reject(new Error("Action cancelled"));
503
+ if (resolved.action === "validateForm") {
504
+ const validateAll = validation?.validateAll;
505
+ if (!validateAll) {
506
+ console.warn("validateForm action was dispatched but no ValidationProvider is connected. Ensure ValidationProvider is rendered inside the provider tree.");
507
+ return;
508
+ }
509
+ const valid = validateAll();
510
+ const errors = {};
511
+ for (const [path, fs] of Object.entries(validation.fieldStates)) {
512
+ if (fs.result && !fs.result.valid) {
513
+ errors[path] = fs.result.errors;
529
514
  }
515
+ }
516
+ const statePath = resolved.params?.statePath || "/formValidation";
517
+ set(statePath, {
518
+ valid,
519
+ errors
530
520
  });
531
- }).then(async () => {
532
- setLoadingActions((prev) => new Set(prev).add(resolved.action));
533
- try {
534
- await executeAction({
521
+ return;
522
+ }
523
+ const handler = handlers()[resolved.action];
524
+ if (!handler) {
525
+ console.warn(`No handler registered for action: ${resolved.action}`);
526
+ return;
527
+ }
528
+ if (resolved.confirm) {
529
+ return new Promise((resolve, reject) => {
530
+ setPendingConfirmation({
535
531
  action: resolved,
536
532
  handler,
537
- setState: set,
538
- navigate: props.navigate,
539
- executeAction: async (name) => {
540
- const subBinding = {
541
- action: name
542
- };
543
- await execute(subBinding);
533
+ resolve: () => {
534
+ setPendingConfirmation(null);
535
+ resolve();
536
+ },
537
+ reject: () => {
538
+ setPendingConfirmation(null);
539
+ reject(new Error("Action cancelled"));
544
540
  }
545
541
  });
546
- } finally {
547
- setLoadingActions((prev) => {
548
- const next = new Set(prev);
549
- next.delete(resolved.action);
550
- return next;
551
- });
552
- }
553
- });
554
- }
555
- setLoadingActions((prev) => new Set(prev).add(resolved.action));
556
- try {
557
- await executeAction({
558
- action: resolved,
559
- handler,
560
- setState: set,
561
- navigate: props.navigate,
562
- executeAction: async (name) => {
563
- const subBinding = {
564
- action: name
565
- };
566
- await execute(subBinding);
567
- }
568
- });
542
+ }).then(async () => {
543
+ setLoadingActions((prev) => new Set(prev).add(resolved.action));
544
+ try {
545
+ await executeAction({
546
+ action: resolved,
547
+ handler,
548
+ setState: set,
549
+ navigate: props.navigate,
550
+ executeAction: async (name) => {
551
+ const subBinding = {
552
+ action: name
553
+ };
554
+ await execute(subBinding);
555
+ }
556
+ });
557
+ } finally {
558
+ setLoadingActions((prev) => {
559
+ const next = new Set(prev);
560
+ next.delete(resolved.action);
561
+ return next;
562
+ });
563
+ }
564
+ });
565
+ }
566
+ setLoadingActions((prev) => new Set(prev).add(resolved.action));
567
+ try {
568
+ await executeAction({
569
+ action: resolved,
570
+ handler,
571
+ setState: set,
572
+ navigate: props.navigate,
573
+ executeAction: async (name) => {
574
+ const subBinding = {
575
+ action: name
576
+ };
577
+ await execute(subBinding);
578
+ }
579
+ });
580
+ } finally {
581
+ setLoadingActions((prev) => {
582
+ const next = new Set(prev);
583
+ next.delete(resolved.action);
584
+ return next;
585
+ });
586
+ }
587
+ } catch (err) {
588
+ __ok = false;
589
+ __error = err;
590
+ throw err;
569
591
  } finally {
570
- setLoadingActions((prev) => {
571
- const next = new Set(prev);
572
- next.delete(resolved.action);
573
- return next;
592
+ const now = Date.now();
593
+ notifyActionSettle({
594
+ id: dispatchId,
595
+ name: resolved.action,
596
+ ok: __ok,
597
+ at: now,
598
+ durationMs: now - dispatchedAt,
599
+ error: __error
574
600
  });
575
601
  }
576
602
  };
@@ -662,15 +688,30 @@ function useRepeatScope() {
662
688
  import { createStateStore as createStateStore2 } from "@json-render/core";
663
689
 
664
690
  // src/renderer.tsx
691
+ import { template as _$template2 } from "solid-js/web";
692
+ import { setAttribute as _$setAttribute } from "solid-js/web";
693
+ import { effect as _$effect2 } from "solid-js/web";
694
+ import { insert as _$insert2 } from "solid-js/web";
665
695
  import { memo as _$memo2 } from "solid-js/web";
666
696
  import { createComponent as _$createComponent6 } from "solid-js/web";
667
- import { createContext as createContext6, useContext as useContext6, createMemo as createMemo3, createEffect as createEffect3, onCleanup as onCleanup2, ErrorBoundary, Show, For } from "solid-js";
668
- import { resolveElementProps, resolveBindings, resolveActionParam, evaluateVisibility as evaluateVisibility2, getByPath as getByPath2 } from "@json-render/core";
697
+ import { createContext as createContext6, useContext as useContext6, createMemo as createMemo3, createEffect as createEffect3, createSignal as createSignal4, onCleanup as onCleanup2, ErrorBoundary, Show, For } from "solid-js";
698
+ import { resolveElementProps, resolveBindings, resolveActionParam, evaluateVisibility as evaluateVisibility2, getByPath as getByPath2, isDevtoolsActive, subscribeDevtoolsActive, createDirectiveRegistry } from "@json-render/core";
699
+ var _tmpl$2 = /* @__PURE__ */ _$template2(`<span style=display:contents>`);
669
700
  var EMPTY_FUNCTIONS = {};
670
701
  var FunctionsContext = createContext6(EMPTY_FUNCTIONS);
671
702
  function useFunctions() {
672
703
  return useContext6(FunctionsContext) ?? EMPTY_FUNCTIONS;
673
704
  }
705
+ var DirectivesContext = createContext6(void 0);
706
+ function useDirectives() {
707
+ return useContext6(DirectivesContext);
708
+ }
709
+ function useDevtoolsActive() {
710
+ const [active, setActive] = createSignal4(isDevtoolsActive());
711
+ const unsub = subscribeDevtoolsActive(() => setActive(isDevtoolsActive()));
712
+ onCleanup2(unsub);
713
+ return active;
714
+ }
674
715
  function ElementRenderer(props) {
675
716
  const repeatScope = useRepeatScope();
676
717
  const {
@@ -682,6 +723,7 @@ function ElementRenderer(props) {
682
723
  const stateStore = useStateStore();
683
724
  const getSnapshot = () => stateStore.getSnapshot();
684
725
  const functions = useFunctions();
726
+ const directives = useDirectives();
685
727
  const fullCtx = createMemo3(() => {
686
728
  const repeatItem = repeatScope?.item;
687
729
  const repeatIndex = repeatScope?.index;
@@ -699,7 +741,8 @@ function ElementRenderer(props) {
699
741
  ...repeatBasePath !== void 0 ? {
700
742
  repeatBasePath
701
743
  } : {},
702
- functions
744
+ functions,
745
+ directives
703
746
  };
704
747
  });
705
748
  const isVisible = createMemo3(() => props.element.visible === void 0 ? true : evaluateVisibility2(props.element.visible, fullCtx()));
@@ -811,6 +854,9 @@ function ElementRenderer(props) {
811
854
  get resolvedElement() {
812
855
  return resolvedElement();
813
856
  },
857
+ get elementKey() {
858
+ return props.elementKey;
859
+ },
814
860
  get spec() {
815
861
  return props.spec;
816
862
  },
@@ -835,6 +881,7 @@ function ElementRenderer(props) {
835
881
  });
836
882
  }
837
883
  function ElementRendererContent(props) {
884
+ const devtoolsActive = useDevtoolsActive();
838
885
  const Comp = () => props.registry[props.resolvedElement.type] ?? props.fallback;
839
886
  return _$createComponent6(Show, {
840
887
  get when() {
@@ -882,6 +929,7 @@ function ElementRendererContent(props) {
882
929
  get element() {
883
930
  return el();
884
931
  },
932
+ elementKey: childKey,
885
933
  get spec() {
886
934
  return props.spec;
887
935
  },
@@ -898,7 +946,7 @@ function ElementRendererContent(props) {
898
946
  });
899
947
  }
900
948
  });
901
- return _$createComponent6(Component, {
949
+ const rendered = _$createComponent6(Component, {
902
950
  get element() {
903
951
  return props.resolvedElement;
904
952
  },
@@ -918,6 +966,18 @@ function ElementRendererContent(props) {
918
966
  return children();
919
967
  }
920
968
  });
969
+ return _$createComponent6(Show, {
970
+ get when() {
971
+ return _$memo2(() => !!devtoolsActive())() && props.elementKey;
972
+ },
973
+ fallback: rendered,
974
+ get children() {
975
+ var _el$ = _tmpl$2();
976
+ _$insert2(_el$, rendered);
977
+ _$effect2(() => _$setAttribute(_el$, "data-jr-key", props.elementKey));
978
+ return _el$;
979
+ }
980
+ });
921
981
  }
922
982
  });
923
983
  }
@@ -999,6 +1059,9 @@ function Renderer(props) {
999
1059
  get element() {
1000
1060
  return el();
1001
1061
  },
1062
+ get elementKey() {
1063
+ return props.spec.root;
1064
+ },
1002
1065
  get spec() {
1003
1066
  return props.spec;
1004
1067
  },
@@ -1015,6 +1078,7 @@ function Renderer(props) {
1015
1078
  });
1016
1079
  }
1017
1080
  function JSONUIProvider(props) {
1081
+ const directiveRegistry = createMemo3(() => props.directives ? createDirectiveRegistry(props.directives) : void 0);
1018
1082
  return _$createComponent6(StateProvider, {
1019
1083
  get store() {
1020
1084
  return props.store;
@@ -1046,7 +1110,14 @@ function JSONUIProvider(props) {
1046
1110
  return props.functions ?? EMPTY_FUNCTIONS;
1047
1111
  },
1048
1112
  get children() {
1049
- return [_$memo2(() => props.children), _$createComponent6(ConfirmationDialogManager, {})];
1113
+ return _$createComponent6(DirectivesContext.Provider, {
1114
+ get value() {
1115
+ return directiveRegistry();
1116
+ },
1117
+ get children() {
1118
+ return [_$memo2(() => props.children), _$createComponent6(ConfirmationDialogManager, {})];
1119
+ }
1120
+ });
1050
1121
  }
1051
1122
  });
1052
1123
  }
@@ -1132,6 +1203,7 @@ function defineRegistry(_catalog, options) {
1132
1203
  function createRenderer(catalog, components) {
1133
1204
  const registry = components;
1134
1205
  return function CatalogRenderer(props) {
1206
+ const directiveRegistry = createMemo3(() => props.directives ? createDirectiveRegistry(props.directives) : void 0);
1135
1207
  const actionHandlers = () => props.onAction ? new Proxy({}, {
1136
1208
  get: (_target, prop) => {
1137
1209
  return (params) => props.onAction(prop, params);
@@ -1163,18 +1235,25 @@ function createRenderer(catalog, components) {
1163
1235
  return props.functions ?? EMPTY_FUNCTIONS;
1164
1236
  },
1165
1237
  get children() {
1166
- return [_$createComponent6(Renderer, {
1167
- get spec() {
1168
- return props.spec;
1169
- },
1170
- registry,
1171
- get loading() {
1172
- return props.loading;
1238
+ return _$createComponent6(DirectivesContext.Provider, {
1239
+ get value() {
1240
+ return directiveRegistry();
1173
1241
  },
1174
- get fallback() {
1175
- return props.fallback;
1242
+ get children() {
1243
+ return [_$createComponent6(Renderer, {
1244
+ get spec() {
1245
+ return props.spec;
1246
+ },
1247
+ registry,
1248
+ get loading() {
1249
+ return props.loading;
1250
+ },
1251
+ get fallback() {
1252
+ return props.fallback;
1253
+ }
1254
+ }), _$createComponent6(ConfirmationDialogManager, {})];
1176
1255
  }
1177
- }), _$createComponent6(ConfirmationDialogManager, {})];
1256
+ });
1178
1257
  }
1179
1258
  });
1180
1259
  }
@@ -1189,7 +1268,7 @@ function createRenderer(catalog, components) {
1189
1268
  }
1190
1269
 
1191
1270
  // src/hooks.ts
1192
- import { createSignal as createSignal4, onCleanup as onCleanup3, createMemo as createMemo4 } from "solid-js";
1271
+ import { createSignal as createSignal5, onCleanup as onCleanup3, createMemo as createMemo4 } from "solid-js";
1193
1272
  import {
1194
1273
  setByPath,
1195
1274
  getByPath as getByPath3,
@@ -1333,11 +1412,11 @@ function applyPatch(spec, patch) {
1333
1412
  return newSpec;
1334
1413
  }
1335
1414
  function useUIStream(options) {
1336
- const [spec, setSpec] = createSignal4(null);
1337
- const [isStreaming, setIsStreaming] = createSignal4(false);
1338
- const [error, setError] = createSignal4(null);
1339
- const [usage, setUsage] = createSignal4(null);
1340
- const [rawLines, setRawLines] = createSignal4([]);
1415
+ const [spec, setSpec] = createSignal5(null);
1416
+ const [isStreaming, setIsStreaming] = createSignal5(false);
1417
+ const [error, setError] = createSignal5(null);
1418
+ const [usage, setUsage] = createSignal5(null);
1419
+ const [rawLines, setRawLines] = createSignal5([]);
1341
1420
  let abortController = null;
1342
1421
  const clear = () => {
1343
1422
  setSpec(null);
@@ -1554,9 +1633,9 @@ function generateChatId() {
1554
1633
  return `msg-${Date.now()}-${chatMessageIdCounter}`;
1555
1634
  }
1556
1635
  function useChatUI(options) {
1557
- const [messages, setMessages] = createSignal4([]);
1558
- const [isStreaming, setIsStreaming] = createSignal4(false);
1559
- const [error, setError] = createSignal4(null);
1636
+ const [messages, setMessages] = createSignal5([]);
1637
+ const [isStreaming, setIsStreaming] = createSignal5(false);
1638
+ const [error, setError] = createSignal5(null);
1560
1639
  let abortController = null;
1561
1640
  const clear = () => {
1562
1641
  setMessages([]);