@ankhorage/contracts 25.1.0 → 26.0.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.
@@ -14,29 +14,14 @@ export function isComponentDataBindingRegistry(value: unknown): boolean {
14
14
  );
15
15
  }
16
16
 
17
- /*** Validate literal, operation and path-based binding sources. */
18
- export function isBindingValueSource(value: unknown): boolean {
19
- return (
20
- isRecord(value) &&
21
- typeof value.kind === 'string' &&
22
- ['context', 'event', 'literal', 'operation', 'state'].includes(value.kind) &&
23
- (value.kind === 'literal'
24
- ? isManifestValue(value.value)
25
- : value.kind === 'operation'
26
- ? isBindingOperationRef(value.operation) && isOptionalString(value.path)
27
- : typeof value.path === 'string')
28
- );
17
+ /*** Validate a readable capability reference or a recursive binding expression. */
18
+ export function isBindingExpression(value: unknown): boolean {
19
+ return isBindingCapabilityReference(value) || isBindingExpressionNode(value);
29
20
  }
30
21
 
31
- /*** Validate a screen operation loader and its optional input mapping. */
22
+ /*** Validate a screen loader as one portable capability invocation. */
32
23
  export function isScreenDataLoaderDefinition(value: unknown): boolean {
33
- return (
34
- isRecord(value) &&
35
- value.kind === 'operation' &&
36
- isOptionalString(value.id) &&
37
- isBindingOperationRef(value.operation) &&
38
- (value.input === undefined || isBindingInputMap(value.input))
39
- );
24
+ return isBindingInvocation(value);
40
25
  }
41
26
 
42
27
  /*** Validate component identity and its property and event bindings. */
@@ -55,104 +40,93 @@ function isComponentDataBinding(value: unknown): boolean {
55
40
  );
56
41
  }
57
42
 
58
- /*** Validate a property binding with optional transforms and fallback behavior. */
43
+ /*** Validate a property expression and its lifecycle-specific render behavior. */
59
44
  function isPropBinding(value: unknown): boolean {
60
45
  return (
61
46
  isRecord(value) &&
62
- isBindingValueSource(value.source) &&
63
- (value.fallback === undefined || isBindingFallback(value.fallback)) &&
47
+ isBindingExpression(value.value) &&
64
48
  (value.loading === undefined || isBindingLifecycleBehavior(value.loading)) &&
65
49
  (value.error === undefined || isBindingLifecycleBehavior(value.error)) &&
66
- (value.empty === undefined || isBindingLifecycleBehavior(value.empty)) &&
67
- isOptionalBindingTransforms(value.transforms)
50
+ (value.empty === undefined || isBindingLifecycleBehavior(value.empty))
68
51
  );
69
52
  }
70
53
 
71
- /*** Validate an event binding and its declared target. */
54
+ /*** Validate an event binding and its one canonical invocation target. */
72
55
  function isEventBinding(value: unknown): boolean {
73
56
  return (
74
57
  isRecord(value) &&
75
- isEventBindingTarget(value.target) &&
76
- (value.input === undefined || isBindingInputMap(value.input)) &&
58
+ isBindingInvocation(value.target) &&
77
59
  (value.when === undefined || isBindingCondition(value.when))
78
60
  );
79
61
  }
80
62
 
81
- /*** Validate the selected operation or action event target. */
82
- function isEventBindingTarget(value: unknown): boolean {
63
+ /*** Validate a serializable capability invocation and optional result slot. */
64
+ function isBindingInvocation(value: unknown): boolean {
83
65
  return (
84
- isRecord(value) &&
85
- ((value.kind === 'action' && typeof value.type === 'string') ||
86
- (value.kind === 'operation' && isBindingOperationRef(value.operation)))
66
+ isBindingCapabilityReference(value) &&
67
+ (value.input === undefined || isBindingInputMap(value.input))
87
68
  );
88
69
  }
89
70
 
90
- /*** Validate a binding condition and its source value. */
91
- function isBindingCondition(value: unknown): boolean {
71
+ /*** Validate a capability identity with optional output path or cached-result slot. */
72
+ function isBindingCapabilityReference(value: unknown): value is Record<string, unknown> {
92
73
  return (
93
74
  isRecord(value) &&
94
- isBindingValueSource(value.source) &&
95
- ['eq', 'exists', 'neq', 'notExists'].includes(String(value.operator)) &&
96
- (value.value === undefined || isManifestValue(value.value))
75
+ typeof value.capability === 'string' &&
76
+ value.capability.includes('.') &&
77
+ isOptionalString(value.path) &&
78
+ isOptionalString(value.result)
97
79
  );
98
80
  }
99
81
 
100
- /*** Validate API and operation identities with an optional endpoint reference. */
101
- function isBindingOperationRef(value: unknown): boolean {
82
+ /*** Validate array, fallback, literal, object and transform expression forms. */
83
+ function isBindingExpressionNode(value: unknown): boolean {
102
84
  return (
103
85
  isRecord(value) &&
104
- typeof value.apiId === 'string' &&
105
- typeof value.operationId === 'string' &&
106
- isOptionalString(value.endpointId)
86
+ ((value.kind === 'array' &&
87
+ Array.isArray(value.items) &&
88
+ value.items.every(isBindingExpression)) ||
89
+ (value.kind === 'fallback' &&
90
+ isBindingExpression(value.value) &&
91
+ isBindingExpression(value.fallback)) ||
92
+ (value.kind === 'literal' && isManifestValue(value.value)) ||
93
+ (value.kind === 'object' && isBindingInputMap(value.fields)) ||
94
+ (value.kind === 'transform' &&
95
+ isBindingExpression(value.value) &&
96
+ isBindingTransforms(value.transforms)))
107
97
  );
108
98
  }
109
99
 
110
- /*** Validate authored binding fallback values. */
111
- function isBindingFallback(value: unknown): boolean {
100
+ /*** Validate a condition expressed through the same recursive binding vocabulary. */
101
+ function isBindingCondition(value: unknown): boolean {
112
102
  return (
113
103
  isRecord(value) &&
114
- (value.value === undefined || isManifestValue(value.value)) &&
115
- (value.source === undefined || isBindingValueSource(value.source))
104
+ isBindingExpression(value.source) &&
105
+ ['eq', 'exists', 'neq', 'notExists'].includes(String(value.operator)) &&
106
+ (value.value === undefined || isBindingExpression(value.value))
116
107
  );
117
108
  }
118
109
 
119
- /*** Validate loading, empty and error binding behavior. */
110
+ /*** Validate a lifecycle state and its optional bound display expression. */
120
111
  function isBindingLifecycleBehavior(value: unknown): boolean {
121
112
  return (
122
113
  isRecord(value) &&
123
114
  typeof value.state === 'string' &&
124
115
  ['empty', 'error', 'loading'].includes(value.state) &&
125
- (value.fallback === undefined || isBindingFallback(value.fallback)) &&
116
+ (value.value === undefined || isBindingExpression(value.value)) &&
126
117
  isOptionalString(value.message)
127
118
  );
128
119
  }
129
120
 
130
- /*** Validate optional ordered binding transformations. */
131
- function isOptionalBindingTransforms(value: unknown): boolean {
132
- return (
133
- value === undefined ||
134
- (Array.isArray(value) &&
135
- value.every((transform) => ['lowercase', 'trim', 'uppercase'].includes(String(transform))))
136
- );
137
- }
138
-
139
- /*** Validate every value in an operation input mapping. */
121
+ /*** Validate every recursively authored invocation input expression. */
140
122
  function isBindingInputMap(value: unknown): boolean {
141
- return isRecord(value) && Object.values(value).every(isBindingInputValue);
123
+ return isRecord(value) && Object.values(value).every(isBindingExpression);
142
124
  }
143
125
 
144
- /*** Validate recursive array, object, literal and source operation inputs. */
145
- function isBindingInputValue(value: unknown): boolean {
126
+ /*** Validate ordered expression transforms. */
127
+ function isBindingTransforms(value: unknown): boolean {
146
128
  return (
147
- isRecord(value) &&
148
- typeof value.kind === 'string' &&
149
- ((value.kind === 'array' &&
150
- Array.isArray(value.items) &&
151
- value.items.every(isBindingInputValue)) ||
152
- (value.kind === 'literal' && isManifestValue(value.value)) ||
153
- (value.kind === 'object' && isBindingInputMap(value.fields)) ||
154
- (value.kind === 'source' &&
155
- isBindingValueSource(value.source) &&
156
- isOptionalBindingTransforms(value.transforms)))
129
+ Array.isArray(value) &&
130
+ value.every((transform) => ['lowercase', 'trim', 'uppercase'].includes(String(transform)))
157
131
  );
158
132
  }
@@ -6,7 +6,7 @@ import { isSerializableSet } from '../collections';
6
6
  import { isMediaAssetReference } from '../media';
7
7
  import { ANKHORAGE_CAPABILITY_NAMES, ANKHORAGE_PERMISSION_NAMES } from '../requirements';
8
8
  import { APP_CATEGORIES, type ThemeRegistry } from '../types';
9
- import { isBindingValueSource, isScreenDataLoaderDefinition } from './bindings';
9
+ import { isBindingExpression, isScreenDataLoaderDefinition } from './bindings';
10
10
 
11
11
  export { isAppNavigatorManifest } from './navigator';
12
12
 
@@ -101,7 +101,7 @@ function isUiNode(value: unknown): boolean {
101
101
  function isUiNodeRepeatSpec(value: unknown): boolean {
102
102
  return (
103
103
  isRecord(value) &&
104
- isBindingValueSource(value.source) &&
104
+ isBindingExpression(value.source) &&
105
105
  isOptionalString(value.itemAlias) &&
106
106
  isOptionalString(value.keyPath) &&
107
107
  (value.empty === undefined || (Array.isArray(value.empty) && value.empty.every(isUiNode)))
@@ -110,15 +110,14 @@ function createManifest(): Record<string, unknown> {
110
110
  {
111
111
  id: 'title',
112
112
  type: 'Text',
113
- repeat: { source: { kind: 'state', path: 'items' }, itemAlias: 'item' },
113
+ repeat: { source: { capability: 'state.items' }, itemAlias: 'item' },
114
114
  },
115
115
  ],
116
116
  },
117
117
  dataLoaders: [
118
118
  {
119
- kind: 'operation',
120
- id: 'load-products',
121
- operation: { apiId: 'nutrition', endpointId: 'products', operationId: 'products.list' },
119
+ capability: 'api.nutrition.productsList',
120
+ result: 'products',
122
121
  },
123
122
  ],
124
123
  requires: {
@@ -138,9 +137,9 @@ function createManifest(): Record<string, unknown> {
138
137
  dataBindings: {
139
138
  title: {
140
139
  componentId: 'title',
141
- props: { text: { source: { kind: 'state', path: 'title' } } },
140
+ props: { text: { value: { capability: 'state.title' } } },
142
141
  events: {
143
- press: [{ target: { kind: 'action', type: 'console' } }],
142
+ press: [{ target: { capability: 'console.log' } }],
144
143
  },
145
144
  },
146
145
  },
@@ -291,11 +290,11 @@ describe('AppManifest runtime parsing', () => {
291
290
  expect(isAppManifest(manifest)).toBe(false);
292
291
  });
293
292
 
294
- it('rejects invalid data-binding source kinds', () => {
293
+ it('rejects invalid data-binding expression shapes', () => {
295
294
  const manifest = createManifest();
296
295
  const bindings = manifest.dataBindings as Record<string, Record<string, unknown>>;
297
296
  const props = bindings.title?.props as Record<string, Record<string, unknown>>;
298
- props.text = { source: { kind: 'provider-specific', path: 'title' } };
297
+ props.text = { value: { kind: 'provider-specific', path: 'title' } };
299
298
 
300
299
  expect(isAppManifest(manifest)).toBe(false);
301
300
  });