@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.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/appManifest/bindings.d.ts +3 -3
- package/dist/appManifest/bindings.d.ts.map +1 -1
- package/dist/appManifest/bindings.js +46 -65
- package/dist/appManifest/bindings.js.map +1 -1
- package/dist/appManifest/screens.js +2 -2
- package/dist/appManifest/screens.js.map +1 -1
- package/dist/bindings.d.ts +39 -64
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js.map +1 -1
- package/dist/storage.d.ts +611 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +173 -1
- package/dist/storage.js.map +1 -1
- package/dist/structure/generated.d.ts +2 -2
- package/dist/structure/generated.js +2 -2
- package/dist/structure/generated.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/src/appManifest/bindings.ts +46 -72
- package/src/appManifest/screens.ts +2 -2
- package/src/appManifest.test.ts +7 -8
- package/src/bindings.test.ts +86 -516
- package/src/bindings.ts +41 -88
- package/src/contracts.test.ts +10 -20
- package/src/storage-schemas.test.ts +86 -0
- package/src/storage.ts +197 -0
- package/src/structure/generated.ts +2 -2
- package/src/types.ts +2 -2
|
@@ -14,29 +14,14 @@ export function isComponentDataBindingRegistry(value: unknown): boolean {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/*** Validate
|
|
18
|
-
export function
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
82
|
-
function
|
|
63
|
+
/*** Validate a serializable capability invocation and optional result slot. */
|
|
64
|
+
function isBindingInvocation(value: unknown): boolean {
|
|
83
65
|
return (
|
|
84
|
-
|
|
85
|
-
(
|
|
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
|
|
91
|
-
function
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
(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
|
|
101
|
-
function
|
|
82
|
+
/*** Validate array, fallback, literal, object and transform expression forms. */
|
|
83
|
+
function isBindingExpressionNode(value: unknown): boolean {
|
|
102
84
|
return (
|
|
103
85
|
isRecord(value) &&
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
111
|
-
function
|
|
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.
|
|
115
|
-
|
|
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
|
|
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.
|
|
116
|
+
(value.value === undefined || isBindingExpression(value.value)) &&
|
|
126
117
|
isOptionalString(value.message)
|
|
127
118
|
);
|
|
128
119
|
}
|
|
129
120
|
|
|
130
|
-
/*** Validate
|
|
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(
|
|
123
|
+
return isRecord(value) && Object.values(value).every(isBindingExpression);
|
|
142
124
|
}
|
|
143
125
|
|
|
144
|
-
/*** Validate
|
|
145
|
-
function
|
|
126
|
+
/*** Validate ordered expression transforms. */
|
|
127
|
+
function isBindingTransforms(value: unknown): boolean {
|
|
146
128
|
return (
|
|
147
|
-
|
|
148
|
-
|
|
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 {
|
|
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
|
-
|
|
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)))
|
package/src/appManifest.test.ts
CHANGED
|
@@ -110,15 +110,14 @@ function createManifest(): Record<string, unknown> {
|
|
|
110
110
|
{
|
|
111
111
|
id: 'title',
|
|
112
112
|
type: 'Text',
|
|
113
|
-
repeat: { source: {
|
|
113
|
+
repeat: { source: { capability: 'state.items' }, itemAlias: 'item' },
|
|
114
114
|
},
|
|
115
115
|
],
|
|
116
116
|
},
|
|
117
117
|
dataLoaders: [
|
|
118
118
|
{
|
|
119
|
-
|
|
120
|
-
|
|
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: {
|
|
140
|
+
props: { text: { value: { capability: 'state.title' } } },
|
|
142
141
|
events: {
|
|
143
|
-
press: [{ target: {
|
|
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
|
|
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 = {
|
|
297
|
+
props.text = { value: { kind: 'provider-specific', path: 'title' } };
|
|
299
298
|
|
|
300
299
|
expect(isAppManifest(manifest)).toBe(false);
|
|
301
300
|
});
|