@ng-native/expo 0.0.1 → 0.1.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 (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -1
  3. package/dist/app-info.d.ts +58 -0
  4. package/dist/app-info.js +71 -0
  5. package/dist/app-info.js.map +1 -0
  6. package/dist/apple-sign-in-button.d.ts +16 -0
  7. package/dist/apple-sign-in-button.js +55 -0
  8. package/dist/apple-sign-in-button.js.map +1 -0
  9. package/dist/apple-sign-in.d.ts +68 -0
  10. package/dist/apple-sign-in.js +120 -0
  11. package/dist/apple-sign-in.js.map +1 -0
  12. package/dist/assets.d.ts +24 -0
  13. package/dist/assets.js +47 -0
  14. package/dist/assets.js.map +1 -0
  15. package/dist/background-task.d.ts +66 -0
  16. package/dist/background-task.js +82 -0
  17. package/dist/background-task.js.map +1 -0
  18. package/dist/battery.d.ts +46 -0
  19. package/dist/battery.js +76 -0
  20. package/dist/battery.js.map +1 -0
  21. package/dist/biometrics.d.ts +45 -0
  22. package/dist/biometrics.js +53 -0
  23. package/dist/biometrics.js.map +1 -0
  24. package/dist/brightness.d.ts +38 -0
  25. package/dist/brightness.js +81 -0
  26. package/dist/brightness.js.map +1 -0
  27. package/dist/browser.d.ts +42 -0
  28. package/dist/browser.js +42 -0
  29. package/dist/browser.js.map +1 -0
  30. package/dist/camera.d.ts +48 -0
  31. package/dist/camera.js +59 -0
  32. package/dist/camera.js.map +1 -0
  33. package/dist/clipboard.d.ts +43 -0
  34. package/dist/clipboard.js +60 -0
  35. package/dist/clipboard.js.map +1 -0
  36. package/dist/community-views.d.ts +14 -0
  37. package/dist/community-views.js +76 -0
  38. package/dist/community-views.js.map +1 -0
  39. package/dist/crypto.d.ts +56 -0
  40. package/dist/crypto.js +84 -0
  41. package/dist/crypto.js.map +1 -0
  42. package/dist/database.d.ts +47 -0
  43. package/dist/database.js +108 -0
  44. package/dist/database.js.map +1 -0
  45. package/dist/document-picker.d.ts +34 -0
  46. package/dist/document-picker.js +40 -0
  47. package/dist/document-picker.js.map +1 -0
  48. package/dist/dom-component.d.ts +87 -0
  49. package/dist/dom-component.js +139 -0
  50. package/dist/dom-component.js.map +1 -0
  51. package/dist/expo-image.d.ts +33 -0
  52. package/dist/expo-image.js +57 -0
  53. package/dist/expo-image.js.map +1 -0
  54. package/dist/expo-ui-components.d.ts +327 -0
  55. package/dist/expo-ui-components.js +707 -0
  56. package/dist/expo-ui-components.js.map +1 -0
  57. package/dist/expo-ui.d.ts +46 -0
  58. package/dist/expo-ui.js +128 -0
  59. package/dist/expo-ui.js.map +1 -0
  60. package/dist/file-system.d.ts +63 -0
  61. package/dist/file-system.js +67 -0
  62. package/dist/file-system.js.map +1 -0
  63. package/dist/fonts.d.ts +90 -0
  64. package/dist/fonts.js +118 -0
  65. package/dist/fonts.js.map +1 -0
  66. package/dist/glass.d.ts +33 -0
  67. package/dist/glass.js +72 -0
  68. package/dist/glass.js.map +1 -0
  69. package/dist/haptics.d.ts +49 -0
  70. package/dist/haptics.js +64 -0
  71. package/dist/haptics.js.map +1 -0
  72. package/dist/image-editor.d.ts +62 -0
  73. package/dist/image-editor.js +101 -0
  74. package/dist/image-editor.js.map +1 -0
  75. package/dist/image-picker.d.ts +49 -0
  76. package/dist/image-picker.js +58 -0
  77. package/dist/image-picker.js.map +1 -0
  78. package/dist/index.d.ts +20 -0
  79. package/dist/index.js +21 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/keep-awake.d.ts +39 -0
  82. package/dist/keep-awake.js +80 -0
  83. package/dist/keep-awake.js.map +1 -0
  84. package/dist/language-model.d.ts +129 -0
  85. package/dist/language-model.js +225 -0
  86. package/dist/language-model.js.map +1 -0
  87. package/dist/locale.d.ts +51 -0
  88. package/dist/locale.js +78 -0
  89. package/dist/locale.js.map +1 -0
  90. package/dist/location.d.ts +68 -0
  91. package/dist/location.js +83 -0
  92. package/dist/location.js.map +1 -0
  93. package/dist/map-view.d.ts +203 -0
  94. package/dist/map-view.js +184 -0
  95. package/dist/map-view.js.map +1 -0
  96. package/dist/media-library.d.ts +103 -0
  97. package/dist/media-library.js +160 -0
  98. package/dist/media-library.js.map +1 -0
  99. package/dist/native-state.d.ts +22 -0
  100. package/dist/native-state.js +43 -0
  101. package/dist/native-state.js.map +1 -0
  102. package/dist/native.d.ts +42 -0
  103. package/dist/native.js +73 -0
  104. package/dist/native.js.map +1 -0
  105. package/dist/network.d.ts +35 -0
  106. package/dist/network.js +60 -0
  107. package/dist/network.js.map +1 -0
  108. package/dist/notifications.d.ts +175 -0
  109. package/dist/notifications.js +296 -0
  110. package/dist/notifications.js.map +1 -0
  111. package/dist/observed.d.ts +37 -0
  112. package/dist/observed.js +55 -0
  113. package/dist/observed.js.map +1 -0
  114. package/dist/orientation.d.ts +35 -0
  115. package/dist/orientation.js +78 -0
  116. package/dist/orientation.js.map +1 -0
  117. package/dist/permissions.d.ts +54 -0
  118. package/dist/permissions.js +72 -0
  119. package/dist/permissions.js.map +1 -0
  120. package/dist/player.d.ts +92 -0
  121. package/dist/player.js +141 -0
  122. package/dist/player.js.map +1 -0
  123. package/dist/register-expo-view.d.ts +55 -0
  124. package/dist/register-expo-view.js +110 -0
  125. package/dist/register-expo-view.js.map +1 -0
  126. package/dist/screen-capture.d.ts +58 -0
  127. package/dist/screen-capture.js +78 -0
  128. package/dist/screen-capture.js.map +1 -0
  129. package/dist/segmented-control.d.ts +32 -0
  130. package/dist/segmented-control.js +55 -0
  131. package/dist/segmented-control.js.map +1 -0
  132. package/dist/sensors.d.ts +85 -0
  133. package/dist/sensors.js +115 -0
  134. package/dist/sensors.js.map +1 -0
  135. package/dist/splash-screen.d.ts +73 -0
  136. package/dist/splash-screen.js +115 -0
  137. package/dist/splash-screen.js.map +1 -0
  138. package/dist/store-review.d.ts +43 -0
  139. package/dist/store-review.js +52 -0
  140. package/dist/store-review.js.map +1 -0
  141. package/dist/store.d.ts +108 -0
  142. package/dist/store.js +247 -0
  143. package/dist/store.js.map +1 -0
  144. package/dist/symbol.d.ts +36 -0
  145. package/dist/symbol.js +69 -0
  146. package/dist/symbol.js.map +1 -0
  147. package/dist/tracking.d.ts +45 -0
  148. package/dist/tracking.js +54 -0
  149. package/dist/tracking.js.map +1 -0
  150. package/dist/transforms.d.ts +2 -0
  151. package/dist/transforms.js +13 -0
  152. package/dist/transforms.js.map +1 -0
  153. package/dist/updates.d.ts +61 -0
  154. package/dist/updates.js +97 -0
  155. package/dist/updates.js.map +1 -0
  156. package/package.json +397 -3
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Native views from the libraries an Expo app already reaches for, by element name.
3
+ *
4
+ * These are not Expo modules, so their Fabric names are their own - what codegen produced from
5
+ * each library's spec - rather than the `ViewManagerAdapter_` names `registerExpoView` derives.
6
+ * They are here because they are in Expo's own bundled module list, which is the list an app
7
+ * actually installs from, and because the alternative is every app looking each name up in a
8
+ * library's generated code.
9
+ *
10
+ * Names and defaults, not components. An Angular component per native view, prop for prop, is a
11
+ * maintenance burden with no end and a second place for every prop to be wrong. An app writes the
12
+ * small component for the one it uses - a selector, and an input per prop it binds, passed through
13
+ * as a host binding - which keeps its template checked; see `ExpoImage` for the shape.
14
+ *
15
+ * A library still has to be installed for its element to commit as anything. This is the
16
+ * JavaScript half; an element whose native side is missing commits as `UnimplementedNativeView`.
17
+ */
18
+ import { registerViewName } from '@ng-native/fabric';
19
+ /** Element name -> the Fabric component name, and the props its React wrapper would default. */
20
+ export const NATIVE_VIEWS = {
21
+ /** `react-native-webview`. */
22
+ 'web-view': ['RNCWebView', { javaScriptEnabled: true, domStorageEnabled: true }],
23
+ /** `@react-native-community/slider`. */
24
+ slider: ['RNCSlider', { maximumValue: 1, minimumValue: 0, step: 0 }],
25
+ /** `@react-native-community/datetimepicker`. Android renders a dialog, iOS an inline view. */
26
+ 'date-time-picker': ['RNDateTimePicker'],
27
+ /** `@react-native-picker/picker`, whose items are `<picker-item>` children. */
28
+ picker: ['RNCPicker'],
29
+ 'picker-item': ['RNCPickerItem'],
30
+ /**
31
+ * `@react-native-segmented-control/segmented-control`. iOS only.
32
+ *
33
+ * A view manager from before Fabric, run through the interop layer, which only installs the
34
+ * `onChange` block when the prop is set - as React's handler does by being there. Without it
35
+ * the control changes on screen and nothing hears about it.
36
+ */
37
+ 'segmented-control': ['RNCSegmentedControl', { onChange: true }],
38
+ /** `@react-native-masked-view/masked-view`: its `maskElement` is a prop, not a child. */
39
+ 'masked-view': ['RNCMaskedView'],
40
+ /** `react-native-pager-view`, the swipeable pager a tab layout is built on. */
41
+ 'pager-view': ['RNCViewPager', { initialPage: 0 }],
42
+ /** `lottie-react-native`. */
43
+ 'lottie-view': ['LottieAnimationView', { autoPlay: true, loop: true }],
44
+ /**
45
+ * `@shopify/react-native-skia`.
46
+ *
47
+ * The view is real and this name is right, but it is worth being plain: Skia's drawing model is
48
+ * React elements through a reconciler of its own - `<Canvas><Path/></Canvas>` - and none of that
49
+ * is reachable here. What a `<skia-view>` will take is a `picture`, built with Skia's imperative
50
+ * `PictureRecorder` API, which has no React in it. Anything drawn declaratively does not come
51
+ * across, and a component that renders nothing is what that looks like.
52
+ */
53
+ 'skia-view': ['SkiaPictureView'],
54
+ /** `react-native-view-shot`, which captures whatever it wraps. */
55
+ 'view-shot': ['RNCViewShot'],
56
+ };
57
+ /**
58
+ * Register several of the known views by element name.
59
+ *
60
+ * ```ts
61
+ * registerNativeViews('web-view', 'slider');
62
+ * ```
63
+ *
64
+ * Named one at a time rather than all at once on purpose: registering a view for a library the
65
+ * app has not installed produces an element that commits as nothing, and finding out at startup
66
+ * which libraries are present is not something JavaScript can do without importing them all.
67
+ */
68
+ export function registerNativeViews(...elements) {
69
+ for (const element of elements) {
70
+ const known = NATIVE_VIEWS[element];
71
+ if (!known)
72
+ throw new Error(`[angular-native] no native view is known as '${element}'`);
73
+ registerViewName(element, known[0], known[1]);
74
+ }
75
+ }
76
+ //# sourceMappingURL=community-views.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-views.js","sourceRoot":"","sources":["../src/community-views.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,gGAAgG;AAChG,MAAM,CAAC,MAAM,YAAY,GAAiE;IACxF,8BAA8B;IAC9B,UAAU,EAAE,CAAC,YAAY,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAChF,wCAAwC;IACxC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACpE,8FAA8F;IAC9F,kBAAkB,EAAE,CAAC,kBAAkB,CAAC;IACxC,+EAA+E;IAC/E,MAAM,EAAE,CAAC,WAAW,CAAC;IACrB,aAAa,EAAE,CAAC,eAAe,CAAC;IAChC;;;;;;OAMG;IACH,mBAAmB,EAAE,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAChE,yFAAyF;IACzF,aAAa,EAAE,CAAC,eAAe,CAAC;IAChC,+EAA+E;IAC/E,YAAY,EAAE,CAAC,cAAc,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAClD,6BAA6B;IAC7B,aAAa,EAAE,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtE;;;;;;;;OAQG;IACH,WAAW,EAAE,CAAC,iBAAiB,CAAC;IAChC,kEAAkE;IAClE,WAAW,EAAE,CAAC,aAAa,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAG,QAAgD;IACrF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,OAAO,GAAG,CAAC,CAAC;QACxF,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;AACH,CAAC","sourcesContent":["/**\n * Native views from the libraries an Expo app already reaches for, by element name.\n *\n * These are not Expo modules, so their Fabric names are their own - what codegen produced from\n * each library's spec - rather than the `ViewManagerAdapter_` names `registerExpoView` derives.\n * They are here because they are in Expo's own bundled module list, which is the list an app\n * actually installs from, and because the alternative is every app looking each name up in a\n * library's generated code.\n *\n * Names and defaults, not components. An Angular component per native view, prop for prop, is a\n * maintenance burden with no end and a second place for every prop to be wrong. An app writes the\n * small component for the one it uses - a selector, and an input per prop it binds, passed through\n * as a host binding - which keeps its template checked; see `ExpoImage` for the shape.\n *\n * A library still has to be installed for its element to commit as anything. This is the\n * JavaScript half; an element whose native side is missing commits as `UnimplementedNativeView`.\n */\nimport { registerViewName } from '@ng-native/fabric';\n\n/** Element name -> the Fabric component name, and the props its React wrapper would default. */\nexport const NATIVE_VIEWS: Readonly<Record<string, [string, Record<string, unknown>?]>> = {\n /** `react-native-webview`. */\n 'web-view': ['RNCWebView', { javaScriptEnabled: true, domStorageEnabled: true }],\n /** `@react-native-community/slider`. */\n slider: ['RNCSlider', { maximumValue: 1, minimumValue: 0, step: 0 }],\n /** `@react-native-community/datetimepicker`. Android renders a dialog, iOS an inline view. */\n 'date-time-picker': ['RNDateTimePicker'],\n /** `@react-native-picker/picker`, whose items are `<picker-item>` children. */\n picker: ['RNCPicker'],\n 'picker-item': ['RNCPickerItem'],\n /**\n * `@react-native-segmented-control/segmented-control`. iOS only.\n *\n * A view manager from before Fabric, run through the interop layer, which only installs the\n * `onChange` block when the prop is set - as React's handler does by being there. Without it\n * the control changes on screen and nothing hears about it.\n */\n 'segmented-control': ['RNCSegmentedControl', { onChange: true }],\n /** `@react-native-masked-view/masked-view`: its `maskElement` is a prop, not a child. */\n 'masked-view': ['RNCMaskedView'],\n /** `react-native-pager-view`, the swipeable pager a tab layout is built on. */\n 'pager-view': ['RNCViewPager', { initialPage: 0 }],\n /** `lottie-react-native`. */\n 'lottie-view': ['LottieAnimationView', { autoPlay: true, loop: true }],\n /**\n * `@shopify/react-native-skia`.\n *\n * The view is real and this name is right, but it is worth being plain: Skia's drawing model is\n * React elements through a reconciler of its own - `<Canvas><Path/></Canvas>` - and none of that\n * is reachable here. What a `<skia-view>` will take is a `picture`, built with Skia's imperative\n * `PictureRecorder` API, which has no React in it. Anything drawn declaratively does not come\n * across, and a component that renders nothing is what that looks like.\n */\n 'skia-view': ['SkiaPictureView'],\n /** `react-native-view-shot`, which captures whatever it wraps. */\n 'view-shot': ['RNCViewShot'],\n};\n\n/**\n * Register several of the known views by element name.\n *\n * ```ts\n * registerNativeViews('web-view', 'slider');\n * ```\n *\n * Named one at a time rather than all at once on purpose: registering a view for a library the\n * app has not installed produces an element that commits as nothing, and finding out at startup\n * which libraries are present is not something JavaScript can do without importing them all.\n */\nexport function registerNativeViews(...elements: readonly (keyof typeof NATIVE_VIEWS)[]): void {\n for (const element of elements) {\n const known = NATIVE_VIEWS[element];\n if (!known) throw new Error(`[angular-native] no native view is known as '${element}'`);\n registerViewName(element, known[0], known[1]);\n }\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `Crypto`, bound to `expo-crypto`: random identifiers, random bytes, and hashes.
3
+ *
4
+ * ```ts
5
+ * import { Crypto, CryptoDigestAlgorithm } from '@ng-native/expo/crypto';
6
+ *
7
+ * private readonly crypto = inject(Crypto);
8
+ *
9
+ * readonly id = this.crypto.randomUUID();
10
+ * async fingerprint(text: string): Promise<string> {
11
+ * return this.crypto.digestString(CryptoDigestAlgorithm.SHA256, text);
12
+ * }
13
+ * ```
14
+ *
15
+ * The module's functions under shorter names, with its own types. Hermes has no Web Crypto, so
16
+ * this is where a UUID or a hash comes from on a device.
17
+ *
18
+ * Unlike the other Expo services, this one is not inert without its module. An empty identifier
19
+ * is a key every record shares, and an empty hash matches every input; neither looks wrong until
20
+ * the data is. So every call without the module throws, naming the package to install.
21
+ */
22
+ import { InjectionToken } from '@angular/core';
23
+ import type { CryptoDigestAlgorithm as ExpoDigestAlgorithm, CryptoEncoding as ExpoEncoding } from 'expo-crypto';
24
+ import * as i0 from "@angular/core";
25
+ type Expo = typeof import('expo-crypto');
26
+ /** The module's functions this service calls. The real module is one; a test provides a fake. */
27
+ export type NativeCrypto = Pick<Expo, 'randomUUID' | 'digestStringAsync' | 'digest' | 'getRandomBytes' | 'getRandomBytesAsync' | 'getRandomValues'>;
28
+ type Args<K extends keyof NativeCrypto> = Parameters<NativeCrypto[K]>;
29
+ /**
30
+ * The hash algorithms, as `expo-crypto`'s own enum. Importing the enum from the module loads the
31
+ * module, which a test in Node cannot; these are the same strings, typed as the enum.
32
+ */
33
+ export declare const CryptoDigestAlgorithm: typeof ExpoDigestAlgorithm;
34
+ /** How `digestString` writes its hash, as `expo-crypto`'s own enum: hex unless asked otherwise. */
35
+ export declare const CryptoEncoding: typeof ExpoEncoding;
36
+ export declare class Crypto {
37
+ /** Overridden in a test to hand out identifiers that are not random. */
38
+ static readonly SOURCE: InjectionToken<NativeCrypto | null>;
39
+ private readonly native;
40
+ /** A version 4 UUID, from the platform's secure random source. */
41
+ randomUUID(): string;
42
+ /** The hash of a string, as hex or base64. */
43
+ digestString(...args: Args<'digestStringAsync'>): Promise<string>;
44
+ /** The hash of bytes, as Web Crypto's `digest` answers it. */
45
+ digest(...args: Args<'digest'>): Promise<ArrayBuffer>;
46
+ /** Cryptographically secure random bytes, between 0 and 1024 of them. */
47
+ randomBytes(count: number): Uint8Array;
48
+ /** The same, as a promise. */
49
+ randomBytesAsync(count: number): Promise<Uint8Array>;
50
+ /** Fills an integer typed array with secure random values and hands it back, as Web Crypto's. */
51
+ randomValues<T extends Args<'getRandomValues'>[0]>(array: T): T;
52
+ private module;
53
+ static ɵfac: i0.ɵɵFactoryDeclaration<Crypto, never>;
54
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
55
+ }
56
+ export {};
package/dist/crypto.js ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `Crypto`, bound to `expo-crypto`: random identifiers, random bytes, and hashes.
3
+ *
4
+ * ```ts
5
+ * import { Crypto, CryptoDigestAlgorithm } from '@ng-native/expo/crypto';
6
+ *
7
+ * private readonly crypto = inject(Crypto);
8
+ *
9
+ * readonly id = this.crypto.randomUUID();
10
+ * async fingerprint(text: string): Promise<string> {
11
+ * return this.crypto.digestString(CryptoDigestAlgorithm.SHA256, text);
12
+ * }
13
+ * ```
14
+ *
15
+ * The module's functions under shorter names, with its own types. Hermes has no Web Crypto, so
16
+ * this is where a UUID or a hash comes from on a device.
17
+ *
18
+ * Unlike the other Expo services, this one is not inert without its module. An empty identifier
19
+ * is a key every record shares, and an empty hash matches every input; neither looks wrong until
20
+ * the data is. So every call without the module throws, naming the package to install.
21
+ */
22
+ import { InjectionToken, Service, inject } from '@angular/core';
23
+ import { expoModule } from "./native.js";
24
+ import * as i0 from "@angular/core";
25
+ /**
26
+ * The hash algorithms, as `expo-crypto`'s own enum. Importing the enum from the module loads the
27
+ * module, which a test in Node cannot; these are the same strings, typed as the enum.
28
+ */
29
+ export const CryptoDigestAlgorithm = {
30
+ SHA1: 'SHA-1',
31
+ SHA256: 'SHA-256',
32
+ SHA384: 'SHA-384',
33
+ SHA512: 'SHA-512',
34
+ MD2: 'MD2',
35
+ MD4: 'MD4',
36
+ MD5: 'MD5',
37
+ };
38
+ /** How `digestString` writes its hash, as `expo-crypto`'s own enum: hex unless asked otherwise. */
39
+ export const CryptoEncoding = {
40
+ HEX: 'hex',
41
+ BASE64: 'base64',
42
+ };
43
+ export class Crypto {
44
+ /** Overridden in a test to hand out identifiers that are not random. */
45
+ static SOURCE = new InjectionToken('angular-native.cryptoSource', {
46
+ factory: () => expoModule('expo-crypto', () => require('expo-crypto')),
47
+ });
48
+ native = inject(Crypto.SOURCE);
49
+ /** A version 4 UUID, from the platform's secure random source. */
50
+ randomUUID() {
51
+ return this.module().randomUUID();
52
+ }
53
+ /** The hash of a string, as hex or base64. */
54
+ async digestString(...args) {
55
+ return this.module().digestStringAsync(...args);
56
+ }
57
+ /** The hash of bytes, as Web Crypto's `digest` answers it. */
58
+ async digest(...args) {
59
+ return this.module().digest(...args);
60
+ }
61
+ /** Cryptographically secure random bytes, between 0 and 1024 of them. */
62
+ randomBytes(count) {
63
+ return this.module().getRandomBytes(count);
64
+ }
65
+ /** The same, as a promise. */
66
+ async randomBytesAsync(count) {
67
+ return this.module().getRandomBytesAsync(count);
68
+ }
69
+ /** Fills an integer typed array with secure random values and hands it back, as Web Crypto's. */
70
+ randomValues(array) {
71
+ return this.module().getRandomValues(array);
72
+ }
73
+ module() {
74
+ if (!this.native)
75
+ throw new Error('Crypto needs expo-crypto: npx expo install expo-crypto');
76
+ return this.native;
77
+ }
78
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Crypto, deps: [], target: i0.ɵɵFactoryTarget.Service });
79
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Crypto });
80
+ }
81
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Crypto, decorators: [{
82
+ type: Service
83
+ }] });
84
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAKhE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAiBzC;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;CAC8B,CAAC;AAE3C,mGAAmG;AACnG,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,QAAQ;CACiB,CAAC;AAGpC,MAAM,OAAO,MAAM;IACjB,wEAAwE;IACxE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAsB,6BAA6B,EAAE;QAC9F,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAS,CAAC;KAC/E,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEhD,kEAAkE;IAClE,UAAU;QACR,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC;IACpC,CAAC;IAED,8CAA8C;IAC9C,KAAK,CAAC,YAAY,CAAC,GAAG,IAA+B;QACnD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,MAAM,CAAC,GAAG,IAAoB;QAClC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,yEAAyE;IACzE,WAAW,CAAC,KAAa;QACvB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,8BAA8B;IAC9B,KAAK,CAAC,gBAAgB,CAAC,KAAa;QAClC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,iGAAiG;IACjG,YAAY,CAAuC,KAAQ;QACzD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;uGAzCU,MAAM;wGAAN,MAAM;;2FAAN,MAAM;kBADlB,OAAO","sourcesContent":["/**\n * `Crypto`, bound to `expo-crypto`: random identifiers, random bytes, and hashes.\n *\n * ```ts\n * import { Crypto, CryptoDigestAlgorithm } from '@ng-native/expo/crypto';\n *\n * private readonly crypto = inject(Crypto);\n *\n * readonly id = this.crypto.randomUUID();\n * async fingerprint(text: string): Promise<string> {\n * return this.crypto.digestString(CryptoDigestAlgorithm.SHA256, text);\n * }\n * ```\n *\n * The module's functions under shorter names, with its own types. Hermes has no Web Crypto, so\n * this is where a UUID or a hash comes from on a device.\n *\n * Unlike the other Expo services, this one is not inert without its module. An empty identifier\n * is a key every record shares, and an empty hash matches every input; neither looks wrong until\n * the data is. So every call without the module throws, naming the package to install.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport type {\n CryptoDigestAlgorithm as ExpoDigestAlgorithm,\n CryptoEncoding as ExpoEncoding,\n} from 'expo-crypto';\nimport { expoModule } from './native.ts';\n\ntype Expo = typeof import('expo-crypto');\n\n/** The module's functions this service calls. The real module is one; a test provides a fake. */\nexport type NativeCrypto = Pick<\n Expo,\n | 'randomUUID'\n | 'digestStringAsync'\n | 'digest'\n | 'getRandomBytes'\n | 'getRandomBytesAsync'\n | 'getRandomValues'\n>;\n\ntype Args<K extends keyof NativeCrypto> = Parameters<NativeCrypto[K]>;\n\n/**\n * The hash algorithms, as `expo-crypto`'s own enum. Importing the enum from the module loads the\n * module, which a test in Node cannot; these are the same strings, typed as the enum.\n */\nexport const CryptoDigestAlgorithm = {\n SHA1: 'SHA-1',\n SHA256: 'SHA-256',\n SHA384: 'SHA-384',\n SHA512: 'SHA-512',\n MD2: 'MD2',\n MD4: 'MD4',\n MD5: 'MD5',\n} as unknown as typeof ExpoDigestAlgorithm;\n\n/** How `digestString` writes its hash, as `expo-crypto`'s own enum: hex unless asked otherwise. */\nexport const CryptoEncoding = {\n HEX: 'hex',\n BASE64: 'base64',\n} as unknown as typeof ExpoEncoding;\n\n@Service()\nexport class Crypto {\n /** Overridden in a test to hand out identifiers that are not random. */\n static readonly SOURCE = new InjectionToken<NativeCrypto | null>('angular-native.cryptoSource', {\n factory: () => expoModule('expo-crypto', () => require('expo-crypto') as Expo),\n });\n\n private readonly native = inject(Crypto.SOURCE);\n\n /** A version 4 UUID, from the platform's secure random source. */\n randomUUID(): string {\n return this.module().randomUUID();\n }\n\n /** The hash of a string, as hex or base64. */\n async digestString(...args: Args<'digestStringAsync'>): Promise<string> {\n return this.module().digestStringAsync(...args);\n }\n\n /** The hash of bytes, as Web Crypto's `digest` answers it. */\n async digest(...args: Args<'digest'>): Promise<ArrayBuffer> {\n return this.module().digest(...args);\n }\n\n /** Cryptographically secure random bytes, between 0 and 1024 of them. */\n randomBytes(count: number): Uint8Array {\n return this.module().getRandomBytes(count);\n }\n\n /** The same, as a promise. */\n async randomBytesAsync(count: number): Promise<Uint8Array> {\n return this.module().getRandomBytesAsync(count);\n }\n\n /** Fills an integer typed array with secure random values and hands it back, as Web Crypto's. */\n randomValues<T extends Args<'getRandomValues'>[0]>(array: T): T {\n return this.module().getRandomValues(array);\n }\n\n private module(): NativeCrypto {\n if (!this.native) throw new Error('Crypto needs expo-crypto: npx expo install expo-crypto');\n return this.native;\n }\n}\n"]}
@@ -0,0 +1,47 @@
1
+ /** Enough of `SQLiteDatabase` to own it. Queries go through the database itself. */
2
+ export interface NativeDatabase {
3
+ execAsync(source: string): Promise<void>;
4
+ getFirstAsync<T>(source: string, ...params: unknown[]): Promise<T | null>;
5
+ closeAsync(): Promise<void>;
6
+ withTransactionAsync(task: () => Promise<void>): Promise<void>;
7
+ }
8
+ /** One step from one schema version to the next. Run in order, each inside a transaction. */
9
+ export interface Migration {
10
+ /** The version this migration produces. Must be greater than every one before it. */
11
+ readonly to: number;
12
+ readonly up: (database: NativeDatabase) => Promise<void>;
13
+ }
14
+ export declare class Database<T extends NativeDatabase> {
15
+ private readonly open;
16
+ private readonly migrations;
17
+ private opening;
18
+ constructor(open: () => Promise<T>, migrations?: readonly Migration[]);
19
+ /**
20
+ * The database, opened and migrated.
21
+ *
22
+ * Awaited at every call site rather than held, because the first caller is the one that pays for
23
+ * opening it and every caller after that is handed the same promise - including the ones that
24
+ * arrive while it is still opening, which is what stops two connections being made at once.
25
+ */
26
+ ready(): Promise<T>;
27
+ /**
28
+ * Close it. The next caller reopens; this is for a sign-out that has to leave nothing behind.
29
+ *
30
+ * Resolves even when the open failed: the connection was already closed by the failure, so there
31
+ * is nothing left to release.
32
+ */
33
+ close(): Promise<void>;
34
+ /** Open and migrate, closing the connection again if the migration fails. */
35
+ private start;
36
+ /**
37
+ * Bring the schema up to date, in order, each step in its own transaction.
38
+ *
39
+ * `user_version` is SQLite's own integer for this, so the record of where a database has got to
40
+ * lives in the database rather than in a table this has to create first. It is transactional,
41
+ * so it is set inside the migration's own transaction: the schema change and the record of it
42
+ * commit together or roll back together, and a migration can never be half done and recorded.
43
+ */
44
+ private migrate;
45
+ }
46
+ /** A database, opened on first use and migrated before the first query sees it. */
47
+ export declare function database(name: string, migrations?: readonly Migration[]): Database<import("expo-sqlite").SQLiteDatabase>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * `database()`, bound to `expo-sqlite`.
3
+ *
4
+ * `SQLiteProvider` and `useSQLiteContext` are the documented way in and both are React: a context
5
+ * that opens the database, runs migrations, and closes it when the provider unmounts. The database
6
+ * itself is a plain object with `getAllAsync` and the rest already on it, so what is rebuilt is
7
+ * the *lifecycle* - opened once, migrated before anything queries, closed on request:
8
+ *
9
+ * ```ts
10
+ * export const notes = database('notes.db', [
11
+ * { to: 1, up: (db) => db.execAsync('CREATE TABLE note (id INTEGER PRIMARY KEY, body TEXT)') },
12
+ * ]);
13
+ *
14
+ * const rows = await (await notes.ready()).getAllAsync<Note>('SELECT * FROM note');
15
+ * ```
16
+ *
17
+ * A value rather than a service, because a schema belongs to a feature rather than to an injector,
18
+ * and an app usually has exactly one. Opening is deferred until something asks: a database opened
19
+ * at startup is a file handle and a WAL journal for an app that may never read from it.
20
+ */
21
+ import { expoModule } from "./native.js";
22
+ export class Database {
23
+ open;
24
+ migrations;
25
+ opening = null;
26
+ constructor(open, migrations = []) {
27
+ this.open = open;
28
+ this.migrations = [...migrations].sort((a, b) => a.to - b.to);
29
+ }
30
+ /**
31
+ * The database, opened and migrated.
32
+ *
33
+ * Awaited at every call site rather than held, because the first caller is the one that pays for
34
+ * opening it and every caller after that is handed the same promise - including the ones that
35
+ * arrive while it is still opening, which is what stops two connections being made at once.
36
+ */
37
+ ready() {
38
+ if (this.opening)
39
+ return this.opening;
40
+ const opening = this.start();
41
+ this.opening = opening;
42
+ // A failed open is forgotten once everyone waiting on it has heard, so the next caller tries
43
+ // again rather than being handed the same failure for the life of the app.
44
+ opening.catch(() => {
45
+ if (this.opening === opening)
46
+ this.opening = null;
47
+ });
48
+ return opening;
49
+ }
50
+ /**
51
+ * Close it. The next caller reopens; this is for a sign-out that has to leave nothing behind.
52
+ *
53
+ * Resolves even when the open failed: the connection was already closed by the failure, so there
54
+ * is nothing left to release.
55
+ */
56
+ async close() {
57
+ const opening = this.opening;
58
+ this.opening = null;
59
+ const database = await opening?.catch(() => null);
60
+ await database?.closeAsync();
61
+ }
62
+ /** Open and migrate, closing the connection again if the migration fails. */
63
+ async start() {
64
+ const database = await this.open();
65
+ try {
66
+ await this.migrate(database);
67
+ }
68
+ catch (error) {
69
+ // The migration's own error is the one worth reporting; a failure to close after it is not.
70
+ await database.closeAsync().catch(() => { });
71
+ throw error;
72
+ }
73
+ return database;
74
+ }
75
+ /**
76
+ * Bring the schema up to date, in order, each step in its own transaction.
77
+ *
78
+ * `user_version` is SQLite's own integer for this, so the record of where a database has got to
79
+ * lives in the database rather than in a table this has to create first. It is transactional,
80
+ * so it is set inside the migration's own transaction: the schema change and the record of it
81
+ * commit together or roll back together, and a migration can never be half done and recorded.
82
+ */
83
+ async migrate(database) {
84
+ if (!this.migrations.length)
85
+ return;
86
+ const row = await database.getFirstAsync('PRAGMA user_version');
87
+ let version = row?.user_version ?? 0;
88
+ for (const migration of this.migrations) {
89
+ if (migration.to <= version)
90
+ continue;
91
+ await database.withTransactionAsync(async () => {
92
+ await migration.up(database);
93
+ await database.execAsync(`PRAGMA user_version = ${migration.to}`);
94
+ });
95
+ version = migration.to;
96
+ }
97
+ }
98
+ }
99
+ /** A database, opened on first use and migrated before the first query sees it. */
100
+ export function database(name, migrations = []) {
101
+ return new Database(async () => {
102
+ const expo = expoModule('expo-sqlite', () => require('expo-sqlite'), ['ios', 'android', 'web']);
103
+ if (!expo)
104
+ throw new Error('[angular-native] expo-sqlite is not installed');
105
+ return expo.openDatabaseAsync(name);
106
+ }, migrations);
107
+ }
108
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `database()`, bound to `expo-sqlite`.\n *\n * `SQLiteProvider` and `useSQLiteContext` are the documented way in and both are React: a context\n * that opens the database, runs migrations, and closes it when the provider unmounts. The database\n * itself is a plain object with `getAllAsync` and the rest already on it, so what is rebuilt is\n * the *lifecycle* - opened once, migrated before anything queries, closed on request:\n *\n * ```ts\n * export const notes = database('notes.db', [\n * { to: 1, up: (db) => db.execAsync('CREATE TABLE note (id INTEGER PRIMARY KEY, body TEXT)') },\n * ]);\n *\n * const rows = await (await notes.ready()).getAllAsync<Note>('SELECT * FROM note');\n * ```\n *\n * A value rather than a service, because a schema belongs to a feature rather than to an injector,\n * and an app usually has exactly one. Opening is deferred until something asks: a database opened\n * at startup is a file handle and a WAL journal for an app that may never read from it.\n */\nimport { expoModule } from './native.ts';\n\n/** Enough of `SQLiteDatabase` to own it. Queries go through the database itself. */\nexport interface NativeDatabase {\n execAsync(source: string): Promise<void>;\n getFirstAsync<T>(source: string, ...params: unknown[]): Promise<T | null>;\n closeAsync(): Promise<void>;\n withTransactionAsync(task: () => Promise<void>): Promise<void>;\n}\n\n/** One step from one schema version to the next. Run in order, each inside a transaction. */\nexport interface Migration {\n /** The version this migration produces. Must be greater than every one before it. */\n readonly to: number;\n readonly up: (database: NativeDatabase) => Promise<void>;\n}\n\nexport class Database<T extends NativeDatabase> {\n private readonly open: () => Promise<T>;\n private readonly migrations: readonly Migration[];\n private opening: Promise<T> | null = null;\n\n constructor(open: () => Promise<T>, migrations: readonly Migration[] = []) {\n this.open = open;\n this.migrations = [...migrations].sort((a, b) => a.to - b.to);\n }\n\n /**\n * The database, opened and migrated.\n *\n * Awaited at every call site rather than held, because the first caller is the one that pays for\n * opening it and every caller after that is handed the same promise - including the ones that\n * arrive while it is still opening, which is what stops two connections being made at once.\n */\n ready(): Promise<T> {\n if (this.opening) return this.opening;\n const opening = this.start();\n this.opening = opening;\n // A failed open is forgotten once everyone waiting on it has heard, so the next caller tries\n // again rather than being handed the same failure for the life of the app.\n opening.catch(() => {\n if (this.opening === opening) this.opening = null;\n });\n return opening;\n }\n\n /**\n * Close it. The next caller reopens; this is for a sign-out that has to leave nothing behind.\n *\n * Resolves even when the open failed: the connection was already closed by the failure, so there\n * is nothing left to release.\n */\n async close(): Promise<void> {\n const opening = this.opening;\n this.opening = null;\n const database = await opening?.catch(() => null);\n await database?.closeAsync();\n }\n\n /** Open and migrate, closing the connection again if the migration fails. */\n private async start(): Promise<T> {\n const database = await this.open();\n try {\n await this.migrate(database);\n } catch (error) {\n // The migration's own error is the one worth reporting; a failure to close after it is not.\n await database.closeAsync().catch(() => {});\n throw error;\n }\n return database;\n }\n\n /**\n * Bring the schema up to date, in order, each step in its own transaction.\n *\n * `user_version` is SQLite's own integer for this, so the record of where a database has got to\n * lives in the database rather than in a table this has to create first. It is transactional,\n * so it is set inside the migration's own transaction: the schema change and the record of it\n * commit together or roll back together, and a migration can never be half done and recorded.\n */\n private async migrate(database: T): Promise<void> {\n if (!this.migrations.length) return;\n\n const row = await database.getFirstAsync<{ user_version: number }>('PRAGMA user_version');\n let version = row?.user_version ?? 0;\n\n for (const migration of this.migrations) {\n if (migration.to <= version) continue;\n await database.withTransactionAsync(async () => {\n await migration.up(database);\n await database.execAsync(`PRAGMA user_version = ${migration.to}`);\n });\n version = migration.to;\n }\n }\n}\n\n/** A database, opened on first use and migrated before the first query sees it. */\nexport function database(name: string, migrations: readonly Migration[] = []) {\n return new Database<import('expo-sqlite').SQLiteDatabase>(async () => {\n const expo = expoModule(\n 'expo-sqlite',\n () => require('expo-sqlite') as typeof import('expo-sqlite'),\n ['ios', 'android', 'web'],\n );\n if (!expo) throw new Error('[angular-native] expo-sqlite is not installed');\n return expo.openDatabaseAsync(name);\n }, migrations);\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `DocumentPicker`, bound to `expo-document-picker`: the system's file picker.
3
+ *
4
+ * ```ts
5
+ * private readonly documents = inject(DocumentPicker);
6
+ * const [pdf] = await this.documents.pick({ type: 'application/pdf' });
7
+ * ```
8
+ *
9
+ * The options are the module's own, passed through unchanged. What this adds is the thing every
10
+ * call site otherwise writes: a cancelled picker answers with no files rather than a result to
11
+ * unwrap. The system picker needs no permission, so none is asked for. Without the module
12
+ * installed, `pick()` answers with no files.
13
+ */
14
+ import { InjectionToken } from '@angular/core';
15
+ import * as i0 from "@angular/core";
16
+ type Expo = typeof import('expo-document-picker');
17
+ /** The module's function this service calls. The real module is one; a test provides a fake. */
18
+ export type NativeDocumentPicker = Pick<Expo, 'getDocumentAsync'>;
19
+ export type DocumentPickerOptions = import('expo-document-picker').DocumentPickerOptions;
20
+ export type PickedDocument = import('expo-document-picker').DocumentPickerAsset;
21
+ export declare class DocumentPicker {
22
+ /** Overridden in a test to pick files without a file system. */
23
+ static readonly SOURCE: InjectionToken<NativeDocumentPicker | null>;
24
+ private readonly native;
25
+ /**
26
+ * Opens the system picker. `type` is a MIME type or a list of them, `multiple` allows more than
27
+ * one file, and `copyToCacheDirectory` (the default) copies each into the app's cache so other
28
+ * Expo modules can read it. Empty if they cancelled.
29
+ */
30
+ pick(options?: DocumentPickerOptions): Promise<readonly PickedDocument[]>;
31
+ static ɵfac: i0.ɵɵFactoryDeclaration<DocumentPicker, never>;
32
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
33
+ }
34
+ export {};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `DocumentPicker`, bound to `expo-document-picker`: the system's file picker.
3
+ *
4
+ * ```ts
5
+ * private readonly documents = inject(DocumentPicker);
6
+ * const [pdf] = await this.documents.pick({ type: 'application/pdf' });
7
+ * ```
8
+ *
9
+ * The options are the module's own, passed through unchanged. What this adds is the thing every
10
+ * call site otherwise writes: a cancelled picker answers with no files rather than a result to
11
+ * unwrap. The system picker needs no permission, so none is asked for. Without the module
12
+ * installed, `pick()` answers with no files.
13
+ */
14
+ import { InjectionToken, Service, inject } from '@angular/core';
15
+ import { expoModule } from "./native.js";
16
+ import * as i0 from "@angular/core";
17
+ export class DocumentPicker {
18
+ /** Overridden in a test to pick files without a file system. */
19
+ static SOURCE = new InjectionToken('angular-native.documentPickerSource', {
20
+ factory: () => expoModule('expo-document-picker', () => require('expo-document-picker')),
21
+ });
22
+ native = inject(DocumentPicker.SOURCE);
23
+ /**
24
+ * Opens the system picker. `type` is a MIME type or a list of them, `multiple` allows more than
25
+ * one file, and `copyToCacheDirectory` (the default) copies each into the app's cache so other
26
+ * Expo modules can read it. Empty if they cancelled.
27
+ */
28
+ async pick(options) {
29
+ if (!this.native)
30
+ return [];
31
+ const result = await this.native.getDocumentAsync(options);
32
+ return result.canceled ? [] : result.assets;
33
+ }
34
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: DocumentPicker, deps: [], target: i0.ɵɵFactoryTarget.Service });
35
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: DocumentPicker });
36
+ }
37
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: DocumentPicker, decorators: [{
38
+ type: Service
39
+ }] });
40
+ //# sourceMappingURL=document-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-picker.js","sourceRoot":"","sources":["../src/document-picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAWzC,MAAM,OAAO,cAAc;IACzB,gEAAgE;IAChE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,qCAAqC,EACrC;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAS,CAAC;KACpF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAExD;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,OAA+B;QACxC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAC9C,CAAC;uGArBU,cAAc;wGAAd,cAAc;;2FAAd,cAAc;kBAD1B,OAAO","sourcesContent":["/**\n * `DocumentPicker`, bound to `expo-document-picker`: the system's file picker.\n *\n * ```ts\n * private readonly documents = inject(DocumentPicker);\n * const [pdf] = await this.documents.pick({ type: 'application/pdf' });\n * ```\n *\n * The options are the module's own, passed through unchanged. What this adds is the thing every\n * call site otherwise writes: a cancelled picker answers with no files rather than a result to\n * unwrap. The system picker needs no permission, so none is asked for. Without the module\n * installed, `pick()` answers with no files.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\n\ntype Expo = typeof import('expo-document-picker');\n\n/** The module's function this service calls. The real module is one; a test provides a fake. */\nexport type NativeDocumentPicker = Pick<Expo, 'getDocumentAsync'>;\n\nexport type DocumentPickerOptions = import('expo-document-picker').DocumentPickerOptions;\nexport type PickedDocument = import('expo-document-picker').DocumentPickerAsset;\n\n@Service()\nexport class DocumentPicker {\n /** Overridden in a test to pick files without a file system. */\n static readonly SOURCE = new InjectionToken<NativeDocumentPicker | null>(\n 'angular-native.documentPickerSource',\n {\n factory: () =>\n expoModule('expo-document-picker', () => require('expo-document-picker') as Expo),\n },\n );\n\n private readonly native = inject(DocumentPicker.SOURCE);\n\n /**\n * Opens the system picker. `type` is a MIME type or a list of them, `multiple` allows more than\n * one file, and `copyToCacheDirectory` (the default) copies each into the app's cache so other\n * Expo modules can read it. Empty if they cancelled.\n */\n async pick(options?: DocumentPickerOptions): Promise<readonly PickedDocument[]> {\n if (!this.native) return [];\n const result = await this.native.getDocumentAsync(options);\n return result.canceled ? [] : result.assets;\n }\n}\n"]}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `<dom-component>`: an Angular component rendered by a browser - DOM, CSS, anything a browser
3
+ * runs - in a web view inside a native screen, with its inputs and outputs bound from the app's
4
+ * template. Expo calls this a DOM component; it is not the Web Components standard.
5
+ *
6
+ * ```ts
7
+ * // web/note.ts: an ordinary Angular component for the browser, in the app's own source
8
+ * 'use dom';
9
+ * @Component({ selector: 'app-note', template: '<textarea ...></textarea>', styles: [...] })
10
+ * export class Note {
11
+ * readonly label = input('');
12
+ * readonly sent = output<string>();
13
+ * }
14
+ * export default mountInWebView(Note); // from '@ng-native/web/web-view'
15
+ *
16
+ * // a native screen
17
+ * import note from './web/note.ts';
18
+ * <dom-component [src]="note" class="flex-1"
19
+ * [inputs]="{ label: title() }" [outputs]="{ sent: save }" />
20
+ * ```
21
+ *
22
+ * The component runs in Expo's own web view (`@expo/dom-webview`, which `expo` already installs),
23
+ * so it is a separate JavaScript runtime from the app and only JSON crosses. It is shipped the way
24
+ * Expo ships its `'use dom'` components: the import is a reference to a page that Expo's dev server
25
+ * serves in development and `expo export:embed` bundles into the app for a release build (see
26
+ * `@ng-native/metro`'s `dom-component.cjs`), and the page is found where Expo finds its own.
27
+ *
28
+ * - **`inputs`** go into the page before it loads, and a changed value is sent to it afterwards
29
+ * without reloading. Anything sent before the page is ready is sent again once it is.
30
+ * - **`outputs`** map the component's outputs, by public name, to handlers. A name the component
31
+ * has no output for is an error naming the ones it does have, as `<ng-native-island>`'s is.
32
+ * - **Errors** the page throws reach the app's `ErrorHandler`.
33
+ */
34
+ import { InjectionToken } from '@angular/core';
35
+ import { type NativeSyntheticEvent } from '@ng-native/fabric';
36
+ import * as i0 from "@angular/core";
37
+ /** A handler for one of the DOM component's outputs. */
38
+ export type DomComponentOutputHandler = (value: never) => void;
39
+ /** The functions Expo's web view defines on its view, called with the view's tag as `this`. */
40
+ export interface WebViewFunctions {
41
+ injectJavaScript(this: {
42
+ nativeTag: number;
43
+ }, script: string): Promise<void>;
44
+ }
45
+ /** Where the pages are, and how to reach the view once it is on screen. */
46
+ export interface DomComponentSource {
47
+ /** Where Expo keeps DOM components' pages: the dev server, `www.bundle`, or an update. */
48
+ readonly baseUrl: string | null;
49
+ readonly functions: WebViewFunctions | null;
50
+ }
51
+ /** What native code gets for `import note from './web/note.ts'`. */
52
+ export interface DomComponentReference {
53
+ readonly domComponent: string;
54
+ }
55
+ export declare class DomComponent {
56
+ /** Overridden in a test to load pages without a dev server. */
57
+ static readonly SOURCE: InjectionToken<DomComponentSource>;
58
+ private readonly native;
59
+ private readonly engine;
60
+ private readonly node;
61
+ private ready;
62
+ /** The DOM component, as imported: `import note from './web/note.ts'`. */
63
+ readonly src: import("@angular/core").InputSignal<DomComponentReference>;
64
+ /** Its inputs, by public name. JSON only: this crosses into another runtime. */
65
+ readonly inputs: import("@angular/core").InputSignal<Readonly<Record<string, unknown>>>;
66
+ /** Handlers for its outputs, by public name. */
67
+ readonly outputs: import("@angular/core").InputSignal<Readonly<Record<string, DomComponentOutputHandler>>>;
68
+ protected readonly debuggable: boolean;
69
+ protected readonly source: import("@angular/core").Signal<{
70
+ uri: string;
71
+ } | null>;
72
+ /** The file's name for an error message: the dev page names it, a release page is a hash. */
73
+ private readonly name;
74
+ /**
75
+ * What the page reads before it loads. The inputs are read untracked: a change to this prop
76
+ * reloads the page, so a changed input is sent to the running page instead.
77
+ */
78
+ protected readonly page: import("@angular/core").Signal<string>;
79
+ constructor();
80
+ protected receive(event: NativeSyntheticEvent<{
81
+ data: string;
82
+ }>): void;
83
+ private checkOutputs;
84
+ private send;
85
+ static ɵfac: i0.ɵɵFactoryDeclaration<DomComponent, never>;
86
+ static ɵcmp: i0.ɵɵComponentDeclaration<DomComponent, "dom-component", never, { "src": { "alias": "src"; "required": true; "isSignal": true; }; "inputs": { "alias": "inputs"; "required": false; "isSignal": true; }; "outputs": { "alias": "outputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
87
+ }