iobroker.webui 1.25.2 → 1.26.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.
Files changed (40) hide show
  1. package/README.md +6 -0
  2. package/io-package.json +14 -14
  3. package/package.json +14 -14
  4. package/www/dist/frontend/common/Common.globals.d.ts +1 -1
  5. package/www/dist/frontend/common/Runtime.d.ts +2 -2
  6. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +4 -4
  7. package/www/dist/frontend/generated/ScriptCommands.json +8 -3
  8. package/www/dist/frontend/helper/DialogHelper.js +7 -4
  9. package/www/dist/frontend/runtime/ScreenViewer.js +13 -16
  10. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +2 -1
  11. package/www/index.html +1 -1
  12. package/www/node_modules/@adobe/css-tools/dist/index.mjs +1 -1
  13. package/www/node_modules/@iobroker/socket-client/dist/esm/AdminConnection.js +7 -4
  14. package/www/node_modules/@iobroker/socket-client/dist/esm/Connection.d.ts +82 -2
  15. package/www/node_modules/@iobroker/socket-client/dist/esm/Connection.js +266 -2
  16. package/www/node_modules/@node-projects/base-custom-webcomponent/dist/BaseCustomWebComponent.d.ts +2 -1
  17. package/www/node_modules/@node-projects/base-custom-webcomponent/dist/BaseCustomWebComponent.js +63 -53
  18. package/www/node_modules/@node-projects/base-custom-webcomponent/dist/Effect.js +2 -0
  19. package/www/node_modules/@node-projects/base-custom-webcomponent/dist/SignalDecorator.js +16 -0
  20. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/BaseCustomWebComponent.js +795 -0
  21. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/Debounce.js +19 -0
  22. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/DeclaritiveBaseCustomWebcomponent.js +176 -0
  23. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/DomHelper.js +96 -0
  24. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/HotModuleReplacement.js +125 -0
  25. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/LazyLoader.js +52 -0
  26. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/TouchContextMenu.js +20 -0
  27. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/TypedEvent.js +48 -0
  28. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/WeakArray.js +27 -0
  29. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/dist/index.js +9 -0
  30. package/www/node_modules/@node-projects/propertygrid.webcomponent/node_modules/@node-projects/base-custom-webcomponent/jsr.json +5 -0
  31. package/www/node_modules/@node-projects/web-component-designer/dist/elements/helper/getBoxQuads.js +14 -15
  32. package/www/node_modules/@node-projects/web-component-designer/dist/elements/services/ServiceContainer.js +1 -0
  33. package/www/node_modules/@node-projects/web-component-designer/dist/elements/services/propertiesService/services/NativeElementsPropertiesService.js +37 -0
  34. package/www/node_modules/@node-projects/web-component-designer/dist/elements/widgets/designerView/designerCanvas.js +2 -0
  35. package/www/node_modules/@node-projects/web-component-designer/dist/elements/widgets/designerView/extensions/buttons/OptionsContextMenuButton.js +29 -4
  36. package/www/node_modules/es-module-shims/dist/es-module-shims.js +754 -628
  37. package/www/node_modules/long/index.js +360 -246
  38. package/www/node_modules/long/umd/index.js +1585 -1395
  39. package/www/node_modules/wunderbaum/dist/wunderbaum.css +1 -1
  40. package/www/node_modules/wunderbaum/dist/wunderbaum.esm.min.js +35 -35
@@ -28,6 +28,10 @@ const ADAPTERS = ['material', 'echarts', 'vis'];
28
28
  export class Connection {
29
29
  constructor(props) {
30
30
  this.props = this.applyDefaultProps(props);
31
+ // Create unique ID of this instance
32
+ this.connId = `${this.props.name ? `${this.props.name}-` : ''}${Math.round(Math.random() * 1000000)
33
+ .toString()
34
+ .padStart(6, '0')}`;
31
35
  this.waitForSocketLib()
32
36
  .then(() => this.startSocket())
33
37
  .catch(e => {
@@ -51,6 +55,8 @@ export class Connection {
51
55
  };
52
56
  }
53
57
  props;
58
+ connId;
59
+ lastAccessToken = null;
54
60
  ignoreState = '';
55
61
  connected = false;
56
62
  subscribed = false;
@@ -83,7 +89,8 @@ export class Connection {
83
89
  _instanceSubscriptions = {};
84
90
  /** Cache for server requests */
85
91
  _promises = {};
86
- _authTimer;
92
+ _authTimer = null;
93
+ _refreshTimer = null;
87
94
  _systemConfig;
88
95
  /** The "system.config" object */
89
96
  get systemConfig() {
@@ -184,6 +191,20 @@ export class Connection {
184
191
  token: this.props.token,
185
192
  });
186
193
  this._socket.on('connect', noTimeout => {
194
+ // Listen for messages from other tabs
195
+ window.addEventListener('storage', this.onAccessTokenUpdated);
196
+ const tokens = Connection.readTokens();
197
+ if (tokens && !tokens.owner) {
198
+ // Take the ownership of the token
199
+ const now = Date.now();
200
+ this.saveTokens({
201
+ access_token: tokens.access_token,
202
+ refresh_token: tokens.refresh_token,
203
+ expires_in: Math.round((tokens.expires_in.getTime() - now) / 1000),
204
+ refresh_token_expires_in: Math.round((tokens.refresh_token_expires_in.getTime() - now) / 1000),
205
+ token_type: 'Bearer',
206
+ }, tokens.stayLoggedIn);
207
+ }
187
208
  this.onReadyDone = false;
188
209
  // If the user is not admin, it takes some time to install the handlers, because all rights must be checked
189
210
  if (noTimeout !== true) {
@@ -313,9 +334,252 @@ export class Connection {
313
334
  }
314
335
  this._subscribe(true);
315
336
  this.onConnectionHandlers.forEach(cb => cb(true));
337
+ this.checkAccessTokenExpire();
316
338
  }
317
339
  this._waitForFirstConnectionPromise.resolve();
318
340
  }
341
+ static readTokens() {
342
+ let tokenString = window.sessionStorage.getItem('iob_tokens');
343
+ const stayLoggedIn = !tokenString;
344
+ if (!tokenString) {
345
+ tokenString = window.localStorage.getItem('iob_tokens');
346
+ }
347
+ if (!tokenString) {
348
+ return null;
349
+ }
350
+ const [refresh_token, refresh_token_expires_in, access_token, expires_in, owner] = tokenString.split(';');
351
+ const refreshExpires = new Date(refresh_token_expires_in);
352
+ if (refreshExpires.getTime() < Date.now()) {
353
+ // refresh token expired
354
+ return null;
355
+ }
356
+ return {
357
+ refresh_token,
358
+ refresh_token_expires_in: refreshExpires,
359
+ access_token,
360
+ expires_in: new Date(expires_in),
361
+ owner,
362
+ stayLoggedIn,
363
+ };
364
+ }
365
+ static saveTokensStatic(data, stayLoggedIn, owner) {
366
+ const tokenStr = `${data.refresh_token};${new Date(Date.now() + data.refresh_token_expires_in * 1000).toISOString()};${data.access_token};${new Date(Date.now() + data.expires_in * 1000).toISOString()}${owner ? `;${owner}` : ''}`;
367
+ if (stayLoggedIn) {
368
+ window.localStorage.setItem('iob_tokens', tokenStr);
369
+ }
370
+ else {
371
+ window.sessionStorage.setItem('iob_tokens', tokenStr);
372
+ }
373
+ }
374
+ saveTokens(data, stayLoggedIn) {
375
+ Connection.saveTokensStatic(data, stayLoggedIn, this.connId);
376
+ }
377
+ static deleteTokensStatic() {
378
+ window.localStorage.removeItem('iob_tokens');
379
+ window.sessionStorage.removeItem('iob_tokens');
380
+ }
381
+ /**
382
+ * Destroy tokens if they were created by this connection if they expired or invalid
383
+ *
384
+ * @param stayLoggedIn if stored in localStorage or in sessionStorage
385
+ * @param logout if logout is requested
386
+ */
387
+ deleteTokens(stayLoggedIn, logout) {
388
+ const tokens = Connection.readTokens();
389
+ if (tokens) {
390
+ if (logout) {
391
+ Connection.deleteTokensStatic();
392
+ }
393
+ else if (tokens.stayLoggedIn === stayLoggedIn && tokens.owner === this.connId) {
394
+ if (tokens.stayLoggedIn) {
395
+ window.localStorage.removeItem('iob_tokens');
396
+ }
397
+ else {
398
+ window.sessionStorage.removeItem('iob_tokens');
399
+ }
400
+ }
401
+ }
402
+ }
403
+ onAccessTokenUpdated = (event) => {
404
+ // Storage event is only fired in other tabs/windows (or iframes) of the same origin when the localStorage (or sessionStorage) is modified, and not in the same window where the change was made.
405
+ if (event.key === 'iob_tokens') {
406
+ const tokens = Connection.readTokens();
407
+ if (tokens) {
408
+ console.log(`Tab ${this.connId} received updated token: ${tokens.access_token}`);
409
+ this.updateTokenExpiration(tokens.access_token);
410
+ }
411
+ }
412
+ };
413
+ updateTokenExpiration(accessToken) {
414
+ // This connection is not a token owner, so only read the new access token and inform the server
415
+ if (this.lastAccessToken !== accessToken) {
416
+ this.lastAccessToken = accessToken;
417
+ this._socket.emit('updateTokenExpiration', accessToken, (err, success) => {
418
+ if (err) {
419
+ console.error(`Cannot update expiration time: ${err}`);
420
+ window.location.reload();
421
+ }
422
+ else if (!success) {
423
+ console.error('Cannot update expiration time');
424
+ window.location.reload();
425
+ }
426
+ });
427
+ }
428
+ this.checkAccessTokenExpire();
429
+ }
430
+ refreshTokens(tokenStructure, takeOwnership) {
431
+ if (!tokenStructure) {
432
+ // Refresh the page, as we cannot refresh the token
433
+ setTimeout(() => window.location.reload(), 500);
434
+ return;
435
+ }
436
+ if (takeOwnership || !tokenStructure.owner || tokenStructure.owner === this.connId) {
437
+ if (this.acquireTokenLock()) {
438
+ // Access token will expire soon => Send authentication again
439
+ fetch('./oauth/token', {
440
+ method: 'POST',
441
+ headers: {
442
+ 'Content-Type': 'application/x-www-form-urlencoded',
443
+ },
444
+ body: `grant_type=refresh_token&refresh_token=${tokenStructure.refresh_token}&client_id=ioBroker&stayloggedin=${tokenStructure.stayLoggedIn}`,
445
+ })
446
+ .then(response => {
447
+ if (response.ok) {
448
+ return response.json();
449
+ }
450
+ throw new Error('Cannot refresh access token');
451
+ })
452
+ .then((data) => {
453
+ if (data.access_token) {
454
+ this.saveTokens(data, tokenStructure.stayLoggedIn);
455
+ this.releaseTokenLock();
456
+ this.updateTokenExpiration(data.access_token);
457
+ }
458
+ else {
459
+ throw new Error('Cannot get access token');
460
+ }
461
+ })
462
+ .catch(err => {
463
+ this.releaseTokenLock();
464
+ this.deleteTokens(tokenStructure.stayLoggedIn);
465
+ console.error(err);
466
+ window.location.reload();
467
+ });
468
+ }
469
+ else {
470
+ // Someone else is updating the token, so wait for the next check
471
+ this.checkAccessTokenExpire();
472
+ }
473
+ }
474
+ else if (this.lastAccessToken !== tokenStructure.access_token) {
475
+ this.updateTokenExpiration(tokenStructure.access_token);
476
+ }
477
+ }
478
+ /**
479
+ * Attempts to acquire the semaphore lock.
480
+ *
481
+ * @returns True if the lock was acquired successfully; otherwise, false.
482
+ */
483
+ acquireTokenLock() {
484
+ const now = Date.now();
485
+ const lock = localStorage.getItem('iob_token_semaphore');
486
+ if (lock) {
487
+ try {
488
+ const lockData = JSON.parse(lock);
489
+ // If the current lock is still valid, we cannot acquire the lock.
490
+ if (now < lockData.expiry) {
491
+ return false;
492
+ }
493
+ // Otherwise, the lock has expired and we can override it.
494
+ }
495
+ catch {
496
+ // ignore
497
+ }
498
+ }
499
+ // Create a new lock with expiry 10 seconds from now.
500
+ const newLock = {
501
+ connId: this.connId,
502
+ expiry: now + 10 * 1000, // 10 seconds in milliseconds
503
+ };
504
+ localStorage.setItem('iob_token_semaphore', JSON.stringify(newLock));
505
+ return true;
506
+ }
507
+ /** Releases the semaphore lock if it's owned by the given connection ID. */
508
+ releaseTokenLock() {
509
+ const lock = localStorage.getItem('iob_token_semaphore');
510
+ if (lock) {
511
+ try {
512
+ const lockData = JSON.parse(lock);
513
+ // Only remove the lock if it's owned by the current connection.
514
+ if (lockData.connId === this.connId) {
515
+ localStorage.removeItem('iob_token_semaphore');
516
+ }
517
+ }
518
+ catch {
519
+ // If parsing fails, remove the lock just in case.
520
+ localStorage.removeItem('iob_token_semaphore');
521
+ }
522
+ }
523
+ }
524
+ checkAccessTokenExpire() {
525
+ if (this._refreshTimer) {
526
+ clearTimeout(this._refreshTimer);
527
+ this._refreshTimer = null;
528
+ }
529
+ if (this.isSecure) {
530
+ const tokens = Connection.readTokens();
531
+ if (tokens) {
532
+ const accessExpireInUnixMs = tokens.expires_in.getTime();
533
+ // Check if the access token expires in the next 30 seconds
534
+ if (accessExpireInUnixMs < Date.now() + 30_000) {
535
+ const takeOwnership = accessExpireInUnixMs < Date.now() + 5_500;
536
+ if (!tokens.refresh_token) {
537
+ // Refresh the page, as we cannot refresh the token
538
+ setTimeout(() => window.location.reload(), Date.now() > accessExpireInUnixMs ? 500 : accessExpireInUnixMs - Date.now());
539
+ }
540
+ else if (tokens.owner === this.connId ||
541
+ // We gave 25 seconds to the owner to update the token, and now we will do it and take the ownership
542
+ takeOwnership) {
543
+ // Handle token expiration if the connection is the owner of the token
544
+ if (this.props.tokenTimeoutHandler) {
545
+ // Asc if the user wants to stay logged in
546
+ void this.props.tokenTimeoutHandler(accessExpireInUnixMs).then(prolong => {
547
+ if (prolong) {
548
+ this.refreshTokens(tokens, takeOwnership);
549
+ }
550
+ else {
551
+ // Refresh the page, as we cannot refresh the token
552
+ setTimeout(() => window.location.reload(), Date.now() > accessExpireInUnixMs ? 500 : accessExpireInUnixMs - Date.now());
553
+ }
554
+ });
555
+ }
556
+ else {
557
+ this.refreshTokens(tokens, takeOwnership);
558
+ }
559
+ }
560
+ else if (this.lastAccessToken !== tokens.access_token) {
561
+ // The connection is not the owner, so just check if access_token changed, so inform the server about it
562
+ this.refreshTokens(tokens);
563
+ }
564
+ else {
565
+ // What 3 seconds and check again, maybe the owner connection will update the token
566
+ this._refreshTimer = setTimeout(() => {
567
+ this._refreshTimer = null;
568
+ this.checkAccessTokenExpire();
569
+ }, 3_000);
570
+ }
571
+ }
572
+ else {
573
+ this._refreshTimer = setTimeout(() => {
574
+ this._refreshTimer = null;
575
+ this.checkAccessTokenExpire();
576
+ }, accessExpireInUnixMs - Date.now() - 30_000 > 120_000
577
+ ? 120_000
578
+ : accessExpireInUnixMs - Date.now() - 30_000);
579
+ }
580
+ }
581
+ }
582
+ }
319
583
  /**
320
584
  * Checks if running in ioBroker cloud
321
585
  */
@@ -355,7 +619,7 @@ export class Connection {
355
619
  reject(err);
356
620
  }
357
621
  else {
358
- resolve(acl);
622
+ resolve(acl || null);
359
623
  }
360
624
  });
361
625
  },
@@ -49,12 +49,13 @@ export declare class BaseCustomWebComponentNoAttachedTemplate extends HTMLElemen
49
49
  * ==> this could also be nested
50
50
  *
51
51
  */
52
- protected _bindingsParse(node?: Node, removeAttributes?: boolean, host?: any, context?: any): void;
52
+ protected _bindingsParse(node?: Node, removeAttributes?: boolean, host?: any, context?: any, useSignals?: boolean): void;
53
53
  private _bindingsInternalParse;
54
54
  private _textFragmentBinding;
55
55
  private _bindingRunEval;
56
56
  private _bindingRunEvalInt;
57
57
  private _bindingConditional;
58
+ oldValuesSymbol: symbol;
58
59
  private _bindingRepeat;
59
60
  private _bindingSetNodeValue;
60
61
  private _bindingSetElementCssValue;
@@ -137,10 +137,10 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
137
137
  * ==> this could also be nested
138
138
  *
139
139
  */
140
- _bindingsParse(node, removeAttributes = false, host = null, context = null) {
141
- this._bindingsInternalParse(node, null, removeAttributes, host, context);
140
+ _bindingsParse(node, removeAttributes = false, host = null, context = null, useSignals = false) {
141
+ this._bindingsInternalParse(node, null, removeAttributes, host, context, useSignals);
142
142
  }
143
- _bindingsInternalParse(startNode, repeatBindingItems, removeAttributes, host, context) {
143
+ _bindingsInternalParse(startNode, repeatBindingItems, removeAttributes, host, context, useSignals = false) {
144
144
  if (!this._bindings)
145
145
  this._bindings = [];
146
146
  if (!startNode)
@@ -384,64 +384,74 @@ export class BaseCustomWebComponentNoAttachedTemplate extends HTMLElement {
384
384
  console.warn(error.message, 'Failed to bind Conditional to expression "' + expression + '"', this, node);
385
385
  }
386
386
  }
387
+ oldValuesSymbol = Symbol('oldValuesSymbol');
387
388
  _bindingRepeat(node, bindingProperty, bindingIndexName, expression, callback, repeatBindingItems, elementsCache, host, context) {
388
389
  try {
389
- const values = this._bindingRunEval(expression, repeatBindingItems, null, host, context);
390
- if (callback) {
391
- if (callback.startsWith('[[') && callback.endsWith(']]'))
392
- callback = callback.substring(2, callback.length - 2);
390
+ let values = this._bindingRunEval(expression, repeatBindingItems, null, host, context);
391
+ if (values && !Array.isArray(values))
392
+ values = [...values];
393
+ const oldValue = node[this.oldValuesSymbol];
394
+ if (oldValue !== values && (oldValue?.length !== values?.length || (values.some((x, i) => x !== oldValue?.[i])))) {
395
+ if (Array.isArray(values))
396
+ node[this.oldValuesSymbol] = [...values];
393
397
  else
394
- callback = "this." + callback;
395
- }
396
- for (let c of elementsCache) {
397
- if (c.parentNode) {
398
- let intRepeatBindingItems = [];
399
- intRepeatBindingItems.push({ name: 'nodes', item: [c] });
400
- intRepeatBindingItems.push({ name: 'callbackType', item: 'remove' });
401
- this._bindingRunEval(callback, intRepeatBindingItems, null, host, context);
402
- const bnds = this._repeatBindings.get(c);
403
- if (bnds?.length) {
404
- for (const b of bnds) {
405
- const idx = this._bindings.indexOf(b);
406
- if (idx >= 0)
407
- this._bindings.splice(idx, 1);
398
+ node[this.oldValuesSymbol] = values;
399
+ if (callback) {
400
+ if (callback.startsWith('[[') && callback.endsWith(']]'))
401
+ callback = callback.substring(2, callback.length - 2);
402
+ else
403
+ callback = "this." + callback;
404
+ }
405
+ for (let c of elementsCache) {
406
+ if (c.parentNode) {
407
+ let intRepeatBindingItems = [];
408
+ intRepeatBindingItems.push({ name: 'nodes', item: [c] });
409
+ intRepeatBindingItems.push({ name: 'callbackType', item: 'remove' });
410
+ this._bindingRunEval(callback, intRepeatBindingItems, null, host, context);
411
+ const bnds = this._repeatBindings.get(c);
412
+ if (bnds?.length) {
413
+ for (const b of bnds) {
414
+ const idx = this._bindings.indexOf(b);
415
+ if (idx >= 0)
416
+ this._bindings.splice(idx, 1);
417
+ }
408
418
  }
419
+ c.parentNode.removeChild(c);
409
420
  }
410
- c.parentNode.removeChild(c);
411
421
  }
412
- }
413
- elementsCache.length = 0;
414
- if (values) {
415
- if (!this._repeatBindings)
416
- this._repeatBindings = new WeakMap();
417
- //todo -> copy values to compare and only generate new controls...
418
- let i = 0;
419
- for (let e of values) {
420
- let intRepeatBindingItems = [];
421
- if (repeatBindingItems)
422
- intRepeatBindingItems = repeatBindingItems.slice();
423
- intRepeatBindingItems.push({ name: bindingProperty, item: e });
424
- intRepeatBindingItems.push({ name: bindingIndexName, item: i });
425
- let nd = node.content.cloneNode(true);
426
- elementsCache.push(...nd.children);
427
- const bndCount = this._bindings.length;
428
- this._bindingsInternalParse(nd, intRepeatBindingItems, true, host, context);
429
- this._repeatBindings.set(nd.children[0], this._bindings.toSpliced(0, bndCount));
430
- if (callback) {
431
- intRepeatBindingItems.push({ name: 'nodes', item: [...nd.children] });
432
- intRepeatBindingItems.push({ name: 'callbackType', item: 'create' });
433
- let nds = this._bindingRunEval(callback, intRepeatBindingItems, null, host, context);
434
- if (nds === undefined)
435
- nds = nd.children;
436
- if (nds) {
437
- for (let n of Array.from(nds))
438
- node.parentNode.insertBefore(n, node);
422
+ elementsCache.length = 0;
423
+ if (values) {
424
+ if (!this._repeatBindings)
425
+ this._repeatBindings = new WeakMap();
426
+ //todo -> copy values to compare and only generate new controls...
427
+ let i = 0;
428
+ for (let e of values) {
429
+ let intRepeatBindingItems = [];
430
+ if (repeatBindingItems)
431
+ intRepeatBindingItems = repeatBindingItems.slice();
432
+ intRepeatBindingItems.push({ name: bindingProperty, item: e });
433
+ intRepeatBindingItems.push({ name: bindingIndexName, item: i });
434
+ let nd = node.content.cloneNode(true);
435
+ elementsCache.push(...nd.children);
436
+ const bndCount = this._bindings.length;
437
+ this._bindingsInternalParse(nd, intRepeatBindingItems, true, host, context);
438
+ this._repeatBindings.set(nd.children[0], this._bindings.toSpliced(0, bndCount));
439
+ if (callback) {
440
+ intRepeatBindingItems.push({ name: 'nodes', item: [...nd.children] });
441
+ intRepeatBindingItems.push({ name: 'callbackType', item: 'create' });
442
+ let nds = this._bindingRunEval(callback, intRepeatBindingItems, null, host, context);
443
+ if (nds === undefined)
444
+ nds = nd.children;
445
+ if (nds) {
446
+ for (let n of Array.from(nds))
447
+ node.parentNode.insertBefore(n, node);
448
+ }
439
449
  }
450
+ else {
451
+ node.parentNode.insertBefore(nd, node);
452
+ }
453
+ i++;
440
454
  }
441
- else {
442
- node.parentNode.insertBefore(nd, node);
443
- }
444
- i++;
445
455
  }
446
456
  }
447
457
  }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //https://github.com/proposal-signals/signal-polyfill
@@ -0,0 +1,16 @@
1
+ //https://github.com/proposal-signals/signal-polyfill
2
+ import { Signal } from "signal-polyfill";
3
+ export function signal(target) {
4
+ const { get } = target;
5
+ return {
6
+ get() {
7
+ return get.call(this).get();
8
+ },
9
+ set(value) {
10
+ get.call(this).set(value);
11
+ },
12
+ init(value) {
13
+ return new Signal.State(value);
14
+ },
15
+ };
16
+ }