@ankhorage/contracts 7.1.0 → 7.3.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 +4 -0
- package/dist/appManifest/bindings.d.ts.map +1 -0
- package/dist/appManifest/bindings.js +96 -0
- package/dist/appManifest/bindings.js.map +1 -0
- package/dist/appManifest/dataSources.d.ts +2 -0
- package/dist/appManifest/dataSources.d.ts.map +1 -0
- package/dist/appManifest/dataSources.js +133 -0
- package/dist/appManifest/dataSources.js.map +1 -0
- package/dist/appManifest/generatedApis.d.ts +2 -0
- package/dist/appManifest/generatedApis.d.ts.map +1 -0
- package/dist/appManifest/generatedApis.js +87 -0
- package/dist/appManifest/generatedApis.js.map +1 -0
- package/dist/appManifest/infra.d.ts +2 -0
- package/dist/appManifest/infra.d.ts.map +1 -0
- package/dist/appManifest/infra.js +136 -0
- package/dist/appManifest/infra.js.map +1 -0
- package/dist/appManifest/media.d.ts +3 -0
- package/dist/appManifest/media.d.ts.map +1 -0
- package/dist/appManifest/media.js +75 -0
- package/dist/appManifest/media.js.map +1 -0
- package/dist/appManifest/screens.d.ts +6 -0
- package/dist/appManifest/screens.d.ts.map +1 -0
- package/dist/appManifest/screens.js +119 -0
- package/dist/appManifest/screens.js.map +1 -0
- package/dist/appManifest/shared.d.ts +8 -0
- package/dist/appManifest/shared.d.ts.map +1 -0
- package/dist/appManifest/shared.js +31 -0
- package/dist/appManifest/shared.js.map +1 -0
- package/dist/appManifest.d.ts +19 -0
- package/dist/appManifest.d.ts.map +1 -0
- package/dist/appManifest.js +67 -0
- package/dist/appManifest.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +48 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +11 -0
- package/dist/media.js.map +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -2
- package/src/appManifest/bindings.ts +135 -0
- package/src/appManifest/dataSources.ts +175 -0
- package/src/appManifest/generatedApis.ts +122 -0
- package/src/appManifest/infra.ts +199 -0
- package/src/appManifest/media.ts +101 -0
- package/src/appManifest/screens.ts +171 -0
- package/src/appManifest/shared.ts +40 -0
- package/src/appManifest.test.ts +203 -0
- package/src/appManifest.ts +89 -0
- package/src/index.ts +2 -0
- package/src/media.test.ts +70 -0
- package/src/media.ts +68 -0
- package/src/types.ts +3 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { AUTH_OAUTH_PROVIDER_IDS } from '../auth';
|
|
2
|
+
import {
|
|
3
|
+
AUTH_PROFILE_CREATE_STRATEGIES,
|
|
4
|
+
AUTH_PROFILE_PRIMARY_KEY_STRATEGIES,
|
|
5
|
+
AUTH_PROFILE_UPDATE_STRATEGIES,
|
|
6
|
+
AUTH_SCOPES,
|
|
7
|
+
AUTH_SIGN_IN_IDENTIFIERS,
|
|
8
|
+
AUTH_SIGN_UP_POLICIES,
|
|
9
|
+
AUTHZ_ENGINES,
|
|
10
|
+
AUTHZ_KINDS,
|
|
11
|
+
DATABASE_TIERS,
|
|
12
|
+
STATE_PERSISTENCE_MODES,
|
|
13
|
+
STORAGE_PROVIDERS,
|
|
14
|
+
} from '../types';
|
|
15
|
+
import {
|
|
16
|
+
isOptionalBoolean,
|
|
17
|
+
isOptionalString,
|
|
18
|
+
isRecord,
|
|
19
|
+
isStringArray,
|
|
20
|
+
isStringRecord,
|
|
21
|
+
} from './shared';
|
|
22
|
+
|
|
23
|
+
const AUTH_OAUTH_PROVIDER_SET = new Set<string>(AUTH_OAUTH_PROVIDER_IDS);
|
|
24
|
+
const AUTH_PROFILE_CREATE_STRATEGY_SET = new Set<string>(AUTH_PROFILE_CREATE_STRATEGIES);
|
|
25
|
+
const AUTH_PROFILE_PRIMARY_KEY_STRATEGY_SET = new Set<string>(AUTH_PROFILE_PRIMARY_KEY_STRATEGIES);
|
|
26
|
+
const AUTH_PROFILE_UPDATE_STRATEGY_SET = new Set<string>(AUTH_PROFILE_UPDATE_STRATEGIES);
|
|
27
|
+
const AUTH_SCOPE_SET = new Set<string>(AUTH_SCOPES);
|
|
28
|
+
const AUTH_SIGN_IN_IDENTIFIER_SET = new Set<string>(AUTH_SIGN_IN_IDENTIFIERS);
|
|
29
|
+
const AUTH_SIGN_UP_POLICY_SET = new Set<string>(AUTH_SIGN_UP_POLICIES);
|
|
30
|
+
const AUTHZ_ENGINE_SET = new Set<string>(AUTHZ_ENGINES);
|
|
31
|
+
const AUTHZ_KIND_SET = new Set<string>(AUTHZ_KINDS);
|
|
32
|
+
const DATABASE_TIER_SET = new Set<string>(DATABASE_TIERS);
|
|
33
|
+
const STATE_PERSISTENCE_MODE_SET = new Set<string>(STATE_PERSISTENCE_MODES);
|
|
34
|
+
const STORAGE_PROVIDER_SET = new Set<string>(STORAGE_PROVIDERS);
|
|
35
|
+
|
|
36
|
+
export function isInfraManifest(value: unknown): boolean {
|
|
37
|
+
return (
|
|
38
|
+
isRecord(value) &&
|
|
39
|
+
(value.deployment === undefined || isDeploymentSpec(value.deployment)) &&
|
|
40
|
+
(value.auth === undefined || isAuthSpec(value.auth)) &&
|
|
41
|
+
(value.database === undefined || isDatabaseSpec(value.database)) &&
|
|
42
|
+
(value.storage === undefined || isStorageSpec(value.storage)) &&
|
|
43
|
+
(value.state === undefined || isStateSpec(value.state)) &&
|
|
44
|
+
(value.networking === undefined || isNetworkingSpec(value.networking)) &&
|
|
45
|
+
Array.isArray(value.modules) &&
|
|
46
|
+
value.modules.every((moduleId) => typeof moduleId === 'string') &&
|
|
47
|
+
(value.modulesConfig === undefined || isRecord(value.modulesConfig)) &&
|
|
48
|
+
!('plugins' in value) &&
|
|
49
|
+
!('pluginsConfig' in value)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isDeploymentSpec(value: unknown): boolean {
|
|
54
|
+
return (
|
|
55
|
+
isRecord(value) && typeof value.target === 'string' && typeof value.monitoring === 'boolean'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isDatabaseSpec(value: unknown): boolean {
|
|
60
|
+
return (
|
|
61
|
+
isRecord(value) &&
|
|
62
|
+
typeof value.provider === 'string' &&
|
|
63
|
+
typeof value.tier === 'string' &&
|
|
64
|
+
DATABASE_TIER_SET.has(value.tier)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isStorageSpec(value: unknown): boolean {
|
|
69
|
+
return (
|
|
70
|
+
isRecord(value) &&
|
|
71
|
+
typeof value.provider === 'string' &&
|
|
72
|
+
STORAGE_PROVIDER_SET.has(value.provider) &&
|
|
73
|
+
isStringArray(value.buckets)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isStateSpec(value: unknown): boolean {
|
|
78
|
+
return (
|
|
79
|
+
isRecord(value) &&
|
|
80
|
+
typeof value.provider === 'string' &&
|
|
81
|
+
(value.persistence === undefined ||
|
|
82
|
+
(typeof value.persistence === 'string' && STATE_PERSISTENCE_MODE_SET.has(value.persistence)))
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isNetworkingSpec(value: unknown): boolean {
|
|
87
|
+
return isRecord(value) && isOptionalString(value.domain) && typeof value.cdn === 'boolean';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isAuthSpec(value: unknown): boolean {
|
|
91
|
+
return (
|
|
92
|
+
isRecord(value) &&
|
|
93
|
+
typeof value.scope === 'string' &&
|
|
94
|
+
AUTH_SCOPE_SET.has(value.scope) &&
|
|
95
|
+
typeof value.provider === 'string' &&
|
|
96
|
+
(value.authorization === undefined || isAuthzSpec(value.authorization)) &&
|
|
97
|
+
(value.flow === undefined || isAuthFlow(value.flow)) &&
|
|
98
|
+
(value.signIn === undefined || isAuthSignInSpec(value.signIn)) &&
|
|
99
|
+
(value.signUp === undefined || isAuthSignUpSpec(value.signUp)) &&
|
|
100
|
+
(value.oauth === undefined || isAuthOAuthConfig(value.oauth)) &&
|
|
101
|
+
(value.profile === undefined || isAuthProfileSpec(value.profile))
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isAuthzSpec(value: unknown): boolean {
|
|
106
|
+
return (
|
|
107
|
+
isRecord(value) &&
|
|
108
|
+
typeof value.kind === 'string' &&
|
|
109
|
+
AUTHZ_KIND_SET.has(value.kind) &&
|
|
110
|
+
typeof value.engine === 'string' &&
|
|
111
|
+
AUTHZ_ENGINE_SET.has(value.engine)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isAuthFlow(value: unknown): boolean {
|
|
116
|
+
return (
|
|
117
|
+
isRecord(value) &&
|
|
118
|
+
typeof value.signInRoute === 'string' &&
|
|
119
|
+
isOptionalString(value.signUpRoute) &&
|
|
120
|
+
isOptionalString(value.signOutRoute) &&
|
|
121
|
+
isOptionalString(value.forgotPasswordRoute) &&
|
|
122
|
+
isOptionalString(value.otpRoute) &&
|
|
123
|
+
typeof value.postSignInRoute === 'string' &&
|
|
124
|
+
isOptionalString(value.unauthorizedRoute)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isAuthSignInSpec(value: unknown): boolean {
|
|
129
|
+
return (
|
|
130
|
+
isRecord(value) &&
|
|
131
|
+
Array.isArray(value.identifiers) &&
|
|
132
|
+
value.identifiers.every(
|
|
133
|
+
(identifier) => typeof identifier === 'string' && AUTH_SIGN_IN_IDENTIFIER_SET.has(identifier),
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isAuthSignUpSpec(value: unknown): boolean {
|
|
139
|
+
return (
|
|
140
|
+
isRecord(value) &&
|
|
141
|
+
isStringArray(value.requiredFields) &&
|
|
142
|
+
(value.optionalFields === undefined || isStringArray(value.optionalFields)) &&
|
|
143
|
+
(value.signUpPolicy === undefined ||
|
|
144
|
+
(typeof value.signUpPolicy === 'string' && AUTH_SIGN_UP_POLICY_SET.has(value.signUpPolicy)))
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isAuthOAuthConfig(value: unknown): boolean {
|
|
149
|
+
return (
|
|
150
|
+
isRecord(value) &&
|
|
151
|
+
typeof value.enabled === 'boolean' &&
|
|
152
|
+
typeof value.callbackRoute === 'string' &&
|
|
153
|
+
Array.isArray(value.providers) &&
|
|
154
|
+
value.providers.every(isAuthOAuthProviderConfig)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isAuthOAuthProviderConfig(value: unknown): boolean {
|
|
159
|
+
return (
|
|
160
|
+
isRecord(value) &&
|
|
161
|
+
typeof value.id === 'string' &&
|
|
162
|
+
(AUTH_OAUTH_PROVIDER_SET.has(value.id) || value.id.length > 0) &&
|
|
163
|
+
isOptionalString(value.label) &&
|
|
164
|
+
isOptionalBoolean(value.enabled) &&
|
|
165
|
+
(value.scopes === undefined || isStringArray(value.scopes)) &&
|
|
166
|
+
(value.queryParams === undefined || isStringRecord(value.queryParams)) &&
|
|
167
|
+
(value.icon === undefined || isIconSpec(value.icon)) &&
|
|
168
|
+
(value.credentialsRef === undefined || typeof value.credentialsRef === 'string')
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function isAuthProfileSpec(value: unknown): boolean {
|
|
173
|
+
return (
|
|
174
|
+
isRecord(value) &&
|
|
175
|
+
isStringArray(value.fields) &&
|
|
176
|
+
isOptionalString(value.table) &&
|
|
177
|
+
(value.primaryKey === undefined ||
|
|
178
|
+
(typeof value.primaryKey === 'string' &&
|
|
179
|
+
AUTH_PROFILE_PRIMARY_KEY_STRATEGY_SET.has(value.primaryKey))) &&
|
|
180
|
+
(value.createStrategy === undefined ||
|
|
181
|
+
(typeof value.createStrategy === 'string' &&
|
|
182
|
+
AUTH_PROFILE_CREATE_STRATEGY_SET.has(value.createStrategy))) &&
|
|
183
|
+
(value.updateStrategy === undefined ||
|
|
184
|
+
(typeof value.updateStrategy === 'string' &&
|
|
185
|
+
AUTH_PROFILE_UPDATE_STRATEGY_SET.has(value.updateStrategy)))
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isIconSpec(value: unknown): boolean {
|
|
190
|
+
return (
|
|
191
|
+
isRecord(value) &&
|
|
192
|
+
typeof value.name === 'string' &&
|
|
193
|
+
isOptionalString(value.provider) &&
|
|
194
|
+
(value.size === undefined ||
|
|
195
|
+
typeof value.size === 'string' ||
|
|
196
|
+
typeof value.size === 'number') &&
|
|
197
|
+
isOptionalString(value.color)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MEDIA_ASSET_KINDS,
|
|
3
|
+
type MediaAsset,
|
|
4
|
+
type MediaAssetMetadata,
|
|
5
|
+
type MediaAssetSource,
|
|
6
|
+
type MediaManifest,
|
|
7
|
+
} from '../media';
|
|
8
|
+
import { isOptionalString, isRecord } from './shared';
|
|
9
|
+
|
|
10
|
+
const MEDIA_ASSET_KIND_SET = new Set<string>(MEDIA_ASSET_KINDS);
|
|
11
|
+
|
|
12
|
+
export function isMediaManifest(value: unknown): value is MediaManifest {
|
|
13
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['assets']) || !isRecord(value.assets)) return false;
|
|
14
|
+
|
|
15
|
+
return Object.entries(value.assets).every(
|
|
16
|
+
([assetId, asset]) => isMediaAsset(asset) && asset.id === assetId,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isMediaAsset(value: unknown): value is MediaAsset {
|
|
21
|
+
return (
|
|
22
|
+
isRecord(value) &&
|
|
23
|
+
hasOnlyKeys(value, ['id', 'name', 'kind', 'source', 'contentType', 'metadata']) &&
|
|
24
|
+
isNonEmptyString(value.id) &&
|
|
25
|
+
isNonEmptyString(value.name) &&
|
|
26
|
+
typeof value.kind === 'string' &&
|
|
27
|
+
MEDIA_ASSET_KIND_SET.has(value.kind) &&
|
|
28
|
+
isMediaAssetSource(value.source) &&
|
|
29
|
+
isOptionalString(value.contentType) &&
|
|
30
|
+
(value.metadata === undefined || isMediaAssetMetadata(value.metadata))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isMediaAssetSource(value: unknown): value is MediaAssetSource {
|
|
35
|
+
if (!isRecord(value) || typeof value.kind !== 'string') return false;
|
|
36
|
+
|
|
37
|
+
if (value.kind === 'storage') {
|
|
38
|
+
return (
|
|
39
|
+
hasOnlyKeys(value, ['kind', 'storageId', 'bucket', 'path']) &&
|
|
40
|
+
isOptionalString(value.storageId) &&
|
|
41
|
+
isNonEmptyString(value.bucket) &&
|
|
42
|
+
isNonEmptyString(value.path)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (value.kind === 'url') {
|
|
47
|
+
return hasOnlyKeys(value, ['kind', 'url']) && isStableRemoteUrl(value.url);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
value.kind === 'bundled' && hasOnlyKeys(value, ['kind', 'path']) && isBundledPath(value.path)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isMediaAssetMetadata(value: unknown): value is MediaAssetMetadata {
|
|
56
|
+
return (
|
|
57
|
+
isRecord(value) &&
|
|
58
|
+
hasOnlyKeys(value, [
|
|
59
|
+
'originalFileName',
|
|
60
|
+
'sizeBytes',
|
|
61
|
+
'createdAt',
|
|
62
|
+
'width',
|
|
63
|
+
'height',
|
|
64
|
+
'durationMs',
|
|
65
|
+
]) &&
|
|
66
|
+
isOptionalString(value.originalFileName) &&
|
|
67
|
+
isOptionalString(value.createdAt) &&
|
|
68
|
+
isOptionalFiniteNonNegativeNumber(value.sizeBytes) &&
|
|
69
|
+
isOptionalFinitePositiveNumber(value.width) &&
|
|
70
|
+
isOptionalFinitePositiveNumber(value.height) &&
|
|
71
|
+
isOptionalFiniteNonNegativeNumber(value.durationMs)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isStableRemoteUrl(value: unknown): boolean {
|
|
76
|
+
return typeof value === 'string' && /^https?:\/\//iu.test(value.trim());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isBundledPath(value: unknown): boolean {
|
|
80
|
+
if (!isNonEmptyString(value)) return false;
|
|
81
|
+
const path = value.trim();
|
|
82
|
+
if (path.startsWith('/') || /^[a-z][a-z0-9+.-]*:/iu.test(path)) return false;
|
|
83
|
+
return !path.split('/').includes('..');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
87
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isOptionalFiniteNonNegativeNumber(value: unknown): boolean {
|
|
91
|
+
return value === undefined || (typeof value === 'number' && Number.isFinite(value) && value >= 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isOptionalFinitePositiveNumber(value: unknown): boolean {
|
|
95
|
+
return value === undefined || (typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasOnlyKeys(value: Record<string, unknown>, allowedKeys: readonly string[]): boolean {
|
|
99
|
+
const allowed = new Set(allowedKeys);
|
|
100
|
+
return Object.keys(value).every((key) => allowed.has(key));
|
|
101
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { COLOR_HARMONIES } from '@ankhorage/color-theory';
|
|
2
|
+
|
|
3
|
+
import { APP_CATEGORIES, NAVIGATOR_TYPES } from '../types';
|
|
4
|
+
import { isBindingValueSource, isScreenDataLoaderDefinition } from './bindings';
|
|
5
|
+
import {
|
|
6
|
+
isOptionalBoolean,
|
|
7
|
+
isOptionalNumber,
|
|
8
|
+
isOptionalString,
|
|
9
|
+
isRecord,
|
|
10
|
+
isStringArray,
|
|
11
|
+
} from './shared';
|
|
12
|
+
|
|
13
|
+
const APP_CATEGORY_SET = new Set<string>(APP_CATEGORIES);
|
|
14
|
+
const COLOR_HARMONY_SET = new Set<string>(COLOR_HARMONIES);
|
|
15
|
+
const NAVIGATOR_TYPE_SET = new Set<string>(NAVIGATOR_TYPES);
|
|
16
|
+
const SPLASH_SCREEN_RESIZE_MODE_SET = new Set<string>(['contain', 'cover', 'native']);
|
|
17
|
+
|
|
18
|
+
export function isManifestMetadata(value: unknown): boolean {
|
|
19
|
+
return (
|
|
20
|
+
isRecord(value) &&
|
|
21
|
+
typeof value.name === 'string' &&
|
|
22
|
+
typeof value.slug === 'string' &&
|
|
23
|
+
typeof value.version === 'string' &&
|
|
24
|
+
typeof value.category === 'string' &&
|
|
25
|
+
APP_CATEGORY_SET.has(value.category) &&
|
|
26
|
+
typeof value.themeId === 'string' &&
|
|
27
|
+
isOptionalString(value.created) &&
|
|
28
|
+
isOptionalString(value.updated)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isThemeConfig(value: unknown): boolean {
|
|
33
|
+
return (
|
|
34
|
+
isRecord(value) &&
|
|
35
|
+
typeof value.id === 'string' &&
|
|
36
|
+
typeof value.name === 'string' &&
|
|
37
|
+
isThemeModeConfig(value.light) &&
|
|
38
|
+
isThemeModeConfig(value.dark)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isNavigatorSpec(value: unknown): boolean {
|
|
43
|
+
return (
|
|
44
|
+
isRecord(value) &&
|
|
45
|
+
typeof value.type === 'string' &&
|
|
46
|
+
NAVIGATOR_TYPE_SET.has(value.type) &&
|
|
47
|
+
isOptionalString(value.initialRouteName) &&
|
|
48
|
+
Array.isArray(value.routes) &&
|
|
49
|
+
value.routes.every(isRouteDefinition) &&
|
|
50
|
+
(value.options === undefined || isRecord(value.options))
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isScreenRegistry(value: unknown): boolean {
|
|
55
|
+
return (
|
|
56
|
+
isRecord(value) &&
|
|
57
|
+
Object.entries(value).every(
|
|
58
|
+
([registryKey, screen]) =>
|
|
59
|
+
isScreenSpec(screen) && isRecord(screen) && registryKey === screen.id,
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isSplashScreenSpec(value: unknown): boolean {
|
|
65
|
+
return (
|
|
66
|
+
isSplashScreenModeSpec(value) &&
|
|
67
|
+
isRecord(value) &&
|
|
68
|
+
(value.dark === undefined || isSplashScreenModeSpec(value.dark))
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isThemeModeConfig(value: unknown): boolean {
|
|
73
|
+
return (
|
|
74
|
+
isRecord(value) &&
|
|
75
|
+
typeof value.primaryColor === 'string' &&
|
|
76
|
+
typeof value.harmony === 'string' &&
|
|
77
|
+
COLOR_HARMONY_SET.has(value.harmony)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isUiNode(value: unknown): boolean {
|
|
82
|
+
return (
|
|
83
|
+
isRecord(value) &&
|
|
84
|
+
typeof value.id === 'string' &&
|
|
85
|
+
typeof value.type === 'string' &&
|
|
86
|
+
isOptionalString(value.alias) &&
|
|
87
|
+
(value.props === undefined || isRecord(value.props)) &&
|
|
88
|
+
(value.style === undefined || isRecord(value.style)) &&
|
|
89
|
+
(value.repeat === undefined || isUiNodeRepeatSpec(value.repeat)) &&
|
|
90
|
+
(value.children === undefined ||
|
|
91
|
+
(Array.isArray(value.children) && value.children.every(isUiNode)))
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isUiNodeRepeatSpec(value: unknown): boolean {
|
|
96
|
+
return (
|
|
97
|
+
isRecord(value) &&
|
|
98
|
+
isBindingValueSource(value.source) &&
|
|
99
|
+
isOptionalString(value.itemAlias) &&
|
|
100
|
+
isOptionalString(value.keyPath) &&
|
|
101
|
+
(value.empty === undefined || (Array.isArray(value.empty) && value.empty.every(isUiNode)))
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isScreenSpec(value: unknown): boolean {
|
|
106
|
+
return (
|
|
107
|
+
isRecord(value) &&
|
|
108
|
+
typeof value.id === 'string' &&
|
|
109
|
+
typeof value.name === 'string' &&
|
|
110
|
+
isOptionalString(value.title) &&
|
|
111
|
+
isOptionalString(value.description) &&
|
|
112
|
+
(value.dataLoaders === undefined ||
|
|
113
|
+
(Array.isArray(value.dataLoaders) &&
|
|
114
|
+
value.dataLoaders.every(isScreenDataLoaderDefinition))) &&
|
|
115
|
+
(value.requires === undefined || isScreenRequirements(value.requires)) &&
|
|
116
|
+
isUiNode(value.root)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isRouteDefinition(value: unknown): boolean {
|
|
121
|
+
return (
|
|
122
|
+
isRecord(value) &&
|
|
123
|
+
typeof value.name === 'string' &&
|
|
124
|
+
isOptionalString(value.path) &&
|
|
125
|
+
isOptionalString(value.label) &&
|
|
126
|
+
(value.icon === undefined || isIconSpec(value.icon)) &&
|
|
127
|
+
isOptionalBoolean(value.showInPrimaryNavigation) &&
|
|
128
|
+
(value.guards === undefined || isStringArray(value.guards)) &&
|
|
129
|
+
isOptionalString(value.screenId) &&
|
|
130
|
+
(value.navigator === undefined || isNavigatorSpec(value.navigator))
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isIconSpec(value: unknown): boolean {
|
|
135
|
+
return (
|
|
136
|
+
isRecord(value) &&
|
|
137
|
+
typeof value.name === 'string' &&
|
|
138
|
+
isOptionalString(value.provider) &&
|
|
139
|
+
(value.size === undefined ||
|
|
140
|
+
typeof value.size === 'string' ||
|
|
141
|
+
typeof value.size === 'number') &&
|
|
142
|
+
isOptionalString(value.color)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isSplashScreenModeSpec(value: unknown): boolean {
|
|
147
|
+
return (
|
|
148
|
+
isRecord(value) &&
|
|
149
|
+
isOptionalString(value.image) &&
|
|
150
|
+
isOptionalNumber(value.imageWidth) &&
|
|
151
|
+
(value.resizeMode === undefined ||
|
|
152
|
+
(typeof value.resizeMode === 'string' &&
|
|
153
|
+
SPLASH_SCREEN_RESIZE_MODE_SET.has(value.resizeMode))) &&
|
|
154
|
+
isOptionalString(value.backgroundColor)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isScreenRequirements(value: unknown): boolean {
|
|
159
|
+
return (
|
|
160
|
+
isRecord(value) &&
|
|
161
|
+
(value.permissions === undefined || isRequirementArray(value.permissions, 'permission')) &&
|
|
162
|
+
(value.capabilities === undefined || isRequirementArray(value.capabilities, 'capability'))
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isRequirementArray(value: unknown, key: 'capability' | 'permission'): boolean {
|
|
167
|
+
return (
|
|
168
|
+
Array.isArray(value) &&
|
|
169
|
+
value.every((entry) => isRecord(entry) && typeof entry[key] === 'string')
|
|
170
|
+
);
|
|
171
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isOptionalString(value: unknown): boolean {
|
|
6
|
+
return value === undefined || typeof value === 'string';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isOptionalNumber(value: unknown): boolean {
|
|
10
|
+
return value === undefined || typeof value === 'number';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isOptionalBoolean(value: unknown): boolean {
|
|
14
|
+
return value === undefined || typeof value === 'boolean';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isStringArray(value: unknown): value is string[] {
|
|
18
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isStringRecord(value: unknown): boolean {
|
|
22
|
+
return isRecord(value) && Object.values(value).every((entry) => typeof entry === 'string');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isManifestValue(value: unknown): boolean {
|
|
26
|
+
if (
|
|
27
|
+
value === null ||
|
|
28
|
+
typeof value === 'string' ||
|
|
29
|
+
typeof value === 'number' ||
|
|
30
|
+
typeof value === 'boolean'
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return value.every(isManifestValue);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return isRecord(value) && Object.values(value).every(isManifestValue);
|
|
40
|
+
}
|