@adecore/intelligent-ui 0.33.3 → 0.34.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/budget.js CHANGED
@@ -1,12 +1,14 @@
1
+ const BLOCK_CHARACTERS = 65536;
1
2
  export const UI_LIMITS = {
2
- characters: 65536,
3
+ characters: BLOCK_CHARACTERS,
3
4
  depth: 32,
4
5
  nodes: 512,
5
6
  diagnostics: 20,
6
- steps: 20000,
7
+ // Parsing and validating a string literal can account for each character four times.
8
+ steps: 4 * BLOCK_CHARACTERS,
7
9
  iterations: 2000,
8
10
  stringLength: 16384,
9
- milliseconds: 30
11
+ milliseconds: 0
10
12
  };
11
13
  export class UiFailure extends Error {
12
14
  code;
@@ -26,16 +28,16 @@ export class UiBudget {
26
28
  constructor(limits = {}, now = Date.now) {
27
29
  this.now = now;
28
30
  this.limits = { ...UI_LIMITS, ...limits };
29
- for (const value of Object.values(this.limits)) {
30
- if (!Number.isSafeInteger(value) || value <= 0) {
31
- throw new UiFailure('invalid_limits', 'Budgets must be positive safe integers.');
31
+ for (const [name, value] of Object.entries(this.limits)) {
32
+ if (!Number.isSafeInteger(value) || value < 0 || (value === 0 && name !== 'milliseconds')) {
33
+ throw new UiFailure('invalid_limits', 'Budgets must be positive safe integers, except milliseconds may be zero.');
32
34
  }
33
35
  }
34
- this.started = now();
36
+ this.started = this.limits.milliseconds > 0 ? now() : 0;
35
37
  }
36
38
  step(count = 1) {
37
39
  this.steps += count;
38
- if (this.steps > this.limits.steps || this.now() - this.started > this.limits.milliseconds) {
40
+ if (this.steps > this.limits.steps || (this.limits.milliseconds > 0 && this.now() - this.started > this.limits.milliseconds)) {
39
41
  throw new UiFailure('budget_exceeded', 'The operation exceeded its work budget.');
40
42
  }
41
43
  }
@@ -7,7 +7,7 @@ export declare const UI_REPLY_LIMITS: {
7
7
  readonly blocks: 16;
8
8
  readonly characters: 262144;
9
9
  readonly nodes: 2048;
10
- readonly milliseconds: 60;
10
+ readonly milliseconds: 0;
11
11
  };
12
12
  export declare const UI_HOST_LIMITS: {
13
13
  readonly queries: 8;
package/dist/compiler.js CHANGED
@@ -9,7 +9,7 @@ import { parseUiSyntax, uiDiagnostic } from './syntax.js';
9
9
  import {} from 'zod';
10
10
  // The info string a fence carries when the host names none of its own.
11
11
  export const UI_FENCE_LANGUAGE = 'ui';
12
- export const UI_REPLY_LIMITS = { blocks: 16, characters: 262144, nodes: 2048, milliseconds: 60 };
12
+ export const UI_REPLY_LIMITS = { blocks: 16, characters: 262144, nodes: 2048, milliseconds: 0 };
13
13
  // What a host reads for one block: its queries, its link targets, the places it looks up and how often a visible block reads again.
14
14
  export const UI_HOST_LIMITS = { queries: 8, links: 64, places: 50, refreshMilliseconds: 10_000 };
15
15
  /* A Place that names an address, whose tag may hold quoted text with a `>` before it. */
@@ -361,7 +361,7 @@ export class UiCompiler {
361
361
  const now = this.options.now ?? Date.now;
362
362
  const language = this.options.fenceLanguage ?? UI_FENCE_LANGUAGE;
363
363
  const diagnostics = this.options.limits?.diagnostics ?? UI_LIMITS.diagnostics;
364
- const started = now();
364
+ const started = UI_REPLY_LIMITS.milliseconds > 0 ? now() : 0;
365
365
  const nodeCount = (items) => items.reduce((count, node) => count + 1 + nodeCount(node.children), 0);
366
366
  const rejected = (start, codeStart, codeEnd, end, complete) => ({
367
367
  id: `${this.options.id}:ui:${start}`,
@@ -393,7 +393,7 @@ export class UiCompiler {
393
393
  }
394
394
  const complete = closed || update.final === true;
395
395
  characters += codeEnd - codeStart;
396
- if (characters > UI_REPLY_LIMITS.characters || now() - started > UI_REPLY_LIMITS.milliseconds) {
396
+ if (characters > UI_REPLY_LIMITS.characters || (UI_REPLY_LIMITS.milliseconds > 0 && now() - started > UI_REPLY_LIMITS.milliseconds)) {
397
397
  blocks.push(rejected(start, codeStart, codeEnd, end, complete));
398
398
  exhausted = true;
399
399
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/intelligent-ui",
3
- "version": "0.33.3",
3
+ "version": "0.34.0",
4
4
  "description": "A streaming UI catalog, compiler and bounded expression interpreter without a framework or DOM.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "Bas Milius",
package/src/budget.ts CHANGED
@@ -6,18 +6,22 @@ export interface UiLimits {
6
6
  steps: number;
7
7
  iterations: number;
8
8
  stringLength: number;
9
+ /* Zero leaves admission to deterministic work limits; a positive value also bounds elapsed time. */
9
10
  milliseconds: number;
10
11
  }
11
12
 
13
+ const BLOCK_CHARACTERS = 65536;
14
+
12
15
  export const UI_LIMITS: Readonly<UiLimits> = {
13
- characters: 65536,
16
+ characters: BLOCK_CHARACTERS,
14
17
  depth: 32,
15
18
  nodes: 512,
16
19
  diagnostics: 20,
17
- steps: 20000,
20
+ // Parsing and validating a string literal can account for each character four times.
21
+ steps: 4 * BLOCK_CHARACTERS,
18
22
  iterations: 2000,
19
23
  stringLength: 16384,
20
- milliseconds: 30
24
+ milliseconds: 0
21
25
  };
22
26
 
23
27
  export class UiFailure extends Error {
@@ -41,17 +45,17 @@ export class UiBudget {
41
45
  constructor(limits: Partial<UiLimits> = {}, now: () => number = Date.now) {
42
46
  this.now = now;
43
47
  this.limits = { ...UI_LIMITS, ...limits };
44
- for (const value of Object.values(this.limits)) {
45
- if (!Number.isSafeInteger(value) || value <= 0) {
46
- throw new UiFailure('invalid_limits', 'Budgets must be positive safe integers.');
48
+ for (const [name, value] of Object.entries(this.limits)) {
49
+ if (!Number.isSafeInteger(value) || value < 0 || (value === 0 && name !== 'milliseconds')) {
50
+ throw new UiFailure('invalid_limits', 'Budgets must be positive safe integers, except milliseconds may be zero.');
47
51
  }
48
52
  }
49
- this.started = now();
53
+ this.started = this.limits.milliseconds > 0 ? now() : 0;
50
54
  }
51
55
 
52
56
  step(count = 1): void {
53
57
  this.steps += count;
54
- if (this.steps > this.limits.steps || this.now() - this.started > this.limits.milliseconds) {
58
+ if (this.steps > this.limits.steps || (this.limits.milliseconds > 0 && this.now() - this.started > this.limits.milliseconds)) {
55
59
  throw new UiFailure('budget_exceeded', 'The operation exceeded its work budget.');
56
60
  }
57
61
  }
package/src/compiler.ts CHANGED
@@ -11,7 +11,7 @@ import { type z } from 'zod';
11
11
  // The info string a fence carries when the host names none of its own.
12
12
  export const UI_FENCE_LANGUAGE = 'ui';
13
13
 
14
- export const UI_REPLY_LIMITS = { blocks: 16, characters: 262144, nodes: 2048, milliseconds: 60 } as const;
14
+ export const UI_REPLY_LIMITS = { blocks: 16, characters: 262144, nodes: 2048, milliseconds: 0 } as const;
15
15
 
16
16
  // What a host reads for one block: its queries, its link targets, the places it looks up and how often a visible block reads again.
17
17
  export const UI_HOST_LIMITS = { queries: 8, links: 64, places: 50, refreshMilliseconds: 10_000 } as const;
@@ -456,7 +456,7 @@ export class UiCompiler {
456
456
  const now = this.options.now ?? Date.now;
457
457
  const language = this.options.fenceLanguage ?? UI_FENCE_LANGUAGE;
458
458
  const diagnostics = this.options.limits?.diagnostics ?? UI_LIMITS.diagnostics;
459
- const started = now();
459
+ const started = UI_REPLY_LIMITS.milliseconds > 0 ? now() : 0;
460
460
  const nodeCount = (items: readonly UiNode[]): number => items.reduce((count, node) => count + 1 + nodeCount(node.children), 0);
461
461
  const rejected = (start: number, codeStart: number, codeEnd: number, end: number, complete: boolean): UiBlock => ({
462
462
  id: `${this.options.id}:ui:${start}`,
@@ -488,7 +488,7 @@ export class UiCompiler {
488
488
  }
489
489
  const complete = closed || update.final === true;
490
490
  characters += codeEnd - codeStart;
491
- if (characters > UI_REPLY_LIMITS.characters || now() - started > UI_REPLY_LIMITS.milliseconds) {
491
+ if (characters > UI_REPLY_LIMITS.characters || (UI_REPLY_LIMITS.milliseconds > 0 && now() - started > UI_REPLY_LIMITS.milliseconds)) {
492
492
  blocks.push(rejected(start, codeStart, codeEnd, end, complete));
493
493
  exhausted = true;
494
494
  return;