lightview 1.5.1-b → 1.6.4-b

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # lightview v1.4.10b (BETA)
1
+ # lightview v1.6.4b (BETA)
2
2
 
3
3
  Small, simple, powerful web UI and micro front end creation ...
4
4
 
@@ -6,3 +6,37 @@ Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 7K
6
6
 
7
7
  See the docs and examples at [https://lightview.dev](https://lightview.dev).
8
8
 
9
+ Meanwhile, here is what you get:
10
+
11
+ 1) Single file and <a href="#local-templates" target=_self>template</a> components.
12
+
13
+ 1) [Sandboxed remote components](https://lightview.dev/#sandboxed-components) and micro front ends</a>.
14
+
15
+ 1) [Unit testable](https://lightview.dev/#unit-testing) components and a [debug mode](https://lightview.dev/#debugging) for using standard JavaScript debuggers</a>.
16
+
17
+ 1) No pre-deployment transpilation/compilation required.
18
+
19
+ 1) Svelte like variable usage, i.e. write your state modifying code like normal code.
20
+
21
+ 1) Extended variable type declarations including `min`, `max` and `step` on `number` or limits on `string` and `array` lengths.
22
+
23
+ 1) [TypeScript like](https://lightview.dev/#variables) runtime type checking of variables in components.
24
+
25
+ 1) Automatic server retrieval and update of variables declared as `remote`.
26
+
27
+ 1) Automatic import, export, cross-component sync, or reactive response to attributes/props/variables. See [superVariable](https://lightview.dev/#super-variable).
28
+
29
+ 1) [Automatic form variable creation and binding](https://lightview.dev/#auto-binding-forms).
30
+
31
+ 1) [Attribute directives](https://lightview.dev/#attribute-directives) like `l-if`, and a single powerful `l-for` that handles array and object keys, values, and entries.
32
+
33
+ 1) Reactive string template literals for content and attribute value replacement.
34
+
35
+ 1) No virtual DOM. The Lightview dependency tracker laser targets just those nodes that need updates.
36
+
37
+ 1) SPA, and MPA friendly ... somewhat SEO friendly and short steps away from fully SEO friendly.
38
+
39
+ 1) A [component library](https://lightview.dev/components) including charts and gauges.
40
+
41
+ 1) Lots of live [editable examples](https://lightview.dev/#examples).
42
+
@@ -1,5 +1,5 @@
1
1
  <head>
2
- <title>Counter</title>
2
+ <title>Lightview:Examples:Counter</title>
3
3
  <script src="../lightview.js?as=x-body"></script>
4
4
  </head>
5
5
 
@@ -9,15 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <script type="lightview/module">
12
- debugger;
13
- self.variables({
14
- count: "number"
15
- }, {
16
- reactive
17
- });
18
- count = 0;
12
+ self.variables({count: "number",}, {reactive,set:0});
19
13
  self.bump = () => count++;
20
- </script>
14
+ </script>
21
15
 
22
16
  <style>
23
17
  button {
@@ -0,0 +1,47 @@
1
+ import 'expect-puppeteer';
2
+
3
+ describe('Lightview:Example:Counter', () => {
4
+ beforeAll(async () => {
5
+ await page.goto('http://localhost:8080/examples/counter.html');
6
+ });
7
+
8
+ test('should be titled "Lightview:Examples:Counter"', async () => {
9
+ await expect(page.title()).resolves.toMatch("Lightview:Examples:Counter");
10
+ });
11
+
12
+ test('count should be a variable', async () => {
13
+ const result = await page.evaluate(async () => {
14
+ return document.body.getVariable("count");
15
+ });
16
+ expect(result).toBeDefined();
17
+ const {name,type,value,reactive} = result;
18
+ expect(name).toBe("count");
19
+ expect(type).toBe("number");
20
+ expect(value).toBe(0);
21
+ expect(reactive).toBe(true);
22
+ });
23
+
24
+ test('bump should be called', async () => {
25
+ const result = await page.evaluate(async () => {
26
+ document.body.bump();
27
+ return document.body.getVariableValue("count");
28
+ });
29
+ expect(result).toBe(1);
30
+ });
31
+
32
+ test('click should bump', async () => {
33
+ const buttonHandle = await page.evaluateHandle('document.body.querySelector("button")');
34
+ await buttonHandle.click();
35
+ const result = await page.evaluate(async () => {
36
+ return document.body.getVariableValue("count");
37
+ });
38
+ expect(result).toBe(2);
39
+ });
40
+
41
+ test("should be custom element", async() => {
42
+ const result = await page.evaluate(async () => {
43
+ return document.body.constructor.name;
44
+ });
45
+ expect(result).toBe("CustomElement");
46
+ })
47
+ })
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Invalid Template Literals</title>
5
+ <script src="../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <p>
9
+ <button l-on:click="bump">Click count:${count}</button>
10
+ </p>
11
+ <div style="margin:20px">
12
+ <p>
13
+ ${"<h1>"+(test++)+"</h1>"}
14
+ </p>
15
+ <p>
16
+ ${(while (test)<10 { test++}; test)}
17
+ </p>
18
+ <p>
19
+ ${(() =>test)()}
20
+ </p>
21
+ <p>
22
+ ${(() = >test)()}
23
+ </p>
24
+ <p>
25
+ ${function(){return \${test}})()}
26
+ </p>
27
+ <p>
28
+ ${window.alert("ok")}
29
+ </p>
30
+ </div>
31
+
32
+ <script type="lightview/module">
33
+ self.variables({count: "number",test:"number"}, {reactive,set:0});
34
+ self.bump = () => count++;
35
+ </script>
36
+
37
+ <style>
38
+ button {
39
+ margin: 20px;
40
+ background: gray
41
+ }
42
+ </style>
43
+ </body>
44
+
45
+ </html>
@@ -13,7 +13,9 @@
13
13
 
14
14
 
15
15
  <script type="lightview/module">
16
- self.variables({myRemote:"object"},{reactive,remote:"http://localhost:8000/remote.json"});
16
+ const {remote} = await import("../types.js");
17
+
18
+ self.variables({myRemote:"object"},{reactive,remote:remote("http://localhost:8000/remote.json")});
17
19
 
18
20
  await myRemote; // must await remotes before the first time they are used, e.g. before HTML is rendered
19
21
 
@@ -1 +1 @@
1
- {"name":"joe","age":20}
1
+ {"name":"joe","age":30}
@@ -0,0 +1,30 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Lightview:Sensor Demo</title>
4
+ <link href="../../components/gauge.html" rel="module">
5
+ <script src="../../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <div style="width:100%;text-align:center">
9
+ <div style="display:inline-block">
10
+ <l-gauge id="sensor1" type="Gauge" label="Sensor One" value="50"></l-gauge>
11
+ </div>
12
+
13
+ <div style="display:inline-block">
14
+ <l-gauge id="sensor2" type="Gauge" label="Sensor Two" value="50"></l-gauge>
15
+ </div>
16
+ </div>
17
+
18
+ <script type="lightview/module">
19
+ const {remote} = await import("../../types.js");
20
+ self.variables({sensor1:"number"},{remote:remote({ttl:5000,path:"https://lightview.dev/sensors/sensor1"})});
21
+ self.variables({sensor2:"number"},{remote:remote({ttl:7500,path:"https://lightview.dev/sensors/sensor2"})});
22
+ await sensor1;
23
+ await sensor2;
24
+ addEventListener("change",({variableName,value}) => {
25
+ const sensor = document.body.getElementById(variableName);
26
+ sensor.setValue(value);
27
+ });
28
+ </script>
29
+ </body>
30
+ </html>
@@ -0,0 +1,30 @@
1
+ const http = require("http"),
2
+ fs = require("fs"),
3
+ host = 'localhost',
4
+ port = 8000,
5
+ requestListener = async function (req, res) {
6
+ const path = `.${req.url}`;
7
+ res.setHeader("Access-Control-Allow-Origin","*");
8
+ res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS");
9
+ res.setHeader("Access-Control-Allow-Headers", "*");
10
+ res.setHeader("Content-Type", "application/json");
11
+ if(req.method==="OPTIONS") {
12
+ res.end();
13
+ return;
14
+ }
15
+ if(req.method==="GET") {
16
+ const value = `${40 + Math.round(60 * Math.random())}`;
17
+ console.log("GET",req.url,"<-",value);
18
+ res.setHeader("Content-Length", value.length);
19
+ res.write(value);
20
+ res.end();
21
+ return;
22
+ }
23
+ console.log(req.method);
24
+ },
25
+ server = http.createServer(requestListener);
26
+ server.listen(port, host, () => {
27
+ console.log(`Server is running on http://${host}:${port}`);
28
+ });
29
+
30
+
package/jest.config.json CHANGED
@@ -9,4 +9,4 @@
9
9
  "globalSetup": "jest-environment-puppeteer/setup",
10
10
  "globalTeardown": "jest-environment-puppeteer/teardown",
11
11
  "testEnvironment": "jest-environment-puppeteer"
12
- }
12
+ }
package/lightview.js CHANGED
@@ -241,18 +241,21 @@ const {observe} = (() => {
241
241
  const variable = target[property],
242
242
  {type, value, shared, exported, constant, reactive, remote} = variable;
243
243
  if (constant) throw new TypeError(`${property}:${type} is a constant`);
244
- newValue = type.validate ? type.validate(newValue,target[property]) : coerce(newValue,type);
244
+ if(newValue!=null || type.required) newValue = type.validate ? type.validate(newValue,target[property]) : coerce(newValue,type);
245
245
  const newtype = typeof (newValue),
246
246
  typetype = typeof (type);
247
- if (newValue == null || type === "any" || (newtype === type && typetype==="string") || (typetype === "function" && (newValue && newtype === "object" && newValue instanceof type) || variable.validityState?.valid)) {
247
+ if ((newValue == null && !type.required) ||
248
+ type === "any" ||
249
+ (newtype === type && typetype==="string") ||
250
+ (typetype === "function" && !type.validate && (newValue && newtype === "object" && newValue instanceof type) || variable.validityState?.valid)) {
248
251
  if (value !== newValue) {
249
252
  event.oldValue = value;
250
253
  target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
251
254
  target.postEvent.value("change", event);
252
255
  if (event.defaultPrevented) {
253
256
  target[property].value = value;
254
- } else if(remote) {
255
- handleRemote({variable,remote,reactive},true);
257
+ } else if(remote && remote.put) {
258
+ remote.handleRemote({variable,config:remote.config,reactive},true);
256
259
  }
257
260
  }
258
261
  return true;
@@ -260,7 +263,7 @@ const {observe} = (() => {
260
263
  if (typetype === "function" && newValue && newtype === "object") {
261
264
  throw new TypeError(`Can't assign instance of '${newValue.constructor.name}' to variable '${property}:${type.name.replace("bound ", "")}'`)
262
265
  }
263
- throw new TypeError(`Can't assign '${typeof (newValue)} ${newtype === "string" ? '"' + newValue + '"' : newValue}' to variable '${property}:${typetype === "function" ? type.name.replace("bound ", "") : type}'`)
266
+ throw new TypeError(`Can't assign '${typeof (newValue)} ${newtype === "string" ? '"' + newValue + '"' : newValue}' to variable '${property}:${typetype === "function" ? type.name.replace("bound ", "") : type} ${type.required ? "required" : ""}'`)
264
267
  },
265
268
  keys() {
266
269
  return [...Object.keys(vars)];
@@ -339,7 +342,7 @@ const {observe} = (() => {
339
342
  }
340
343
  return nodes;
341
344
  }
342
- const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
345
+
343
346
  const resolveNodeOrText = (node, component, safe) => {
344
347
  const type = typeof (node),
345
348
  template = type === "string" ? node.trim() : node.template;
@@ -357,15 +360,6 @@ const {observe} = (() => {
357
360
  }
358
361
  return node?.nodeValue;
359
362
  }
360
- const render = (hasTemplate, render) => {
361
- let observer;
362
- if (hasTemplate) {
363
- if (observer) observer.cancel();
364
- observer = observe(render)
365
- } else {
366
- render();
367
- }
368
- }
369
363
  const inputTypeToType = (inputType) => {
370
364
  if (!inputType) return "any"
371
365
  if (["text", "tel", "email", "url", "search", "radio", "color", "password"].includes(inputType)) return "string";
@@ -430,8 +424,7 @@ const {observe} = (() => {
430
424
  reactive: {value: true, constant: true},
431
425
  shared: {value: true, constant: true},
432
426
  exported: {value: true, constant: true},
433
- imported: {value: true, constant: true},
434
- remote: {}
427
+ imported: {value: true, constant: true}
435
428
  };
436
429
  const createClass = (domElementNode, {observer, framed}) => {
437
430
  const instances = new Set(),
@@ -475,7 +468,7 @@ const {observe} = (() => {
475
468
  constant: true
476
469
  },
477
470
  postEvent: {
478
- value: (eventName, event={}) => {
471
+ value: (eventName, event = {}) => {
479
472
  //event = {...event}
480
473
  event.type = eventName;
481
474
  event.target = currentComponent;
@@ -558,13 +551,12 @@ const {observe} = (() => {
558
551
  const nodes = getNodes(ctx);
559
552
  nodes.forEach((node) => {
560
553
  if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
561
- render(!!node.template, () => resolveNodeOrText(node, this))
554
+ observe(() => resolveNodeOrText(node, this));
562
555
  } else if (node.nodeType === Node.ELEMENT_NODE) {
563
556
  // resolve the value before all else;
564
557
  const attr = node.attributes.value,
565
558
  template = attr?.template;
566
559
  if (attr && template) {
567
- //render(!!template, () => {
568
560
  let value = resolveNodeOrText(attr, this),
569
561
  eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
570
562
  const template = attr.template;
@@ -612,22 +604,22 @@ const {observe} = (() => {
612
604
  const {name, value} = attr,
613
605
  vname = node.attributes.name?.value;
614
606
  if (name === "type" && value=="radio" && vname) {
615
- bindInput(node, vname, this);
616
- render(true, () => {
617
- const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
618
- if (node.attributes.value.value == varvalue) {
619
- node.setAttribute("checked", "");
620
- node.checked = true;
621
- } else {
622
- node.removeAttribute("checked");
623
- node.checked = false;
624
- }
625
- });
607
+ bindInput(node, vname, this);
608
+ observe(() => {
609
+ const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
610
+ if (node.attributes.value.value == varvalue) {
611
+ node.setAttribute("checked", "");
612
+ node.checked = true;
613
+ } else {
614
+ node.removeAttribute("checked");
615
+ node.checked = false;
616
+ }
617
+ });
626
618
  }
627
619
 
628
620
  const [type, ...params] = name.split(":");
629
621
  if (type === "") { // name is :something
630
- render(!!attr.template, () => {
622
+ observe(() => {
631
623
  const value = attr.value;
632
624
  if (params[0]) {
633
625
  if (value === "true") node.setAttribute(params[0], "")
@@ -643,20 +635,20 @@ const {observe} = (() => {
643
635
  })
644
636
  } else if (type === "l-on") {
645
637
  let listener;
646
- render(!!attr.template, () => {
638
+ observe(() => {
647
639
  const value = resolveNodeOrText(attr, this);
648
640
  if (listener) node.removeEventListener(params[0], listener);
649
641
  listener = this[value] || window[value] || Function(value);
650
642
  addListener(node, params[0], listener);
651
643
  })
652
644
  } else if (type === "l-if") {
653
- render(!!attr.template, () => {
645
+ observe(() => {
654
646
  const value = resolveNodeOrText(attr, this);
655
647
  node.style.setProperty("display", value === "true" ? "revert" : "none");
656
648
  })
657
649
  } else if (type === "l-for") {
658
650
  node.template ||= node.innerHTML;
659
- render(!!attr.template, () => {
651
+ observe(() => {
660
652
  const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
661
653
  value = resolveNodeOrText(attr, this),
662
654
  coerced = coerce(value, what === "each" ? Array : "object"),
@@ -683,8 +675,8 @@ const {observe} = (() => {
683
675
  else node.appendChild(parsed.body.firstChild);
684
676
  }
685
677
  })
686
- } else {
687
- render(!!attr.template, () => {
678
+ } else if(attr.template) {
679
+ observe(() => {
688
680
  resolveNodeOrText(attr, this);
689
681
  })
690
682
  }
@@ -699,11 +691,9 @@ const {observe} = (() => {
699
691
  }
700
692
  adoptedCallback(callback) {
701
693
  this.vars.postEvent.value("adopted");
702
- super.adoptedCallback();
703
694
  }
704
695
  disconnectedCallback() {
705
696
  this.vars.postEvent.value("disconnected");
706
- super.disconnectedCallback();
707
697
  }
708
698
  get observedAttributes() {
709
699
  return CustomElement.observedAttributes;
@@ -713,9 +703,12 @@ const {observe} = (() => {
713
703
  }
714
704
 
715
705
  getVariableNames() {
716
- return Object.keys(this.vars).filter((name) => {
717
- return !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name)
718
- })
706
+ return Object.keys(this.vars)
707
+ .filter(name => !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name))
708
+ }
709
+
710
+ getVariable(name) {
711
+ return this.vars[name] ? {...this.vars[name]} : undefined;
719
712
  }
720
713
 
721
714
  setVariableValue(variableName, value, {coerceTo = typeof (value)} = {}) {
@@ -751,17 +744,25 @@ const {observe} = (() => {
751
744
  return this.vars[variableName]?.value;
752
745
  }
753
746
 
754
- variables(variables, {observed, reactive, shared, exported, imported, remote} = {}) { // options = {observed,reactive,shared,exported,imported}
747
+ variables(variables, {observed, reactive, shared, exported, imported, remote, constant,set} = {}) { // options = {observed,reactive,shared,exported,imported}
755
748
  const addEventListener = this.varsProxy.addEventListener;
756
749
  if (variables !== undefined) {
757
750
  Object.entries(variables)
758
751
  .forEach(([key, type]) => {
759
752
  const variable = this.vars[key] ||= {name: key, type};
753
+ if(set!==undefined && constant!==undefined) throw new TypeError(`${key} has the constant value ${constant} and can't be set to ${set}`);
754
+ variable.value = set;
755
+ if(constant!==undefined) {
756
+ variable.constant = true;
757
+ variable.value = constant;
758
+ }
760
759
  if (observed || imported) {
761
760
  variable.value = this.hasAttribute(key) ? coerce(this.getAttribute(key), variable.type) : variable.value;
762
- variable.observed = observed;
763
761
  variable.imported = imported;
764
- if(variable.observed) this.observedAttributes.add(key);
762
+ if(variable.observed) {
763
+ variable.observed = observed;
764
+ this.observedAttributes.add(key);
765
+ }
765
766
  }
766
767
  if (reactive) {
767
768
  variable.reactive = true;
@@ -780,10 +781,11 @@ const {observe} = (() => {
780
781
  this.changeListener.targets.add(key);
781
782
  }
782
783
  if (remote) {
784
+ if(typeof(remote)==="function") remote = remote(`./${key}`);
783
785
  variable.remote = remote;
784
- handleRemote({variable, remote, reactive});
786
+ remote.handleRemote({variable, config:remote.config, reactive,component:this});
785
787
  }
786
- if(type.validate) type.validate(undefined,variable);
788
+ if(type.validate && variable.value!==undefined) type.validate(variable.value,variable);
787
789
  });
788
790
  }
789
791
  return Object.entries(this.vars)
@@ -792,146 +794,6 @@ const {observe} = (() => {
792
794
  return result;
793
795
  }, {});
794
796
  }
795
-
796
- constants(variables) {
797
- if (variables !== undefined) {
798
- Object.entries(variables)
799
- .forEach(([key, value]) => {
800
- const type = typeof (value) === "function" ? value : typeof (value),
801
- variable = this.vars[key];
802
- if (variable !== undefined) throw new TypeError(`${variable.constant ? "const" : "let"} ${key}:${variable.type} already declared.`);
803
- if (value === undefined) throw new TypeError(`const ${key}:undefined must be initialized.`);
804
- this.vars[key] = {type, value, constant: true};
805
- })
806
- }
807
- return Object.entries(this.vars)
808
- .reduce((result, [key, variable]) => {
809
- if (variable.constant) result[key] = {...variable};
810
- return result;
811
- }, {});
812
- }
813
- }
814
- }
815
-
816
- const remoteProxy = ({json, variable,remote, reactive}) => {
817
- const type = typeof (remote);
818
- return new Proxy(json, {
819
- get(target,property) {
820
- if(property==="__remoteProxytarget__") return json;
821
- return target[property];
822
- },
823
- async set(target, property, value) {
824
- if(value && typeof(value)==="object" && value instanceof Promise) value = await value;
825
- const oldValue = target[property];
826
- if (oldValue !== value) {
827
- let remotevalue;
828
- if (type === "string") {
829
- const href = new URL(remote,window.location.href).href;
830
- remotevalue = patch({target,property,value,oldValue},href,variable);
831
- } else if(remote && type==="object") {
832
- let href;
833
- if(remote.path) href = new URL(remote.path,window.location.href).href;
834
- if(!remote.patch) {
835
- if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
836
- remote.patch = patch;
837
- }
838
- remotevalue = remote.patch({target,property,value,oldValue},href,variable);
839
- }
840
- if(remotevalue) {
841
- await remotevalue.then((newjson) => {
842
- if (newjson && typeof (newjson) === "object" && reactive) {
843
- const target = variable.value?.__reactorProxyTarget__ ? json : variable.value;
844
- Object.entries(newjson).forEach(([key,newValue]) => {
845
- if(target[key]!==newValue) target[key] = newValue;
846
- })
847
- Object.keys(target).forEach((key) => {
848
- if(!(key in newjson)) delete target[key];
849
- });
850
- if(variable.value?.__reactorProxyTarget__) {
851
- const dependents = variable.value.__dependents__,
852
- observers = dependents[property] || [];
853
- [...observers].forEach((f) => {
854
- if (f.cancelled) dependents[property].delete(f);
855
- else f();
856
- })
857
- }
858
- } else {
859
- variable.value = json;
860
- }
861
- })
862
- }
863
- }
864
- return true;
865
- }
866
- })
867
- }
868
-
869
- const patch = ({target,property,value,oldValue},href,variable) => {
870
- return fetch(href, {
871
- method: "PATCH",
872
- body: JSON.stringify({property,value,oldValue}),
873
- headers: {
874
- "Content-Type": "application/json"
875
- }
876
- }).then((response) => {
877
- if (response.status < 400) return response.json();
878
- })
879
- }
880
-
881
- const get = (href,variable) => {
882
- return fetch(href)
883
- .then((response) => {
884
- if (response.status < 400) return response.json();
885
- })
886
- }
887
-
888
- const put = (href,variable) => {
889
- return fetch(href, {
890
- method: "PUT",
891
- body: JSON.stringify(variable.value),
892
- headers: {
893
- "Content-Type": "application/json"
894
- }
895
- }).then((response) => {
896
- if (response.status === 200) return response.json();
897
- })
898
- }
899
-
900
- const handleRemote = async ({variable, remote, reactive},doput) => {
901
- const type = typeof (remote);
902
- let value;
903
- if (type === "string") {
904
- const href = new URL(remote,window.location.href).href;
905
- value = (doput
906
- ? put(href,variable)
907
- : get(href,variable));
908
- if(variable.value===undefined) variable.value = value;
909
- } else if (remote && type === "object") {
910
- let href;
911
- if(remote.path) href = new URL(remote.path,window.location.href).href;
912
- if(!remote.get || !remote.put) {
913
- if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
914
- if(!remote.get) remote.get = get;
915
- if(!remote.put) remote.put = put;
916
- }
917
- value = (doput
918
- ? remote.put(href,variable)
919
- : remote.get(href,variable));
920
- if(remote.ttl && !doput && !remote.intervalId) {
921
- remote.intervalId = setInterval(async () => {
922
- await handleRemote({variable, remote, reactive});
923
- })
924
- }
925
- if(variable.value===undefined) variable.value = value;
926
- }
927
- if(value) {
928
- variable.value = await value.then((json) => {
929
- if (json && typeof (json) === "object" && reactive) {
930
- return remoteProxy({json, variable,remote, reactive})
931
- } else {
932
- return json;
933
- }
934
- })
935
797
  }
936
798
  }
937
799
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "1.5.1b",
3
+ "version": "1.6.4b",
4
4
  "description": "Small, simple, powerful web UI and micro front end creation ... Great ideas from Svelte, React, Vue and Riot combined.",
5
5
  "main": "lightview.js",
6
6
  "scripts": {
package/test/basic.html CHANGED
@@ -59,7 +59,7 @@
59
59
 
60
60
  inumber = 1;
61
61
  irange = 1;
62
- debugger;
62
+
63
63
  idatetime = new Date();
64
64
 
65
65
  icheckbox = true;
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Extended</title>
6
+ <script src="../lightview.js?as=x-body"></script>
7
+ </head>
8
+ <body>
9
+ <script type="lightview/module">
10
+ const {array,boolean,number,object,string} = await import("../types.js");
11
+
12
+ self.variables({strictarray:array},{set:[]});
13
+ self.variables({strictboolean:boolean},{set:true});
14
+ self.variables({strictnumber:number},{set:0});
15
+ self.variables({strictobject:object},{set:{}});
16
+ self.variables({strictstring:string},{set:"test"});
17
+
18
+
19
+ self.variables({requiredarray:array({required:true})});
20
+ self.variables({requiredboolean:boolean({required:true})});
21
+ self.variables({requirednumber:number({required:true})});
22
+ self.variables({requiredobject:object({required:true})});
23
+ self.variables({requiredstring:string({required:true})});
24
+
25
+ //self.setVariableValue("requirednumber",null);
26
+
27
+ </script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,270 @@
1
+ import 'expect-puppeteer';
2
+ function reviver(property,value) {
3
+ if(value==="@-Infinity") return -Infinity;
4
+ if(value==="@Infinity") return Infinity;
5
+ if(value==="@NaN") return NaN;
6
+ return value;
7
+ }
8
+
9
+ describe('Lightview - Variables', () => {
10
+ beforeAll(async () => {
11
+ await page.goto('http://localhost:8080/test/extended.html');
12
+ });
13
+
14
+ test('should be titled "Extended"', async () => {
15
+ await expect(page.title()).resolves.toMatch('Extended');
16
+ });
17
+
18
+ test('strictarray - should be array', async () => {
19
+ const result = await page.evaluate(() => {
20
+ return document.body.getVariableValue("strictarray")
21
+ });
22
+ expect(Array.isArray(result)).toBe(true);
23
+ });
24
+
25
+ test('strictarray - should set', async () => {
26
+ const result = await page.evaluate(() => {
27
+ try {
28
+ document.body.setVariableValue("strictarray",[]);
29
+ } catch(e) {
30
+ return e;
31
+ }
32
+ return document.body.getVariableValue("strictarray")
33
+ });
34
+ expect(Array.isArray(result)).toBe(true);
35
+ });
36
+
37
+ test('strictarray - should allow null', async () => {
38
+ const result = await page.evaluate(() => {
39
+ try {
40
+ document.body.setVariableValue("strictarray",undefined);
41
+ document.body.setVariableValue("strictarray",null);
42
+ } catch(e) {
43
+ return e;
44
+ }
45
+ return document.body.getVariableValue("strictarray")
46
+ });
47
+ expect(result).toBe(null);
48
+ });
49
+
50
+ test('strictarray - should throw', async () => {
51
+ const result = await page.evaluate(() => {
52
+ try {
53
+ document.body.setVariableValue("strictarray","false");
54
+ } catch(e) {
55
+ return e.message;
56
+ }
57
+ return document.body.getVariableValue("strictarray");
58
+ });
59
+ const {name,validityState} = JSON.parse(result,reviver);
60
+ expect(name).toBe("strictarray");
61
+ expect(validityState.typeMismatch).toBe(true);
62
+ expect(validityState.value).toBe("false");
63
+ });
64
+
65
+ test('strictboolean - should be boolean', async () => {
66
+ const result = await page.evaluate(() => {
67
+ return document.body.getVariableValue("strictboolean")
68
+ });
69
+ expect(result).toBe(true);
70
+ });
71
+
72
+ test('strictboolean - should set', async () => {
73
+ const result = await page.evaluate(() => {
74
+ try {
75
+ document.body.setVariableValue("strictboolean",false);
76
+ } catch(e) {
77
+ return e.message;
78
+ }
79
+ return document.body.getVariableValue("strictboolean")
80
+ });
81
+ expect(result).toBe(false);
82
+ });
83
+
84
+ test('strictboolean - should allow null', async () => {
85
+ const result = await page.evaluate(() => {
86
+ try {
87
+ document.body.setVariableValue("strictboolean",undefined);
88
+ document.body.setVariableValue("strictboolean",null);
89
+ } catch(e) {
90
+ return e.message;
91
+ }
92
+ return document.body.getVariableValue("strictboolean")
93
+ });
94
+ expect(result).toBe(null);
95
+ });
96
+
97
+ test('strictboolean - should throw', async () => {
98
+ const result = await page.evaluate(() => {
99
+ try {
100
+ document.body.setVariableValue("strictboolean","true");
101
+ } catch(e) {
102
+ return e.message;
103
+ }
104
+ return document.body.getVariableValue("strictboolean");
105
+ });
106
+ const {name,validityState} = JSON.parse(result,reviver);
107
+ expect(name).toBe("strictboolean");
108
+ expect(validityState.typeMismatch).toBe(true);
109
+ expect(validityState.value).toBe("true");
110
+ });
111
+
112
+ test('strictnumber - should be number', async () => {
113
+ const result = await page.evaluate(() => {
114
+ return document.body.getVariableValue("strictnumber")
115
+ });
116
+ expect(result).toBe(0);
117
+ });
118
+
119
+ test('strictnumber - should set', async () => {
120
+ const result = await page.evaluate(() => {
121
+ try {
122
+ document.body.setVariableValue("strictnumber",1);
123
+ } catch(e) {
124
+ return e.message;
125
+ }
126
+ return document.body.getVariableValue("strictnumber")
127
+ });
128
+ expect(result).toBe(1);
129
+ });
130
+
131
+ test('strictnumber - should allow null', async () => {
132
+ const result = await page.evaluate(() => {
133
+ try {
134
+ document.body.setVariableValue("strictnumber",undefined);
135
+ document.body.setVariableValue("strictnumber",null);
136
+ } catch(e) {
137
+ return e.message;
138
+ }
139
+ return document.body.getVariableValue("strictnumber")
140
+ });
141
+ expect(result).toBe(null);
142
+ });
143
+
144
+ test('strictnumber - should throw', async () => {
145
+ const result = await page.evaluate(() => {
146
+ try {
147
+ document.body.setVariableValue("strictnumber","0");
148
+ } catch(e) {
149
+ return e.message;
150
+ }
151
+ return document.body.getVariableValue("strictnumber");
152
+ });
153
+ const {name,validityState} = JSON.parse(result,reviver);
154
+ expect(name).toBe("strictnumber");
155
+ expect(validityState.typeMismatch).toBe(true);
156
+ expect(validityState.value).toBe("0");
157
+ });
158
+
159
+ test('strictobject - should be object', async () => {
160
+ const result = await page.evaluate(() => {
161
+ return document.body.getVariableValue("strictobject")
162
+ });
163
+ expect(typeof(result)).toBe("object");
164
+ expect(Object.keys(result).length).toBe(0);
165
+ });
166
+
167
+ test('strictobject - should set', async () => {
168
+ const result = await page.evaluate(() => {
169
+ try {
170
+ document.body.setVariableValue("strictobject", {test:"test"});
171
+ } catch(e) {
172
+ return e.message;
173
+ }
174
+ return document.body.getVariableValue("strictobject")
175
+ });
176
+ expect(typeof(result)).toBe("object");
177
+ expect(Object.keys(result).length).toBe(1);
178
+ expect(result.test).toBe("test");
179
+ });
180
+
181
+ test('strictobject - should allow null', async () => {
182
+ const result = await page.evaluate(() => {
183
+ try {
184
+ document.body.setVariableValue("strictobject",undefined);
185
+ document.body.setVariableValue("strictobject",null);
186
+ } catch(e) {
187
+ return e.message;
188
+ }
189
+ return document.body.getVariableValue("strictobject")
190
+ });
191
+ expect(result).toBe(null);
192
+ });
193
+
194
+ test('strictobject - should throw', async () => {
195
+ const result = await page.evaluate(() => {
196
+ try {
197
+ document.body.setVariableValue("strictobject","false");
198
+ } catch(e) {
199
+ return e.message;
200
+ }
201
+ return document.body.getVariableValue("strictobject");
202
+ });
203
+ const {name,validityState} = JSON.parse(result,reviver);
204
+ expect(name).toBe("strictobject");
205
+ expect(validityState.typeMismatch).toBe(true);
206
+ expect(validityState.value).toBe("false");
207
+ });
208
+
209
+ test('strictstring - should be string', async () => {
210
+ const result = await page.evaluate(() => {
211
+ return document.body.getVariableValue("strictstring")
212
+ });
213
+ expect(result).toBe("test");
214
+ });
215
+
216
+ test('strictstring - should set', async () => {
217
+ const result = await page.evaluate(() => {
218
+ try {
219
+ document.body.setVariableValue("strictstring","anothertest");
220
+ } catch(e) {
221
+ return e.message;
222
+ }
223
+ return document.body.getVariableValue("strictstring")
224
+ });
225
+ expect(result).toBe("anothertest");
226
+ });
227
+
228
+ test('strictstring - should allow null', async () => {
229
+ const result = await page.evaluate(() => {
230
+ try {
231
+ document.body.setVariableValue("strictstring",undefined);
232
+ document.body.setVariableValue("strictstring",null);
233
+ } catch(e) {
234
+ return e.message;
235
+ }
236
+ return document.body.getVariableValue("strictstring")
237
+ });
238
+ expect(result).toBe(null);
239
+ });
240
+
241
+ test('strictstring - should throw', async () => {
242
+ const result = await page.evaluate(() => {
243
+ try {
244
+ document.body.setVariableValue("strictstring",0);
245
+ } catch(e) {
246
+ return e.message;
247
+ }
248
+ return document.body.getVariableValue("strictstring");
249
+ });
250
+ const {name,validityState} = JSON.parse(result,reviver);
251
+ expect(name).toBe("strictstring");
252
+ expect(validityState.typeMismatch).toBe(true);
253
+ expect(validityState.value).toBe(0);
254
+ });
255
+
256
+ test('requirednumber - should throw and not allow null', async () => {
257
+ const result = await page.evaluate(() => {
258
+ try {
259
+ document.body.setVariableValue("requirednumber",null);
260
+ } catch(e) {
261
+ return e.message;
262
+ }
263
+ return document.body.getVariableValue("requirednumber")
264
+ });
265
+ const {name,validityState} = JSON.parse(result,reviver);
266
+ expect(name).toBe("requirednumber");
267
+ expect(validityState.valueMissing).toBe(true);
268
+ //expect(validityState.value).toBe(null);
269
+ });
270
+ })
package/types.js CHANGED
@@ -1,3 +1,23 @@
1
+ const toJSON = (value) => {
2
+ if([-Infinity,Infinity].includes(value)) return `@${value}`;
3
+ if(typeof(value)==="number" && isNaN(value)) return "@NaN";
4
+ if(value && typeof(value)==="object") {
5
+ return Object.entries(value)
6
+ .reduce((json,[key,value]) => {
7
+ if(value && typeof(value)==="object" && value.toJSON) value = value.toJSON();
8
+ json[key] = toJSON(value);
9
+ return json;
10
+ },Array.isArray(value) ? [] : {})
11
+ }
12
+ return value;
13
+ };
14
+ function reviver(property,value) {
15
+ if(value==="@-Infinity") return -Infinity;
16
+ if(value==="@Infinity") return Infinity;
17
+ if(value==="@NaN") return NaN;
18
+ return value;
19
+ }
20
+
1
21
  function ValidityState(options) {
2
22
  if(!this || !(this instanceof ValidityState)) return new ValidityState(options);
3
23
  Object.assign(this,{
@@ -15,9 +35,17 @@ function ValidityState(options) {
15
35
  },options);
16
36
  }
17
37
 
38
+ function DataType(options) {
39
+ if(!this || !(this instanceof DataType)) return new DataType(options);
40
+ Object.assign(this,options);
41
+ }
42
+ DataType.prototype.toJSON = function() {
43
+ return toJSON(this);
44
+ }
45
+
18
46
  const tryParse = (value) => {
19
47
  try {
20
- return JSON.parse(value+"")
48
+ return JSON.parse(value+"",reviver)
21
49
  } catch(e) {
22
50
 
23
51
  }
@@ -25,7 +53,7 @@ const tryParse = (value) => {
25
53
 
26
54
  const ifInvalid = (variable) => {
27
55
  variable.validityState.type = typeof(variable.type)==="string" ? variable.type : variable.type.type;
28
- throw new TypeError(JSON.stringify(variable));
56
+ throw new TypeError(JSON.stringify(DataType(variable)));
29
57
  // or could return existing value variable.value
30
58
  // or could return nothing
31
59
  }
@@ -40,7 +68,7 @@ const validateAny = function(value,variable) {
40
68
  variable.validityState = ValidityState({valid:true});
41
69
  return value;
42
70
  }
43
- return this.whenInvalid(variable);
71
+ return this.whenInvalid(variable,value);
44
72
  }
45
73
  const any = ({required=false,whenInvalid = ifInvalid,...rest}) => { // ...rest allows use of property "default", which is otherwise reserved
46
74
  if(typeof(required)!=="boolean") throw new TypeError(`required, ${JSON.stringify(required)}, must be a boolean`);
@@ -78,7 +106,7 @@ const validateArray = function(value,variable) {
78
106
  return result;
79
107
  }
80
108
  }
81
- return this.whenInvalid(variable);
109
+ return this.whenInvalid(variable,value);
82
110
  }
83
111
  const array = ({coerce=false, required = false,whenInvalid = ifInvalid,maxlength=Infinity,minlength=0,...rest}) => {
84
112
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -99,6 +127,7 @@ const array = ({coerce=false, required = false,whenInvalid = ifInvalid,maxlength
99
127
  }
100
128
  }
101
129
  array.validate = validateArray;
130
+ array.whenInvalid = ifInvalid;
102
131
  array.coerce = false;
103
132
  array.required = false;
104
133
 
@@ -110,7 +139,7 @@ const validateBoolean = function(value,variable) {
110
139
  if(this.required && value==null) {
111
140
  variable.validityState = ValidityState({valueMissing: true});
112
141
  } else {
113
- const result = !!(this.coerce ? tryParse(value) : value);
142
+ const result = this.coerce ? tryParse(value) : value;
114
143
  if(typeof(result)!=="boolean") {
115
144
  variable.validityState = ValidityState({typeMismatch: true, value});
116
145
  } else {
@@ -118,7 +147,7 @@ const validateBoolean = function(value,variable) {
118
147
  return result;
119
148
  }
120
149
  }
121
- return this.whenInvalid(variable);
150
+ return this.whenInvalid(variable,value);
122
151
  }
123
152
  const boolean = ({coerce=false,required=false, whenInvalid = ifInvalid,...rest}) =>{
124
153
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -135,6 +164,7 @@ const boolean = ({coerce=false,required=false, whenInvalid = ifInvalid,...rest})
135
164
  }
136
165
  }
137
166
  boolean.validate = validateBoolean;
167
+ boolean.whenInvalid = ifInvalid;
138
168
  boolean.coerce = false;
139
169
  boolean.required = false;
140
170
 
@@ -161,7 +191,7 @@ const validateNumber = function(value,variable) {
161
191
  return result;
162
192
  }
163
193
  }
164
- return this.whenInvalid(variable);
194
+ return this.whenInvalid(variable,value);
165
195
  }
166
196
  const number = ({coerce=false,required = false,whenInvalid = ifInvalid,min=-Infinity,max=Infinity,step = 1,allowNaN = true,...rest}) => {
167
197
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -186,11 +216,12 @@ const number = ({coerce=false,required = false,whenInvalid = ifInvalid,min=-Infi
186
216
  }
187
217
  }
188
218
  number.validate = validateNumber;
219
+ number.whenInvalid = ifInvalid;
189
220
  number.min = -Infinity;
190
221
  number.max = Infinity;
191
222
  number.coerce = false;
192
223
  number.required = false;
193
- number.allwNaN = true;
224
+ number.allowNaN = true;
194
225
  number.step = 1;
195
226
 
196
227
  const validateObject = function(value,variable) {
@@ -208,7 +239,7 @@ const validateObject = function(value,variable) {
208
239
  return result;
209
240
  }
210
241
  }
211
- return this.whenInvalid(variable);
242
+ return this.whenInvalid(variable,value);
212
243
  }
213
244
  const object = ({coerce=false, required = false,whenInvalid = ifInvalid,...rest}) => {
214
245
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -225,6 +256,7 @@ const object = ({coerce=false, required = false,whenInvalid = ifInvalid,...rest}
225
256
  }
226
257
  }
227
258
  object.validate = validateObject;
259
+ object.whenInvalid = ifInvalid;
228
260
  object.coerce = false;
229
261
  object.required = false;
230
262
 
@@ -247,7 +279,7 @@ const validateString = function(value,variable) {
247
279
  return result;
248
280
  }
249
281
  }
250
- return this.whenInvalid(variable);
282
+ return this.whenInvalid(variable,value);
251
283
  }
252
284
  const string = ({coerce=false, required = false,whenInvalid = ifInvalid, maxlength = Infinity, minlength = 0, pattern, ...rest}) => {
253
285
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -269,6 +301,7 @@ const string = ({coerce=false, required = false,whenInvalid = ifInvalid, maxleng
269
301
  }
270
302
  }
271
303
  string.validate = validateString;
304
+ string.whenInvalid = ifInvalid;
272
305
  string.coerce = false;
273
306
  string.required = false;
274
307
  string.maxlength = Infinity;
@@ -289,7 +322,7 @@ const validateSymbol = function(value,variable) {
289
322
  return result;
290
323
  }
291
324
  }
292
- return this.whenInvalid(variable);
325
+ return this.whenInvalid(variable,value);
293
326
  }
294
327
  const symbol = ({coerce=false,required=false, whenInvalid = ifInvalid,...rest}) =>{
295
328
  if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
@@ -306,7 +339,141 @@ const symbol = ({coerce=false,required=false, whenInvalid = ifInvalid,...rest})
306
339
  }
307
340
  }
308
341
  symbol.validate = validateSymbol;
342
+ symbol.whenInvalid = ifInvalid;
309
343
  symbol.coerce = false;
310
344
  symbol.required = false;
311
345
 
312
- export {ValidityState,any,array,boolean,number,string,symbol}
346
+ const remoteProxy = ({json, variable,config, reactive, component}) => {
347
+ const type = typeof (config);
348
+ return new Proxy(json, {
349
+ get(target,property) {
350
+ if(property==="__remoteProxytarget__") return json;
351
+ return target[property];
352
+ },
353
+ async set(target, property, value) {
354
+ if(value && typeof(value)==="object" && value instanceof Promise) value = await value;
355
+ const oldValue = target[property];
356
+ if (oldValue !== value) {
357
+ let remotevalue;
358
+ if (type === "string") {
359
+ const href = new URL(config,window.location.href).href;
360
+ remotevalue = patch({target,property,value,oldValue},href,variable);
361
+ } else if(config && type==="object") {
362
+ let href;
363
+ if(config.path) href = new URL(config.path,window.location.href).href;
364
+ if(!config.patch) {
365
+ if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
366
+ config.patch = patch;
367
+ }
368
+ remotevalue = config.patch({target,property,value,oldValue},href,variable);
369
+ }
370
+ if(remotevalue) {
371
+ await remotevalue.then((newjson) => {
372
+ if (newjson && typeof (newjson) === "object" && reactive) {
373
+ const target = variable.value?.__reactorProxyTarget__ ? json : variable.value;
374
+ Object.entries(newjson).forEach(([key,newValue]) => {
375
+ if(target[key]!==newValue) target[key] = newValue;
376
+ })
377
+ Object.keys(target).forEach((key) => {
378
+ if(!(key in newjson)) delete target[key];
379
+ });
380
+ if(variable.value?.__reactorProxyTarget__) {
381
+ const dependents = variable.value.__dependents__,
382
+ observers = dependents[property] || [];
383
+ [...observers].forEach((f) => {
384
+ if (f.cancelled) dependents[property].delete(f);
385
+ else f();
386
+ })
387
+ }
388
+ } else {
389
+ component.setVariableValue(variable.name,newjson)
390
+ //variable.value = json;
391
+ }
392
+ })
393
+ }
394
+ }
395
+ return true;
396
+ }
397
+ })
398
+ }
399
+
400
+ const patch = ({target,property,value,oldValue},href,variable) => {
401
+ return fetch(href, {
402
+ method: "PATCH",
403
+ body: JSON.stringify({property,value,oldValue}),
404
+ headers: {
405
+ "Content-Type": "application/json"
406
+ }
407
+ }).then((response) => {
408
+ if (response.status < 400) return response.json();
409
+ })
410
+ }
411
+
412
+ const get = (href,variable) => {
413
+ return fetch(href)
414
+ .then((response) => {
415
+ if (response.status < 400) return response.json();
416
+ })
417
+ }
418
+
419
+ const put = (href,variable) => {
420
+ return fetch(href, {
421
+ method: "PUT",
422
+ body: JSON.stringify(variable.value),
423
+ headers: {
424
+ "Content-Type": "application/json"
425
+ }
426
+ }).then((response) => {
427
+ if (response.status === 200) return response.json();
428
+ })
429
+ }
430
+
431
+ const handleRemote = async ({variable, config, reactive, component},doput) => {
432
+ const type = typeof (config);
433
+ let value;
434
+ if (type === "string") {
435
+ const href = new URL(config,window.location.href).href;
436
+ value = (doput
437
+ ? put(href,variable)
438
+ : get(href,variable));
439
+ if(variable.value===undefined) variable.value = value; // do not await here
440
+ } else if (remote && type === "object") {
441
+ let href;
442
+ if(!config.path) config.path = `./${variable.name}`;
443
+ if(config.path) href = new URL(config.path,window.location.href).href;
444
+ if(!config.get || !config.put) {
445
+ if(!href) throw new Error(`A remote path is required if no put function is provided for remote data`)
446
+ if(!config.get) config.get = get;
447
+ if(!config.put && reactive) config.put = put;
448
+ }
449
+ value = (doput
450
+ ? config.put(href,variable)
451
+ : config.get(href,variable));
452
+ if(config.ttl && !doput && !config.intervalId) {
453
+ config.intervalId = setInterval(async () => {
454
+ await handleRemote({variable, config, reactive, component});
455
+ //schedule();
456
+ },config.ttl);
457
+ }
458
+ if(variable.value===undefined) variable.value = value;
459
+ }
460
+ if(value) {
461
+ const json = await value;
462
+ //value.then((json) => {
463
+ if (json && typeof (json) === "object" && reactive) {
464
+ variable.value = remoteProxy({json, variable,config, reactive, component});
465
+ } else {
466
+ component.setVariableValue(variable.name,json);
467
+ }
468
+ //})
469
+ }
470
+ }
471
+
472
+ const remote = (config) => {
473
+ return {
474
+ config,
475
+ handleRemote
476
+ }
477
+ }
478
+
479
+ export {ValidityState,any,array,boolean,number,object,string,symbol,remote,reviver}