@vxrn/utils 2.0.0-beta.99.1 → 2.6.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/dist/cjs/createMessageSocket.native.cjs +1 -1
- package/dist/cjs/createMessageSocket.native.js +1 -1
- package/dist/cjs/createMessageSocket.native.js.map +1 -1
- package/dist/cjs/detectPackageManager.cjs +3 -2
- package/dist/cjs/detectPackageManager.native.cjs +5 -4
- package/dist/cjs/detectPackageManager.native.js +5 -4
- package/dist/cjs/detectPackageManager.native.js.map +1 -1
- package/dist/cjs/exec.native.cjs +3 -3
- package/dist/cjs/exec.native.js +3 -3
- package/dist/cjs/exec.native.js.map +1 -1
- package/dist/cjs/nativeAppManifest.cjs +231 -1
- package/dist/cjs/nativeAppManifest.native.cjs +328 -6
- package/dist/cjs/nativeAppManifest.native.js +328 -6
- package/dist/cjs/nativeAppManifest.native.js.map +1 -1
- package/dist/esm/createMessageSocket.native.js +1 -1
- package/dist/esm/createMessageSocket.native.js.map +1 -1
- package/dist/esm/detectPackageManager.mjs +3 -2
- package/dist/esm/detectPackageManager.mjs.map +1 -1
- package/dist/esm/detectPackageManager.native.js +5 -4
- package/dist/esm/detectPackageManager.native.js.map +1 -1
- package/dist/esm/exec.native.js +3 -3
- package/dist/esm/exec.native.js.map +1 -1
- package/dist/esm/nativeAppManifest.mjs +228 -1
- package/dist/esm/nativeAppManifest.mjs.map +1 -1
- package/dist/esm/nativeAppManifest.native.js +325 -6
- package/dist/esm/nativeAppManifest.native.js.map +1 -1
- package/package.json +5 -4
- package/src/createMessageSocket.ts +1 -1
- package/src/detectPackageManager.ts +3 -2
- package/src/nativeAppManifest.test.ts +180 -1
- package/src/nativeAppManifest.ts +517 -1
- package/types/detectPackageManager.d.ts.map +1 -1
- package/types/nativeAppManifest.d.ts +135 -1
- package/types/nativeAppManifest.d.ts.map +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
expoClientFromNativeApp,
|
|
4
|
+
validateNativeApp,
|
|
5
|
+
type NativeAppManifest,
|
|
6
|
+
} from './nativeAppManifest'
|
|
3
7
|
|
|
4
8
|
const app = {
|
|
5
9
|
name: 'MyApp',
|
|
@@ -42,6 +46,17 @@ describe('native.app manifest', () => {
|
|
|
42
46
|
icon: { source: '', backgroundColor: '#000000' },
|
|
43
47
|
})
|
|
44
48
|
).toThrow(/icon/)
|
|
49
|
+
expect(() => validateNativeApp({
|
|
50
|
+
...app, ios: { ...app.ios, alternateIcons: {
|
|
51
|
+
TestAlternate: { source: 'alternate.svg', backgroundColor: '#123456' },
|
|
52
|
+
} },
|
|
53
|
+
})).toThrow(/requires a primary icon/)
|
|
54
|
+
expect(() => validateNativeApp({
|
|
55
|
+
...app, icon: { source: 'primary.svg', backgroundColor: '#123456' },
|
|
56
|
+
ios: { ...app.ios, alternateIcons: {
|
|
57
|
+
AppIcon: { source: 'alternate.svg', backgroundColor: '#123456' },
|
|
58
|
+
} },
|
|
59
|
+
})).toThrow(/other than AppIcon/)
|
|
45
60
|
expect(() =>
|
|
46
61
|
validateNativeApp({
|
|
47
62
|
...app,
|
|
@@ -62,6 +77,76 @@ describe('native.app manifest', () => {
|
|
|
62
77
|
).toThrow(/splash/)
|
|
63
78
|
})
|
|
64
79
|
|
|
80
|
+
test('requires both speech usage strings', () => {
|
|
81
|
+
const speech = { recognition: 'Dictate messages.', microphone: 'Dictate messages.' }
|
|
82
|
+
expect(() => validateNativeApp({ ...app, speech })).not.toThrow()
|
|
83
|
+
expect(() => validateNativeApp({ ...app, speech: { ...speech, microphone: ' ' } })).toThrow(
|
|
84
|
+
/speech\.recognition and speech\.microphone/
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('accepts the brightness system flag', () => {
|
|
89
|
+
expect(() => validateNativeApp({ ...app, brightness: true })).not.toThrow()
|
|
90
|
+
expect(() => validateNativeApp({ ...app, brightness: 'yes' as any })).toThrow(/brightness/)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('requires a pedometer motion prompt', () => {
|
|
94
|
+
expect(() =>
|
|
95
|
+
validateNativeApp({ ...app, pedometer: { usage: 'Count your steps.' } })
|
|
96
|
+
).not.toThrow()
|
|
97
|
+
expect(() => validateNativeApp({ ...app, pedometer: { usage: ' ' } })).toThrow(
|
|
98
|
+
/pedometer\.usage/
|
|
99
|
+
)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('requires an audio recording prompt', () => {
|
|
103
|
+
expect(() => validateNativeApp({ ...app, audio: { microphone: 'Record notes.' } })).not.toThrow()
|
|
104
|
+
expect(() => validateNativeApp({ ...app, audio: { background: true } })).not.toThrow()
|
|
105
|
+
expect(() => validateNativeApp({ ...app, audio: {} })).toThrow(/audio must configure/)
|
|
106
|
+
expect(() => validateNativeApp({ ...app, audio: { background: 'yes' } as any })).toThrow(
|
|
107
|
+
/audio\.background/
|
|
108
|
+
)
|
|
109
|
+
expect(() => validateNativeApp({ ...app, audio: { microphone: ' ' } })).toThrow(
|
|
110
|
+
/audio\.microphone/
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('requires a Photos purpose string for each configured access level', () => {
|
|
115
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: { addOnly: 'Save edits.' } })).not.toThrow()
|
|
116
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: { readWrite: 'Browse photos.' } })).not.toThrow()
|
|
117
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: {} })).toThrow(
|
|
118
|
+
/photoLibrary must configure/
|
|
119
|
+
)
|
|
120
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: null } as any)).toThrow(
|
|
121
|
+
/photoLibrary must configure/
|
|
122
|
+
)
|
|
123
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: { addOnly: ' ' } })).toThrow(
|
|
124
|
+
/photoLibrary\.addOnly/
|
|
125
|
+
)
|
|
126
|
+
expect(() => validateNativeApp({ ...app, photoLibrary: { readWrite: ' ' } })).toThrow(
|
|
127
|
+
/photoLibrary\.readWrite/
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('requires a Contacts purpose string', () => {
|
|
132
|
+
expect(() => validateNativeApp({ ...app, contacts: { usage: 'Find people.' } })).not.toThrow()
|
|
133
|
+
expect(() => validateNativeApp({ ...app, contacts: null } as any)).toThrow(/contacts\.usage/)
|
|
134
|
+
expect(() => validateNativeApp({ ...app, contacts: { usage: ' ' } })).toThrow(
|
|
135
|
+
/contacts\.usage/
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('requires a Calendar purpose string', () => {
|
|
140
|
+
expect(() => validateNativeApp({ ...app, calendar: { usage: 'Show events.' } })).not.toThrow()
|
|
141
|
+
expect(() => validateNativeApp({ ...app, calendar: { remindersUsage: 'Manage tasks.' } })).not.toThrow()
|
|
142
|
+
expect(() => validateNativeApp({ ...app, calendar: {} })).toThrow(/calendar\.usage/)
|
|
143
|
+
expect(() => validateNativeApp({ ...app, calendar: null } as any)).toThrow(/calendar\.usage/)
|
|
144
|
+
expect(() => validateNativeApp({ ...app, calendar: { usage: ' ' } })).toThrow(/calendar\.usage/)
|
|
145
|
+
expect(() => validateNativeApp({ ...app, calendar: { remindersUsage: ' ' } })).toThrow(
|
|
146
|
+
/calendar\.remindersUsage/
|
|
147
|
+
)
|
|
148
|
+
})
|
|
149
|
+
|
|
65
150
|
test('accepts a camera permission string and rejects empty ones', () => {
|
|
66
151
|
expect(() => validateNativeApp({ ...app, imagePicker: undefined })).not.toThrow()
|
|
67
152
|
expect(() => validateNativeApp({ ...app, imagePicker: { camera: '' } })).toThrow(
|
|
@@ -72,6 +157,25 @@ describe('native.app manifest', () => {
|
|
|
72
157
|
)
|
|
73
158
|
})
|
|
74
159
|
|
|
160
|
+
test('requires a non-empty foreground location usage string', () => {
|
|
161
|
+
expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.' } })).not.toThrow()
|
|
162
|
+
expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.', background: true } })).not.toThrow()
|
|
163
|
+
expect(() =>
|
|
164
|
+
validateNativeApp({
|
|
165
|
+
...app,
|
|
166
|
+
location: { whenInUse: 'Find me.', alwaysAndWhenInUse: 'Track regions.' },
|
|
167
|
+
})
|
|
168
|
+
).not.toThrow()
|
|
169
|
+
expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.', background: 'yes' } } as any)).toThrow(/location\.background/)
|
|
170
|
+
expect(() =>
|
|
171
|
+
validateNativeApp({ ...app, location: { whenInUse: 'Find me.', alwaysAndWhenInUse: ' ' } })
|
|
172
|
+
).toThrow(/location\.alwaysAndWhenInUse/)
|
|
173
|
+
expect(() => validateNativeApp({ ...app, location: null } as any)).toThrow(/location\.whenInUse/)
|
|
174
|
+
expect(() => validateNativeApp({ ...app, location: { whenInUse: ' ' } })).toThrow(
|
|
175
|
+
/location\.whenInUse/
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
|
|
75
179
|
test('validates the iOS widget target and App Group before prebuild', () => {
|
|
76
180
|
const widgets = {
|
|
77
181
|
appGroup: 'group.dev.one.myapp',
|
|
@@ -102,6 +206,22 @@ describe('native.app manifest', () => {
|
|
|
102
206
|
).toThrow(/appGroup/)
|
|
103
207
|
})
|
|
104
208
|
|
|
209
|
+
test('validates static App Intent identifiers and shortcut phrases', () => {
|
|
210
|
+
const first = {
|
|
211
|
+
id: 'dev.one.myapp.echo',
|
|
212
|
+
title: 'Echo text',
|
|
213
|
+
textParameterTitle: 'Text',
|
|
214
|
+
shortcutPhrase: 'Echo text in {app}',
|
|
215
|
+
}
|
|
216
|
+
const withActions = (actions: Array<typeof first>) =>
|
|
217
|
+
({ ...app, ios: { ...app.ios, appIntents: { actions } } })
|
|
218
|
+
expect(() => validateNativeApp(withActions([first]))).not.toThrow()
|
|
219
|
+
expect(() => validateNativeApp(withActions([first, { ...first }]))).toThrow(/duplicated/)
|
|
220
|
+
expect(() => validateNativeApp(withActions([{ ...first, shortcutPhrase: 'Echo text' }]))).toThrow(/shortcu\w*Phrase/)
|
|
221
|
+
expect(() => validateNativeApp(withActions([{ ...first, shortcutPhrase: '{app} in {app}' }]))).toThrow(/shortcu\w*Phrase/)
|
|
222
|
+
expect(() => validateNativeApp(withActions([{ ...first, textParameterTitle: ' ' }]))).toThrow(/textParameterTitle/)
|
|
223
|
+
})
|
|
224
|
+
|
|
105
225
|
test('rejects missing platform ids and out-of-range platform values', () => {
|
|
106
226
|
expect(() => validateNativeApp({ name: 'MyApp' } as any)).toThrow(/bundleId/)
|
|
107
227
|
expect(() =>
|
|
@@ -193,6 +313,65 @@ describe('native.app manifest', () => {
|
|
|
193
313
|
).toThrow(/googleMapsApiKey/)
|
|
194
314
|
})
|
|
195
315
|
|
|
316
|
+
test('accepts updates with a runtime version and an optional url', () => {
|
|
317
|
+
expect(() => validateNativeApp({ ...app, updates: undefined })).not.toThrow()
|
|
318
|
+
expect(() =>
|
|
319
|
+
validateNativeApp({
|
|
320
|
+
...app,
|
|
321
|
+
updates: { url: 'https://updates.example.com', runtimeVersion: 'test-1' },
|
|
322
|
+
})
|
|
323
|
+
).not.toThrow()
|
|
324
|
+
expect(() =>
|
|
325
|
+
validateNativeApp({ ...app, updates: { runtimeVersion: 'test-1' } })
|
|
326
|
+
).not.toThrow()
|
|
327
|
+
expect(() => validateNativeApp({ ...app, updates: {} } as any)).toThrow(
|
|
328
|
+
/updates\.runtimeVersion/
|
|
329
|
+
)
|
|
330
|
+
expect(() =>
|
|
331
|
+
validateNativeApp({ ...app, updates: { runtimeVersion: ' ' } })
|
|
332
|
+
).toThrow(/updates\.runtimeVersion/)
|
|
333
|
+
expect(() =>
|
|
334
|
+
validateNativeApp({ ...app, updates: { url: '', runtimeVersion: 'test-1' } })
|
|
335
|
+
).toThrow(/updates\.url/)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// prebuild writes no UIUserInterfaceStyle for an unset style, so the built
|
|
339
|
+
// app follows the system; an expo client reads a missing style as light.
|
|
340
|
+
test('the dev manifest states the interface style the built app has', () => {
|
|
341
|
+
expect(expoClientFromNativeApp(app).userInterfaceStyle).toBe('automatic')
|
|
342
|
+
expect(
|
|
343
|
+
expoClientFromNativeApp({ ...app, userInterfaceStyle: 'dark' }).userInterfaceStyle
|
|
344
|
+
).toBe('dark')
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
test('the dev manifest carries both native launch background images', () => {
|
|
348
|
+
const client = expoClientFromNativeApp({
|
|
349
|
+
...app,
|
|
350
|
+
splash: {
|
|
351
|
+
source: './assets/mark.png',
|
|
352
|
+
backgroundImage: './assets/launch-light.png',
|
|
353
|
+
backgroundColor: '#ffffff',
|
|
354
|
+
dark: {
|
|
355
|
+
source: './assets/mark-dark.png',
|
|
356
|
+
backgroundImage: './assets/launch-dark.png',
|
|
357
|
+
backgroundColor: '#000000',
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
})
|
|
361
|
+
expect(client.splash).toEqual({
|
|
362
|
+
image: './assets/mark.png',
|
|
363
|
+
backgroundImage: './assets/launch-light.png',
|
|
364
|
+
backgroundColor: '#ffffff',
|
|
365
|
+
imageWidth: undefined,
|
|
366
|
+
resizeMode: undefined,
|
|
367
|
+
dark: {
|
|
368
|
+
image: './assets/mark-dark.png',
|
|
369
|
+
backgroundImage: './assets/launch-dark.png',
|
|
370
|
+
backgroundColor: '#000000',
|
|
371
|
+
},
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
196
375
|
test('platform scope skips the other platform requirement', () => {
|
|
197
376
|
expect(() =>
|
|
198
377
|
validateNativeApp({ name: 'MyApp', android: app.android } as any, 'android')
|