@vxrn/utils 2.0.0-beta.33.1 → 2.0.0-beta.362.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.
Files changed (73) hide show
  1. package/dist/cjs/createMessageSocket.cjs +115 -121
  2. package/dist/cjs/createMessageSocket.native.cjs +166 -0
  3. package/dist/cjs/createMessageSocket.native.js +152 -166
  4. package/dist/cjs/createMessageSocket.native.js.map +1 -1
  5. package/dist/cjs/detectPackageManager.cjs +76 -72
  6. package/dist/cjs/detectPackageManager.native.cjs +106 -0
  7. package/dist/cjs/detectPackageManager.native.js +82 -77
  8. package/dist/cjs/detectPackageManager.native.js.map +1 -1
  9. package/dist/cjs/exec.cjs +43 -45
  10. package/dist/cjs/exec.native.cjs +65 -0
  11. package/dist/cjs/exec.native.js +48 -49
  12. package/dist/cjs/exec.native.js.map +1 -1
  13. package/dist/cjs/index.cjs +10 -11
  14. package/dist/cjs/index.native.cjs +26 -0
  15. package/dist/cjs/index.native.js +10 -10
  16. package/dist/cjs/index.native.js.map +1 -1
  17. package/dist/cjs/mustReplace.cjs +27 -35
  18. package/dist/cjs/mustReplace.native.cjs +60 -0
  19. package/dist/cjs/mustReplace.native.js +44 -60
  20. package/dist/cjs/mustReplace.native.js.map +1 -1
  21. package/dist/cjs/nativeAppManifest.cjs +328 -0
  22. package/dist/cjs/nativeAppManifest.native.cjs +445 -0
  23. package/dist/cjs/nativeAppManifest.native.js +447 -0
  24. package/dist/cjs/nativeAppManifest.native.js.map +1 -0
  25. package/dist/cjs/publicEnv.cjs +38 -0
  26. package/dist/cjs/publicEnv.native.cjs +57 -0
  27. package/dist/cjs/publicEnv.native.js +59 -0
  28. package/dist/cjs/publicEnv.native.js.map +1 -0
  29. package/dist/esm/createMessageSocket.mjs +103 -103
  30. package/dist/esm/createMessageSocket.mjs.map +1 -1
  31. package/dist/esm/createMessageSocket.native.js +139 -148
  32. package/dist/esm/createMessageSocket.native.js.map +1 -1
  33. package/dist/esm/detectPackageManager.mjs +65 -55
  34. package/dist/esm/detectPackageManager.mjs.map +1 -1
  35. package/dist/esm/detectPackageManager.native.js +71 -61
  36. package/dist/esm/detectPackageManager.native.js.map +1 -1
  37. package/dist/esm/exec.mjs +29 -27
  38. package/dist/esm/exec.mjs.map +1 -1
  39. package/dist/esm/exec.native.js +34 -32
  40. package/dist/esm/exec.native.js.map +1 -1
  41. package/dist/esm/index.js +13 -7
  42. package/dist/esm/index.mjs +13 -7
  43. package/dist/esm/index.native.js +13 -7
  44. package/dist/esm/mustReplace.mjs +15 -17
  45. package/dist/esm/mustReplace.mjs.map +1 -1
  46. package/dist/esm/mustReplace.native.js +32 -43
  47. package/dist/esm/mustReplace.native.js.map +1 -1
  48. package/dist/esm/nativeAppManifest.mjs +305 -0
  49. package/dist/esm/nativeAppManifest.mjs.map +1 -0
  50. package/dist/esm/nativeAppManifest.native.js +420 -0
  51. package/dist/esm/nativeAppManifest.native.js.map +1 -0
  52. package/dist/esm/publicEnv.mjs +14 -0
  53. package/dist/esm/publicEnv.mjs.map +1 -0
  54. package/dist/esm/publicEnv.native.js +31 -0
  55. package/dist/esm/publicEnv.native.js.map +1 -0
  56. package/package.json +18 -5
  57. package/src/index.ts +1 -0
  58. package/src/nativeAppManifest.test.ts +360 -0
  59. package/src/nativeAppManifest.ts +702 -0
  60. package/src/publicEnv.ts +19 -0
  61. package/types/createMessageSocket.d.ts +1 -1
  62. package/types/createMessageSocket.d.ts.map +1 -1
  63. package/types/detectPackageManager.d.ts.map +1 -1
  64. package/types/exec.d.ts.map +1 -1
  65. package/types/index.d.ts +1 -0
  66. package/types/index.d.ts.map +1 -1
  67. package/types/nativeAppManifest.d.ts +180 -0
  68. package/types/nativeAppManifest.d.ts.map +1 -0
  69. package/types/publicEnv.d.ts +9 -0
  70. package/types/publicEnv.d.ts.map +1 -0
  71. package/dist/esm/index.js.map +0 -1
  72. package/dist/esm/index.mjs.map +0 -1
  73. package/dist/esm/index.native.js.map +0 -1
@@ -0,0 +1,360 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import {
3
+ expoClientFromNativeApp,
4
+ validateNativeApp,
5
+ type NativeAppManifest,
6
+ } from './nativeAppManifest'
7
+
8
+ const app = {
9
+ name: 'MyApp',
10
+ displayName: 'My App',
11
+ scheme: ['myapp', 'myapp-dev'],
12
+ version: '1.0.0',
13
+ imagePicker: { camera: 'Take profile photos.' },
14
+ ios: {
15
+ bundleId: 'dev.one.myapp',
16
+ buildNumber: '42',
17
+ tablet: true,
18
+ deploymentTarget: '17.0',
19
+ screensGamma: true,
20
+ useFrameworks: 'static',
21
+ ccache: true,
22
+ usesNonExemptEncryption: false,
23
+ },
24
+ android: { applicationId: 'dev.one.myapp', versionCode: 42, minSdk: 28 },
25
+ } satisfies NativeAppManifest
26
+
27
+ describe('native.app manifest', () => {
28
+ test('accepts a valid manifest', () => {
29
+ expect(() => validateNativeApp(app)).not.toThrow()
30
+ expect(() => validateNativeApp(app, 'ios')).not.toThrow()
31
+ expect(() => validateNativeApp(app, 'android')).not.toThrow()
32
+ expect(validateNativeApp(app)).toBe(app)
33
+ })
34
+
35
+ test('rejects invalid target names and schemes', () => {
36
+ expect(() => validateNativeApp({} as any)).toThrow(/name/)
37
+ expect(() => validateNativeApp({ name: 'my-app' } as any)).toThrow(/name/)
38
+ expect(() => validateNativeApp({ ...app, scheme: 'not a scheme' })).toThrow(/scheme/)
39
+ })
40
+
41
+ test('rejects bad versions, icons, and splashes', () => {
42
+ expect(() => validateNativeApp({ ...app, version: 'latest' })).toThrow(/version/)
43
+ expect(() =>
44
+ validateNativeApp({
45
+ ...app,
46
+ icon: { source: '', backgroundColor: '#000000' },
47
+ })
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/)
60
+ expect(() =>
61
+ validateNativeApp({
62
+ ...app,
63
+ splash: { source: './splash.png', backgroundColor: 'black' },
64
+ })
65
+ ).toThrow(/splash/)
66
+ expect(() =>
67
+ validateNativeApp({
68
+ ...app,
69
+ splash: { source: './splash.png', backgroundColor: '#000000', width: 0.99 },
70
+ })
71
+ ).toThrow(/splash/)
72
+ expect(() =>
73
+ validateNativeApp({
74
+ ...app,
75
+ splash: { source: './splash.png', backgroundColor: '#000000', width: 289 },
76
+ })
77
+ ).toThrow(/splash/)
78
+ })
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('requires an audio recording prompt', () => {
89
+ expect(() => validateNativeApp({ ...app, audio: { microphone: 'Record notes.' } })).not.toThrow()
90
+ expect(() => validateNativeApp({ ...app, audio: { background: true } })).not.toThrow()
91
+ expect(() => validateNativeApp({ ...app, audio: {} })).toThrow(/audio must configure/)
92
+ expect(() => validateNativeApp({ ...app, audio: { background: 'yes' } as any })).toThrow(
93
+ /audio\.background/
94
+ )
95
+ expect(() => validateNativeApp({ ...app, audio: { microphone: ' ' } })).toThrow(
96
+ /audio\.microphone/
97
+ )
98
+ })
99
+
100
+ test('requires a Photos purpose string for each configured access level', () => {
101
+ expect(() => validateNativeApp({ ...app, photoLibrary: { addOnly: 'Save edits.' } })).not.toThrow()
102
+ expect(() => validateNativeApp({ ...app, photoLibrary: { readWrite: 'Browse photos.' } })).not.toThrow()
103
+ expect(() => validateNativeApp({ ...app, photoLibrary: {} })).toThrow(
104
+ /photoLibrary must configure/
105
+ )
106
+ expect(() => validateNativeApp({ ...app, photoLibrary: null } as any)).toThrow(
107
+ /photoLibrary must configure/
108
+ )
109
+ expect(() => validateNativeApp({ ...app, photoLibrary: { addOnly: ' ' } })).toThrow(
110
+ /photoLibrary\.addOnly/
111
+ )
112
+ expect(() => validateNativeApp({ ...app, photoLibrary: { readWrite: ' ' } })).toThrow(
113
+ /photoLibrary\.readWrite/
114
+ )
115
+ })
116
+
117
+ test('requires a Contacts purpose string', () => {
118
+ expect(() => validateNativeApp({ ...app, contacts: { usage: 'Find people.' } })).not.toThrow()
119
+ expect(() => validateNativeApp({ ...app, contacts: null } as any)).toThrow(/contacts\.usage/)
120
+ expect(() => validateNativeApp({ ...app, contacts: { usage: ' ' } })).toThrow(
121
+ /contacts\.usage/
122
+ )
123
+ })
124
+
125
+ test('requires a Calendar purpose string', () => {
126
+ expect(() => validateNativeApp({ ...app, calendar: { usage: 'Show events.' } })).not.toThrow()
127
+ expect(() => validateNativeApp({ ...app, calendar: { remindersUsage: 'Manage tasks.' } })).not.toThrow()
128
+ expect(() => validateNativeApp({ ...app, calendar: {} })).toThrow(/calendar\.usage/)
129
+ expect(() => validateNativeApp({ ...app, calendar: null } as any)).toThrow(/calendar\.usage/)
130
+ expect(() => validateNativeApp({ ...app, calendar: { usage: ' ' } })).toThrow(/calendar\.usage/)
131
+ expect(() => validateNativeApp({ ...app, calendar: { remindersUsage: ' ' } })).toThrow(
132
+ /calendar\.remindersUsage/
133
+ )
134
+ })
135
+
136
+ test('accepts a camera permission string and rejects empty ones', () => {
137
+ expect(() => validateNativeApp({ ...app, imagePicker: undefined })).not.toThrow()
138
+ expect(() => validateNativeApp({ ...app, imagePicker: { camera: '' } })).toThrow(
139
+ /imagePicker\.camera/
140
+ )
141
+ expect(() => validateNativeApp({ ...app, imagePicker: { camera: ' ' } })).toThrow(
142
+ /imagePicker\.camera/
143
+ )
144
+ })
145
+
146
+ test('requires a non-empty foreground location usage string', () => {
147
+ expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.' } })).not.toThrow()
148
+ expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.', background: true } })).not.toThrow()
149
+ expect(() => validateNativeApp({ ...app, location: { whenInUse: 'Find me.', background: 'yes' } } as any)).toThrow(/location\.background/)
150
+ expect(() => validateNativeApp({ ...app, location: null } as any)).toThrow(/location\.whenInUse/)
151
+ expect(() => validateNativeApp({ ...app, location: { whenInUse: ' ' } })).toThrow(
152
+ /location\.whenInUse/
153
+ )
154
+ })
155
+
156
+ test('validates the iOS widget target and App Group before prebuild', () => {
157
+ const widgets = {
158
+ appGroup: 'group.dev.one.myapp',
159
+ kind: 'MyAppStatus',
160
+ displayName: 'Status',
161
+ description: 'Current status',
162
+ }
163
+ expect(() =>
164
+ validateNativeApp({ ...app, ios: { ...app.ios, widgets } })
165
+ ).not.toThrow()
166
+ expect(() =>
167
+ validateNativeApp({
168
+ ...app,
169
+ ios: { ...app.ios, widgets: { ...widgets, pushNotifications: 'yes' as any } },
170
+ })
171
+ ).toThrow(/pushNotifications/)
172
+ expect(() =>
173
+ validateNativeApp({
174
+ ...app,
175
+ ios: { ...app.ios, deploymentTarget: '16.4', widgets },
176
+ })
177
+ ).toThrow(/deploymentTarget/)
178
+ expect(() =>
179
+ validateNativeApp({
180
+ ...app,
181
+ ios: { ...app.ios, widgets: { ...widgets, appGroup: 'dev.one.myapp' } },
182
+ })
183
+ ).toThrow(/appGroup/)
184
+ })
185
+
186
+ test('validates static App Intent identifiers and shortcut phrases', () => {
187
+ const first = {
188
+ id: 'dev.one.myapp.echo',
189
+ title: 'Echo text',
190
+ textParameterTitle: 'Text',
191
+ shortcutPhrase: 'Echo text in {app}',
192
+ }
193
+ const withActions = (actions: Array<typeof first>) =>
194
+ ({ ...app, ios: { ...app.ios, appIntents: { actions } } })
195
+ expect(() => validateNativeApp(withActions([first]))).not.toThrow()
196
+ expect(() => validateNativeApp(withActions([first, { ...first }]))).toThrow(/duplicated/)
197
+ expect(() => validateNativeApp(withActions([{ ...first, shortcutPhrase: 'Echo text' }]))).toThrow(/shortcu\w*Phrase/)
198
+ expect(() => validateNativeApp(withActions([{ ...first, shortcutPhrase: '{app} in {app}' }]))).toThrow(/shortcu\w*Phrase/)
199
+ expect(() => validateNativeApp(withActions([{ ...first, textParameterTitle: ' ' }]))).toThrow(/textParameterTitle/)
200
+ })
201
+
202
+ test('rejects missing platform ids and out-of-range platform values', () => {
203
+ expect(() => validateNativeApp({ name: 'MyApp' } as any)).toThrow(/bundleId/)
204
+ expect(() =>
205
+ validateNativeApp({ name: 'MyApp', android: app.android } as any)
206
+ ).toThrow(/bundleId/)
207
+ expect(() =>
208
+ validateNativeApp({
209
+ name: 'MyApp',
210
+ ios: { bundleId: 'not-an-id' },
211
+ android: app.android,
212
+ } as any)
213
+ ).toThrow(/bundleId/)
214
+ expect(() => validateNativeApp({ name: 'MyApp', ios: app.ios } as any)).toThrow(
215
+ /applicationId/
216
+ )
217
+ expect(() =>
218
+ validateNativeApp({
219
+ name: 'MyApp',
220
+ ios: app.ios,
221
+ android: { applicationId: '' },
222
+ } as any)
223
+ ).toThrow(/applicationId/)
224
+ expect(() =>
225
+ validateNativeApp({
226
+ ...app,
227
+ ios: { bundleId: 'dev.one.myapp', deploymentTarget: 'latest' },
228
+ } as any)
229
+ ).toThrow(/deploymentTarget/)
230
+ expect(() =>
231
+ validateNativeApp({ ...app, android: { ...app.android, minSdk: 20 } } as any)
232
+ ).toThrow(/minSdk/)
233
+ expect(() =>
234
+ validateNativeApp({
235
+ ...app,
236
+ ios: { bundleId: 'dev.one.myapp', buildNumber: '1 2' },
237
+ })
238
+ ).toThrow(/buildNumber/)
239
+ expect(() =>
240
+ validateNativeApp({
241
+ ...app,
242
+ ios: { bundleId: 'dev.one.myapp', buildNumber: '' },
243
+ })
244
+ ).toThrow(/buildNumber/)
245
+ expect(() =>
246
+ validateNativeApp({
247
+ ...app,
248
+ android: { applicationId: 'dev.one.myapp', versionCode: 0 },
249
+ })
250
+ ).toThrow(/versionCode/)
251
+ expect(() =>
252
+ validateNativeApp({
253
+ ...app,
254
+ android: { applicationId: 'dev.one.myapp', versionCode: 1.5 },
255
+ })
256
+ ).toThrow(/versionCode/)
257
+ })
258
+
259
+ test('accepts notifications and rejects a non-boolean push', () => {
260
+ expect(() => validateNativeApp({ ...app, notifications: undefined })).not.toThrow()
261
+ expect(() =>
262
+ validateNativeApp({ ...app, notifications: { push: true } })
263
+ ).not.toThrow()
264
+ expect(() =>
265
+ validateNativeApp({ ...app, notifications: { push: 'yes' } } as any)
266
+ ).toThrow(/notifications\.push/)
267
+ expect(() => validateNativeApp({ ...app, notifications: true } as any)).toThrow(
268
+ /notifications\.push/
269
+ )
270
+ })
271
+
272
+ test('accepts a maps key and rejects empty ones', () => {
273
+ expect(() =>
274
+ validateNativeApp({
275
+ ...app,
276
+ android: { ...app.android, googleMapsApiKey: 'AIza-test' },
277
+ })
278
+ ).not.toThrow()
279
+ expect(() =>
280
+ validateNativeApp({
281
+ ...app,
282
+ android: { ...app.android, googleMapsApiKey: '' },
283
+ })
284
+ ).toThrow(/googleMapsApiKey/)
285
+ expect(() =>
286
+ validateNativeApp({
287
+ ...app,
288
+ android: { ...app.android, googleMapsApiKey: ' ' },
289
+ })
290
+ ).toThrow(/googleMapsApiKey/)
291
+ })
292
+
293
+ test('accepts updates with a runtime version and an optional url', () => {
294
+ expect(() => validateNativeApp({ ...app, updates: undefined })).not.toThrow()
295
+ expect(() =>
296
+ validateNativeApp({
297
+ ...app,
298
+ updates: { url: 'https://updates.example.com', runtimeVersion: 'test-1' },
299
+ })
300
+ ).not.toThrow()
301
+ expect(() =>
302
+ validateNativeApp({ ...app, updates: { runtimeVersion: 'test-1' } })
303
+ ).not.toThrow()
304
+ expect(() => validateNativeApp({ ...app, updates: {} } as any)).toThrow(
305
+ /updates\.runtimeVersion/
306
+ )
307
+ expect(() =>
308
+ validateNativeApp({ ...app, updates: { runtimeVersion: ' ' } })
309
+ ).toThrow(/updates\.runtimeVersion/)
310
+ expect(() =>
311
+ validateNativeApp({ ...app, updates: { url: '', runtimeVersion: 'test-1' } })
312
+ ).toThrow(/updates\.url/)
313
+ })
314
+
315
+ // prebuild writes no UIUserInterfaceStyle for an unset style, so the built
316
+ // app follows the system; an expo client reads a missing style as light.
317
+ test('the dev manifest states the interface style the built app has', () => {
318
+ expect(expoClientFromNativeApp(app).userInterfaceStyle).toBe('automatic')
319
+ expect(
320
+ expoClientFromNativeApp({ ...app, userInterfaceStyle: 'dark' }).userInterfaceStyle
321
+ ).toBe('dark')
322
+ })
323
+
324
+ test('the dev manifest carries both native launch background images', () => {
325
+ const client = expoClientFromNativeApp({
326
+ ...app,
327
+ splash: {
328
+ source: './assets/mark.png',
329
+ backgroundImage: './assets/launch-light.png',
330
+ backgroundColor: '#ffffff',
331
+ dark: {
332
+ source: './assets/mark-dark.png',
333
+ backgroundImage: './assets/launch-dark.png',
334
+ backgroundColor: '#000000',
335
+ },
336
+ },
337
+ })
338
+ expect(client.splash).toEqual({
339
+ image: './assets/mark.png',
340
+ backgroundImage: './assets/launch-light.png',
341
+ backgroundColor: '#ffffff',
342
+ imageWidth: undefined,
343
+ resizeMode: undefined,
344
+ dark: {
345
+ image: './assets/mark-dark.png',
346
+ backgroundImage: './assets/launch-dark.png',
347
+ backgroundColor: '#000000',
348
+ },
349
+ })
350
+ })
351
+
352
+ test('platform scope skips the other platform requirement', () => {
353
+ expect(() =>
354
+ validateNativeApp({ name: 'MyApp', android: app.android } as any, 'android')
355
+ ).not.toThrow()
356
+ expect(() =>
357
+ validateNativeApp({ name: 'MyApp', ios: app.ios } as any, 'ios')
358
+ ).not.toThrow()
359
+ })
360
+ })