@ankhorage/contracts 25.2.0 → 26.0.1

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/src/bindings.ts CHANGED
@@ -1,123 +1,76 @@
1
+ import type { Capability } from './capability';
1
2
  import type { EntityRegistry, ValueMap } from './collections';
2
- import type { ApiId, EndpointId, OperationId } from './data';
3
3
  import type { SerializableValue } from './serializable';
4
4
 
5
5
  export type ComponentInstanceId = string;
6
6
  export type ComponentTypeId = string;
7
-
8
7
  export type BindingValue = SerializableValue;
9
-
10
8
  export type BindingDataPath = string;
11
-
12
9
  export type BindingValueTransform = 'lowercase' | 'trim' | 'uppercase';
13
10
 
14
- export interface BindingOperationRef {
15
- readonly apiId: ApiId;
16
- readonly operationId: OperationId;
17
- readonly endpointId?: EndpointId;
11
+ /** A portable reference to a readable capability value or a cached invocation result. */
12
+ export interface BindingCapabilityReference {
13
+ readonly capability: Capability['id'];
14
+ readonly path?: BindingDataPath;
15
+ readonly result?: string;
18
16
  }
19
17
 
20
- export type BindingValueSource =
21
- | {
22
- readonly kind: 'context';
23
- readonly path: BindingDataPath;
24
- }
25
- | {
26
- readonly kind: 'event';
27
- readonly path: BindingDataPath;
28
- }
29
- | {
30
- readonly kind: 'literal';
31
- readonly value: BindingValue;
32
- }
18
+ /** A portable request to invoke a capability and optionally retain its result in a local slot. */
19
+ export interface BindingInvocation {
20
+ readonly capability: Capability['id'];
21
+ readonly input?: BindingInputMap;
22
+ readonly result?: string;
23
+ }
24
+
25
+ /** A recursive, serializable value expression used by bindings and invocation inputs. */
26
+ export type BindingExpression =
27
+ | BindingCapabilityReference
28
+ | { readonly kind: 'array'; readonly items: readonly BindingExpression[] }
33
29
  | {
34
- readonly kind: 'operation';
35
- readonly operation: BindingOperationRef;
36
- readonly path?: BindingDataPath;
30
+ readonly kind: 'fallback';
31
+ readonly value: BindingExpression;
32
+ readonly fallback: BindingExpression;
37
33
  }
34
+ | { readonly kind: 'literal'; readonly value: BindingValue }
35
+ | { readonly kind: 'object'; readonly fields: ValueMap<string, BindingExpression> }
38
36
  | {
39
- readonly kind: 'state';
40
- readonly path: BindingDataPath;
37
+ readonly kind: 'transform';
38
+ readonly value: BindingExpression;
39
+ readonly transforms: readonly BindingValueTransform[];
41
40
  };
42
41
 
43
- export interface BindingValueExpression {
44
- readonly source: BindingValueSource;
45
- readonly transforms?: readonly BindingValueTransform[];
46
- }
42
+ export type BindingInputValue = BindingExpression;
43
+ export type BindingInputMap = ValueMap<string, BindingInputValue>;
44
+ export type BindingConditionOperator = 'eq' | 'exists' | 'neq' | 'notExists';
47
45
 
48
- export interface BindingFallback {
49
- readonly value?: BindingValue;
50
- readonly source?: BindingValueSource;
46
+ /** A condition evaluated from portable binding expressions. */
47
+ export interface BindingCondition {
48
+ readonly source: BindingExpression;
49
+ readonly operator: BindingConditionOperator;
50
+ readonly value?: BindingExpression;
51
51
  }
52
52
 
53
- export type BindingLifecycleState = 'empty' | 'error' | 'loading';
54
-
53
+ /** A lifecycle-specific expression rendered when a bound value reaches one state. */
55
54
  export interface BindingLifecycleBehavior {
56
55
  readonly state: BindingLifecycleState;
57
- readonly fallback?: BindingFallback;
56
+ readonly value?: BindingExpression;
58
57
  readonly message?: string;
59
58
  }
60
59
 
60
+ export type BindingLifecycleState = 'empty' | 'error' | 'loading';
61
+
61
62
  export interface PropBinding {
62
- readonly source: BindingValueSource;
63
- readonly fallback?: BindingFallback;
63
+ readonly value: BindingExpression;
64
64
  readonly loading?: BindingLifecycleBehavior;
65
65
  readonly error?: BindingLifecycleBehavior;
66
66
  readonly empty?: BindingLifecycleBehavior;
67
- readonly transforms?: readonly BindingValueTransform[];
68
67
  }
69
68
 
70
- export type BindingInputValue =
71
- | {
72
- readonly kind: 'array';
73
- readonly items: readonly BindingInputValue[];
74
- }
75
- | {
76
- readonly kind: 'literal';
77
- readonly value: BindingValue;
78
- }
79
- | {
80
- readonly kind: 'object';
81
- readonly fields: ValueMap<string, BindingInputValue>;
82
- }
83
- | {
84
- readonly kind: 'source';
85
- readonly source: BindingValueSource;
86
- readonly transforms?: readonly BindingValueTransform[];
87
- };
88
-
89
- export type BindingInputMap = ValueMap<string, BindingInputValue>;
90
-
91
- export interface OperationScreenDataLoaderDefinition {
92
- readonly kind: 'operation';
93
- readonly id?: string;
94
- readonly operation: BindingOperationRef;
95
- readonly input?: BindingInputMap;
96
- }
97
-
98
- export type ScreenDataLoaderDefinition = OperationScreenDataLoaderDefinition;
99
-
100
- export type BindingConditionOperator = 'eq' | 'exists' | 'neq' | 'notExists';
101
-
102
- export interface BindingCondition {
103
- readonly source: BindingValueSource;
104
- readonly operator: BindingConditionOperator;
105
- readonly value?: BindingValue;
106
- }
107
-
108
- export type EventBindingTarget =
109
- | {
110
- readonly kind: 'action';
111
- readonly type: string;
112
- }
113
- | {
114
- readonly kind: 'operation';
115
- readonly operation: BindingOperationRef;
116
- };
69
+ /** A screen loader is one capability invocation, optionally storing its result by slot name. */
70
+ export type ScreenDataLoaderDefinition = BindingInvocation;
117
71
 
118
72
  export interface EventBinding {
119
- readonly target: EventBindingTarget;
120
- readonly input?: BindingInputMap;
73
+ readonly target: BindingInvocation;
121
74
  readonly when?: BindingCondition;
122
75
  }
123
76
 
@@ -219,7 +219,7 @@ describe('contracts', () => {
219
219
  expect(theme.recipes?.patterns?.Panel?.padding).toBe('xl');
220
220
  });
221
221
 
222
- it('accepts screens with operation data loaders and repeat empty-state nodes', () => {
222
+ it('accepts screens with capability loaders and repeat empty-state nodes', () => {
223
223
  const manifest: Pick<AppManifest, 'screens'> = {
224
224
  screens: {
225
225
  products: {
@@ -230,12 +230,8 @@ describe('contracts', () => {
230
230
  type: 'Grid',
231
231
  repeat: {
232
232
  source: {
233
- kind: 'operation',
234
- operation: {
235
- dataSourceId: 'nutrition-api',
236
- endpointId: 'products',
237
- operationId: 'nutrition.products.list',
238
- },
233
+ capability: 'api.nutrition.productsList',
234
+ result: 'products',
239
235
  path: 'products',
240
236
  },
241
237
  empty: [
@@ -252,20 +248,12 @@ describe('contracts', () => {
252
248
  },
253
249
  dataLoaders: [
254
250
  {
255
- kind: 'operation',
256
- id: 'product-detail',
257
- operation: {
258
- dataSourceId: 'nutrition-api',
259
- endpointId: 'products',
260
- operationId: 'nutrition.products.getById',
261
- },
251
+ capability: 'api.nutrition.productsGetById',
252
+ result: 'product',
262
253
  input: {
263
254
  id: {
264
- kind: 'source',
265
- source: {
266
- kind: 'context',
267
- path: 'route.params.id',
268
- },
255
+ capability: 'context.route',
256
+ path: 'params.id',
269
257
  },
270
258
  },
271
259
  },
@@ -275,7 +263,9 @@ describe('contracts', () => {
275
263
  };
276
264
 
277
265
  expect(JSON.parse(JSON.stringify(manifest))).toEqual(manifest);
278
- expect(manifest.screens.products?.dataLoaders?.[0]?.kind).toBe('operation');
266
+ expect(manifest.screens.products?.dataLoaders?.[0]?.capability).toBe(
267
+ 'api.nutrition.productsGetById',
268
+ );
279
269
  expect(manifest.screens.products?.root.repeat?.empty?.[0]?.type).toBe('Notice');
280
270
  });
281
271
 
@@ -2,12 +2,12 @@
2
2
  import type { StructureDescriptorDocument } from '@ankhorage/contracts/structure';
3
3
 
4
4
  export const STRUCTURE_DESCRIPTOR_COMPILER_VERSION = '6.0.3';
5
- export const STRUCTURE_DESCRIPTOR_FINGERPRINT = 'sha256:e6117f029e0a0fde13ed1d08fe1a39d7e06a9f83a0d32198865446501875e27d';
5
+ export const STRUCTURE_DESCRIPTOR_FINGERPRINT = 'sha256:241a40ac622d83197bcad3df767803ab8757978774bf935cee990416d3e2774e';
6
6
 
7
7
  export const STRUCTURE_DESCRIPTOR = {
8
8
  "protocolVersion": 1,
9
9
  "packageName": "@ankhorage/contracts",
10
- "packageVersion": "25.2.0",
10
+ "packageVersion": "26.0.1",
11
11
  "roots": {
12
12
  "auth-flow": "AuthFlowConfig",
13
13
  "auth-profile": "AuthProfileSpec",
package/src/types.ts CHANGED
@@ -2,7 +2,7 @@ import type { ColorHarmony } from '@ankhorage/color-theory';
2
2
 
3
3
  import type { AuthIdentifierKind, AuthSignUpField } from './auth';
4
4
  import type {
5
- BindingValueSource,
5
+ BindingExpression,
6
6
  ComponentDataBindingRegistry,
7
7
  ScreenDataLoaderDefinition,
8
8
  } from './bindings';
@@ -153,7 +153,7 @@ export interface SvgIconSpec extends IconPresentationSpec {
153
153
  export type IconSpec = NamedIconSpec | SvgIconSpec;
154
154
 
155
155
  export interface UiNodeRepeatSpec {
156
- source: BindingValueSource;
156
+ source: BindingExpression;
157
157
  itemAlias?: string;
158
158
  keyPath?: string;
159
159
  empty?: readonly UiNode[];