arrmatura 6.3.2 → 6.3.3

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.
@@ -1,7 +1,7 @@
1
1
  const CACHE = new Map();
2
2
 
3
3
  function upnum(r: number, n: number) {
4
- const rr = ((r << 5) - r) + n;
4
+ const rr = (r << 5) - r + n;
5
5
  return rr & rr;
6
6
  }
7
7
 
@@ -34,12 +34,11 @@ export class FingerprintMashine {
34
34
  }
35
35
 
36
36
  upString(x: string) {
37
- return this.upNumber(stringHash(x))
37
+ return this.upNumber(stringHash(x));
38
38
  }
39
39
 
40
40
  upObject(x) {
41
-
42
- if (x.$hash) return x.$hash;
41
+ if (x.uid) return x.uid;
43
42
 
44
43
  if (x instanceof Date) return this.upNumber(x.getTime());
45
44
 
@@ -55,18 +54,18 @@ export class FingerprintMashine {
55
54
  this.upString(k);
56
55
  this.upNumber(COLON_NUM);
57
56
  this.upAny(v);
58
- })
57
+ });
59
58
  } else if (x instanceof Set) {
60
59
  this.upString("$$$Set$$$");
61
- x.forEach((v) => this.upAny(v))
60
+ x.forEach((v) => this.upAny(v));
62
61
  } else if (Array.isArray(x)) {
63
62
  this.upString("$$$Array$$$");
64
- x.forEach((v) => this.upAny(v))
63
+ x.forEach((v) => this.upAny(v));
65
64
  } else {
66
65
  this.upString("$$$Object$$$");
67
66
  for (const k in x) {
68
- if (Object.prototype.hasOwnProperty.call(x, k) && k[0] !== '$') {
69
- this.upString(k)
67
+ if (Object.prototype.hasOwnProperty.call(x, k) && k[0] !== "$") {
68
+ this.upString(k);
70
69
  this.upNumber(COLON_NUM);
71
70
  this.upAny(x[k]);
72
71
  }
@@ -78,37 +77,37 @@ export class FingerprintMashine {
78
77
  return this.r;
79
78
  }
80
79
  upAny(x: any): void {
81
-
82
80
  if (x == null) {
83
81
  this.upString(String(x));
84
- } else if (typeof x === 'number') {
82
+ } else if (typeof x === "number") {
85
83
  if (isNaN(x)) {
86
84
  this.upString(`$$$NaN$$$`);
87
85
  } else {
88
86
  this.upNumber(x);
89
87
  }
90
- } else if (typeof x === 'object') {
88
+ } else if (typeof x === "object") {
91
89
  this.upObject(x);
92
- } else if (typeof x === 'string') {
90
+ } else if (typeof x === "string") {
93
91
  this.upString(x);
94
- } else if (typeof x === 'boolean') {
92
+ } else if (typeof x === "boolean") {
95
93
  this.upString(`$$$Boolean$$$${x}`);
96
- } else if (typeof x === 'function') {
94
+ } else if (typeof x === "function") {
97
95
  // this.upString(`$$$Function$$$${x}`);
98
96
  } else {
99
- this.upString(String(x))
97
+ this.upString(String(x));
100
98
  }
101
99
  }
102
100
 
103
101
  do(x: any) {
104
-
105
102
  if (x == null) return x;
106
103
 
107
104
  if (typeof x !== "object") return x;
108
105
 
109
106
  if (Object.isFrozen(x)) return x;
110
107
 
111
- if (x.$hash) return x.$hash;
108
+ if (x.uid) return x.uid;
109
+
110
+ if (x.$ctx) return x.$ctx.uid;
112
111
 
113
112
  this.r = 0;
114
113
  this.x = x;
@@ -120,9 +119,8 @@ export class FingerprintMashine {
120
119
 
121
120
  return this.r;
122
121
  }
123
-
124
122
  }
125
123
 
126
124
  const FM = new FingerprintMashine();
127
125
 
128
- export const objectFingerprint = (x: any) => FM.do(x)
126
+ export const objectFingerprint = (x: any) => FM.do(x);
@@ -4,86 +4,6 @@ import type { ExpressionText, Fn, StringHash } from "ultimus/types";
4
4
 
5
5
  type PipeFn = (c: IArrmatron, argValue: unknown) => unknown;
6
6
 
7
- // function tokenize(expression) {
8
- // return expression.split(/[\(\)\<\>=\?!:']+/g);
9
- // }
10
- // class Node {
11
- // constructor(public type, public value: string | null = null, public left = null, public right = null) {
12
- // }
13
- // }
14
-
15
- // function parse(tokens) {
16
- // let current = 0;
17
-
18
- // function walk(priority = 0) {
19
- // const token = tokens[current++];
20
- // const nextToken = tokens[current];
21
-
22
- // if (priority >= 0 && token === '(') {
23
- // const node = walk();
24
- // current++;
25
- // return node;
26
- // }
27
-
28
- // if (priority >= 0 && ['>', '<', '==', '!=', '&&', '||', '??'].includes(nextToken)) {
29
- // return new Node('binary', nextToken, walk(), walk());
30
- // }
31
-
32
- // if (priority >= 0 && ['>', '<', '==', '!=', '&&', '||', '??'].includes(nextToken)) {
33
- // return new Node('binary', nextToken, walk(), walk());
34
- // }
35
-
36
- // if (priority >= 0 && ['>', '<', '==', '!=', '&&', '||', '??'].includes(nextToken)) {
37
- // return new Node('binary', nextToken, walk(), walk());
38
- // }
39
-
40
- // if (priority >= 0 && nextToken === '?') {
41
- // const node = new Node('coalesce', walk());
42
- // if (tokens[current] !== ':') {
43
- // throw new TypeError('Expected colon after "?" in coalesce operation');
44
- // }
45
- // current++;
46
- // node.right = new Node('colon', token, walk(), walk());
47
- // return node;
48
- // }
49
- // return new Node('operand', (token));;
50
- // }
51
-
52
- // return walk();
53
- // }
54
-
55
- // function evaluate(node) {
56
- // switch (node.type) {
57
- // case 'number':
58
- // return node.value;
59
- // case 'operator':
60
- // switch (node.value) {
61
- // case '>':
62
- // return evaluate(node.left) > evaluate(node.right);
63
- // case '<':
64
- // return evaluate(node.left) < evaluate(node.right);
65
- // case '==':
66
- // return evaluate(node.left) == evaluate(node.right);
67
- // case '!=':
68
- // return evaluate(node.left) != evaluate(node.right);
69
- // default:
70
- // throw new TypeError(`Unknown operator: ${node.value}`);
71
- // }
72
- // case 'coalesce':
73
- // const condition = evaluate(node.left);
74
- // return condition ? evaluate(node.middle) : evaluate(node.right);
75
- // default:
76
- // throw new TypeError(`Unknown node type: ${node.type}`);
77
- // }
78
- // }
79
-
80
- // // Example usage
81
- // const expression = '1 < 2 ? 3 : 4';
82
- // const tokens = tokenize(expression);
83
- // const ast = parse(tokens);
84
- // console.log(evaluate(ast)); // Outputs: 3
85
-
86
-
87
7
  const OP_SHORTCUTS: StringHash = {
88
8
  "&&": "|and:",
89
9
  "==": "|equals:",
@@ -99,7 +19,8 @@ const OP_SHORTCUTS: StringHash = {
99
19
 
100
20
  const withNoPipes = (_: unknown, v: unknown) => v;
101
21
 
102
- const isConstKey = (key: string) => "'+-0123456789".includes(key[0]) || key === 'true' || key === 'false';
22
+ const isConstKey = (key: string) =>
23
+ "'+-0123456789".includes(key[0]) || key === "true" || key === "false";
103
24
 
104
25
  export function compileConstant(a: string) {
105
26
  const vv = scalarParse(a[0] === "'" ? a.slice(1, a.length - 1) : a);
@@ -114,7 +35,7 @@ const compileArgument = (a: string, strings?: string[]) => {
114
35
  if (a[0] === "!") {
115
36
  const getter = compilePropGet(a.slice(1));
116
37
  return (c: IArrmatron) => !getter(c);
117
- };
38
+ }
118
39
 
119
40
  if (a[0] === "#" && a[1] === "#") {
120
41
  const vv = strings?.[Number(a.slice(2))];
@@ -128,6 +49,7 @@ const compileArgument = (a: string, strings?: string[]) => {
128
49
 
129
50
  const splitExpression = (v: ExpressionText) =>
130
51
  v
52
+ .trim()
131
53
  .replace(/(&&|!=|==|>|<|\[|\]|\|\||\?\??)/g, (_, s) => `${OP_SHORTCUTS[s]}`)
132
54
  .split("|")
133
55
  .map((s) => s.trim());
@@ -139,8 +61,8 @@ function withPipes(pipes: string[]) {
139
61
  const strings: string[] = [];
140
62
 
141
63
  const expr = key.replaceAll(/'[^']*'/g, (s) => {
142
- strings.push(s.slice(1, -1))
143
- return `##${strings.length - 1}`
64
+ strings.push(s.slice(1, -1));
65
+ return `##${strings.length - 1}`;
144
66
  });
145
67
 
146
68
  const [pipeId, ...args] = expr.split(":").map((s) => s.trim());
@@ -175,10 +97,10 @@ function withPipes(pipes: string[]) {
175
97
  }
176
98
 
177
99
  function withHardValue(hardValue: ExpressionText, pipec: PipeFn): PipeFn {
178
- if (!hardValue || hardValue === '*') return pipec;
100
+ if (!hardValue || hardValue === "it") return pipec;
179
101
 
180
- if (hardValue.startsWith('*.')) {
181
- const propKey = hardValue.slice(2)
102
+ if (hardValue.startsWith("it.")) {
103
+ const propKey = hardValue.slice(3);
182
104
  return (c: IArrmatron, ini: any) => pipec(c, gettus(ini, propKey));
183
105
  }
184
106
 
@@ -188,8 +110,8 @@ function withHardValue(hardValue: ExpressionText, pipec: PipeFn): PipeFn {
188
110
  }
189
111
 
190
112
  export function compilePipeExpression(v: ExpressionText) {
191
- if (v.endsWith(')')) {
192
- const parts = v.slice(0, -1).split('(')
113
+ if (v.endsWith(")")) {
114
+ const parts = v.slice(0, -1).split("(");
193
115
  return compilePipeExpression(`${parts[0]}=${parts[1]}`);
194
116
  }
195
117
 
@@ -212,4 +134,3 @@ export function compilePlaceholder(expr: string): Fn {
212
134
  export function compileJsExpression(x: string) {
213
135
  return new Function("c", x.replaceAll(/[^'a-z]([a-z][a-z0-9-.]+)/gi, 'c.prop("$1")'));
214
136
  }
215
-
@@ -0,0 +1,15 @@
1
+ import { mapEntries } from "ultimus";
2
+
3
+ export const narrowData = (data: any) => {
4
+ if (!data) {
5
+ return [];
6
+ }
7
+ if (typeof data === "string") return data.split(",").map((id) => ({ id, name: id }));
8
+ if (Array.isArray(data)) return data;
9
+ if (typeof data[Symbol.iterator] === "function") return [...data];
10
+ if (typeof data === "object")
11
+ return mapEntries(data, (key, value) =>
12
+ typeof value === "object" ? { ...value, id: key } : { id: key, name: String(value) }
13
+ );
14
+ return [{ id: String(data), name: String(data) }];
15
+ };
package/types.ts CHANGED
@@ -3,47 +3,51 @@ import { Uid, Delta, Data, LogEntry, Hash, XmlNode } from "ultimus/types";
3
3
  import { Arrmatron } from "./src/core/Arrmatron";
4
4
 
5
5
  export type IComponent = {
6
- // optional Lifecycle method called when the component is done.
7
- done?: (c: IArrmatron) => void;
8
6
  // optional Lifecycle method called during initialization.
9
-
10
- init?: (c: IArrmatron) => Delta | null | undefined | unknown | void;
11
- // optional Lifecycle method called when the state changes.
7
+ __init?: (c: IArrmatron) => Delta | null | undefined | unknown | void;
8
+ // optional method called when the state changes.
12
9
  __stateChanged?: (changes: Map<string, unknown>) => void;
10
+ // optional method to get a state property by name.
11
+ __getStateProperty?: (propName: string) => any;
13
12
  // state key/values
14
13
  [key: string]: unknown;
15
- }
14
+ };
16
15
 
17
- export type IArrmatron = {
18
- isDone: any;
19
- readonly uid: Uid;
20
- parent?: IArrmatron;
21
- children?: Map<Uid, IArrmatron>;
22
- scope?: IArrmatron;
23
- $component: IComponent;
24
- root: IArrmatron;
16
+ export type IComponentContext = {
25
17
  platform: IPlatform;
26
18
 
27
19
  // state access
28
20
  get: (propId: string) => unknown;
29
- getFromScope: (key: string) => unknown;
30
21
  up: (d: Delta | null | void | unknown, force?: boolean) => void;
31
22
 
32
23
  touch: () => void;
33
24
  emit: (key: string, arg1: Delta) => void;
34
-
35
- // done life-cycle hook
36
- done: () => void;
37
25
  defer: (fn: () => void) => void;
38
26
 
39
- // get registered arrmatron instance by reference. look up recurrsively from current to root scopes.
40
27
  readonly refId?: string;
41
- getByRef: (refId: string) => IArrmatron | undefined;
42
28
 
43
29
  log: (val: unknown, ...arg1: unknown[]) => void;
44
30
  logError: (error: unknown, ...arg1: unknown[]) => void;
45
31
  toast(t: LogEntry): void;
46
- }
32
+ };
33
+
34
+ export type IArrmatron = IComponentContext & {
35
+ readonly uid: Uid;
36
+ parent?: IArrmatron;
37
+ children?: Map<Uid, Arrmatron>;
38
+ scope?: IArrmatron;
39
+ $component: IComponent;
40
+ root: IArrmatron;
41
+
42
+ getFromScope: (key: string) => unknown;
43
+ // get registered arrmatron instance by reference. look up recurrsively from current to root scopes.
44
+ getByRef: (refId: string) => IArrmatron | undefined;
45
+ };
46
+
47
+ export type IArrmatronRoot = IArrmatron & {
48
+ // done life-cycle hook
49
+ done: () => void;
50
+ };
47
51
 
48
52
  export type IManifestNode = {
49
53
  connectors?: Map<string, (c: IArrmatron, x: unknown) => unknown>;
@@ -54,7 +58,7 @@ export type IManifestNode = {
54
58
  resolveInitialProps: (c: IArrmatron) => Hash;
55
59
 
56
60
  createArrmatron: (platform: IPlatform, parent?: Arrmatron, scope?: Arrmatron) => Arrmatron;
57
- }
61
+ };
58
62
 
59
63
  export type IPlatform = {
60
64
  log(arg0: LogEntry): void;
@@ -66,10 +70,14 @@ export type IPlatform = {
66
70
  getResource(key: string | string[]): Data;
67
71
  getFunction(id: string): (...args: unknown[]) => unknown;
68
72
  redraw(source: IArrmatron, root: IArrmatron): void;
69
- createComponent(def: IComponentDescriptor | string, initials: Hash, ctx: IArrmatron): IComponent | Hash;
70
- }
73
+ createComponent(
74
+ def: IComponentDescriptor | string,
75
+ initials: Hash,
76
+ ctx: IComponentContext
77
+ ): IComponent | Hash;
78
+ };
71
79
 
72
- export type ComponentConstructor = new (initials: Hash, c: IArrmatron) => IComponent;
80
+ export type ComponentConstructor = new (initials: Hash, ctx: IComponentContext) => IComponent;
73
81
 
74
82
  export type IComponentDescriptor = {
75
83
  tag?: string;
@@ -77,6 +85,6 @@ export type IComponentDescriptor = {
77
85
  Ctor?: ComponentConstructor;
78
86
  unknown?: boolean;
79
87
  native?: boolean;
80
- }
88
+ };
81
89
 
82
- export type EContent = Map<Uid, IManifestNode>;
90
+ export type EContent = Map<Uid, IManifestNode>;
package/docs/glossary.md DELETED
@@ -1,19 +0,0 @@
1
- # Glossary
2
-
3
- | _Term_ | _Description_ |
4
- | ----------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
5
- | `Program` | A formal prescription given to a runtime to process (to apply trasformation rules in some order) incoming data. |
6
- | `Type` | A specification that defines the possible values that can be reffered by a variable. |
7
- | `Variable` | A program entity, which may reffer some data value. |
8
- | `Object-oriented programming` | A programming style that evolves around composing components, which interact with each others by handling streams of events. |
9
- | `Component` | An individual runtime entity that has a lifecycle, state, and behavior. Components can interact with others and are treated as black boxes. |
10
- | `Class` | A design-time specification for the state and behavior of a component. It provides guidance for the runtime to create and manage instances of the component. |
11
- | `State` | A private data structure that is managed by a component. |
12
- | `Property` | A public getter/setter pair that allows access to a state value by a specified key. |
13
- | `Behavior` | A specification that defines how a component should change its state in response to external impacts. |
14
- | `Interface` | A design-time specification of the methods and signatures that can be used to interact with a component. |
15
- | `Composition` | The | parent-child and context-content relationships between components. It defines the scope and lifecycle of the components that they can interact with. |
16
- | `Template` | A text written in a formal language that allows for the description of component composition and property binding in a declarative way, focused on the desired outcome rather than the implementation details. |
17
- | `Property binding` | A formal sentence that expresses a dependency between the value of a target component's property and other properties. It ensures that the target property is updated whenever the dependency changes. |
18
- | `Property propagation` | A one-way binding that passes property values from a container component to its contents. |
19
- | `Hook` | A component-bound method that is called by the runtime at specific points in the component's lifecycle. |