@vxrn/utils 2.0.0-beta.98.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.
Files changed (34) hide show
  1. package/dist/cjs/createMessageSocket.native.cjs +1 -1
  2. package/dist/cjs/createMessageSocket.native.js +1 -1
  3. package/dist/cjs/createMessageSocket.native.js.map +1 -1
  4. package/dist/cjs/detectPackageManager.cjs +3 -2
  5. package/dist/cjs/detectPackageManager.native.cjs +5 -4
  6. package/dist/cjs/detectPackageManager.native.js +5 -4
  7. package/dist/cjs/detectPackageManager.native.js.map +1 -1
  8. package/dist/cjs/exec.native.cjs +3 -3
  9. package/dist/cjs/exec.native.js +3 -3
  10. package/dist/cjs/exec.native.js.map +1 -1
  11. package/dist/cjs/nativeAppManifest.cjs +234 -1
  12. package/dist/cjs/nativeAppManifest.native.cjs +331 -6
  13. package/dist/cjs/nativeAppManifest.native.js +331 -6
  14. package/dist/cjs/nativeAppManifest.native.js.map +1 -1
  15. package/dist/esm/createMessageSocket.native.js +1 -1
  16. package/dist/esm/createMessageSocket.native.js.map +1 -1
  17. package/dist/esm/detectPackageManager.mjs +3 -2
  18. package/dist/esm/detectPackageManager.mjs.map +1 -1
  19. package/dist/esm/detectPackageManager.native.js +5 -4
  20. package/dist/esm/detectPackageManager.native.js.map +1 -1
  21. package/dist/esm/exec.native.js +3 -3
  22. package/dist/esm/exec.native.js.map +1 -1
  23. package/dist/esm/nativeAppManifest.mjs +231 -1
  24. package/dist/esm/nativeAppManifest.mjs.map +1 -1
  25. package/dist/esm/nativeAppManifest.native.js +328 -6
  26. package/dist/esm/nativeAppManifest.native.js.map +1 -1
  27. package/package.json +5 -4
  28. package/src/createMessageSocket.ts +1 -1
  29. package/src/detectPackageManager.ts +3 -2
  30. package/src/nativeAppManifest.test.ts +180 -1
  31. package/src/nativeAppManifest.ts +526 -1
  32. package/types/detectPackageManager.d.ts.map +1 -1
  33. package/types/nativeAppManifest.d.ts +136 -1
  34. package/types/nativeAppManifest.d.ts.map +1 -1
@@ -1,5 +1,9 @@
1
1
  import { describe, expect, test } from 'vitest'
2
- import { validateNativeApp, type NativeAppManifest } from './nativeAppManifest'
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')