@axonpack/preview 0.0.0-stage → 0.1.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 (182) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +118 -2
  4. package/android/build.gradle +21 -0
  5. package/android/consumer-rules.pro +3 -0
  6. package/android/src/main/java/expo/modules/axonpackpreview/AxonpackPreviewModule.kt +72 -0
  7. package/android/src/main/java/expo/modules/axonpackpreview/BundleLoaderOverride.kt +41 -0
  8. package/android/src/main/java/expo/modules/axonpackpreview/HermesHeader.kt +24 -0
  9. package/android/src/main/java/expo/modules/axonpackpreview/PreviewPackage.kt +76 -0
  10. package/android/src/main/java/expo/modules/axonpackpreview/PreviewState.kt +295 -0
  11. package/app.plugin.js +112 -0
  12. package/bin/preview.js +2 -0
  13. package/build/cli.cjs +1465 -0
  14. package/build/cli.d.ts +3 -0
  15. package/build/cli.d.ts.map +1 -0
  16. package/build/cli.js +164 -0
  17. package/build/cli.js.map +1 -0
  18. package/build/components/preview-list/preview-list.component.d.ts +13 -0
  19. package/build/components/preview-list/preview-list.component.d.ts.map +1 -0
  20. package/build/components/preview-list/preview-list.component.js +316 -0
  21. package/build/components/preview-list/preview-list.component.js.map +1 -0
  22. package/build/components/preview-list/preview-list.types.d.ts +59 -0
  23. package/build/components/preview-list/preview-list.types.d.ts.map +1 -0
  24. package/build/components/preview-list/preview-list.types.js +2 -0
  25. package/build/components/preview-list/preview-list.types.js.map +1 -0
  26. package/build/components/preview-provider/preview-provider.component.d.ts +12 -0
  27. package/build/components/preview-provider/preview-provider.component.d.ts.map +1 -0
  28. package/build/components/preview-provider/preview-provider.component.js +19 -0
  29. package/build/components/preview-provider/preview-provider.component.js.map +1 -0
  30. package/build/constants/dev-preview-servers/dev-preview-servers.const.d.ts +26 -0
  31. package/build/constants/dev-preview-servers/dev-preview-servers.const.d.ts.map +1 -0
  32. package/build/constants/dev-preview-servers/dev-preview-servers.const.js +9 -0
  33. package/build/constants/dev-preview-servers/dev-preview-servers.const.js.map +1 -0
  34. package/build/constants/preview-list/theme.const.d.ts +5 -0
  35. package/build/constants/preview-list/theme.const.d.ts.map +1 -0
  36. package/build/constants/preview-list/theme.const.js +65 -0
  37. package/build/constants/preview-list/theme.const.js.map +1 -0
  38. package/build/index.d.ts +24 -0
  39. package/build/index.d.ts.map +1 -0
  40. package/build/index.js +16 -0
  41. package/build/index.js.map +1 -0
  42. package/build/services/cli/config.service.d.ts +13 -0
  43. package/build/services/cli/config.service.d.ts.map +1 -0
  44. package/build/services/cli/config.service.js +39 -0
  45. package/build/services/cli/config.service.js.map +1 -0
  46. package/build/services/cli/project.service.d.ts +25 -0
  47. package/build/services/cli/project.service.d.ts.map +1 -0
  48. package/build/services/cli/project.service.js +81 -0
  49. package/build/services/cli/project.service.js.map +1 -0
  50. package/build/services/cli/title.service.d.ts +15 -0
  51. package/build/services/cli/title.service.d.ts.map +1 -0
  52. package/build/services/cli/title.service.js +24 -0
  53. package/build/services/cli/title.service.js.map +1 -0
  54. package/build/services/dev-preview-servers/discover.service.d.ts +14 -0
  55. package/build/services/dev-preview-servers/discover.service.d.ts.map +1 -0
  56. package/build/services/dev-preview-servers/discover.service.js +106 -0
  57. package/build/services/dev-preview-servers/discover.service.js.map +1 -0
  58. package/build/services/native/build-info.service.d.ts +11 -0
  59. package/build/services/native/build-info.service.d.ts.map +1 -0
  60. package/build/services/native/build-info.service.js +18 -0
  61. package/build/services/native/build-info.service.js.map +1 -0
  62. package/build/services/native/native.service.d.ts +24 -0
  63. package/build/services/native/native.service.d.ts.map +1 -0
  64. package/build/services/native/native.service.js +13 -0
  65. package/build/services/native/native.service.js.map +1 -0
  66. package/build/services/previews/download.service.d.ts +14 -0
  67. package/build/services/previews/download.service.d.ts.map +1 -0
  68. package/build/services/previews/download.service.js +66 -0
  69. package/build/services/previews/download.service.js.map +1 -0
  70. package/build/services/previews/local-previews.service.d.ts +25 -0
  71. package/build/services/previews/local-previews.service.d.ts.map +1 -0
  72. package/build/services/previews/local-previews.service.js +50 -0
  73. package/build/services/previews/local-previews.service.js.map +1 -0
  74. package/build/services/previews/reload.service.d.ts +6 -0
  75. package/build/services/previews/reload.service.d.ts.map +1 -0
  76. package/build/services/previews/reload.service.js +13 -0
  77. package/build/services/previews/reload.service.js.map +1 -0
  78. package/build/services/previews/remote.service.d.ts +11 -0
  79. package/build/services/previews/remote.service.d.ts.map +1 -0
  80. package/build/services/previews/remote.service.js +34 -0
  81. package/build/services/previews/remote.service.js.map +1 -0
  82. package/build/services/previews/select.service.d.ts +11 -0
  83. package/build/services/previews/select.service.d.ts.map +1 -0
  84. package/build/services/previews/select.service.js +25 -0
  85. package/build/services/previews/select.service.js.map +1 -0
  86. package/build/services/previews/use-preview.service.d.ts +21 -0
  87. package/build/services/previews/use-preview.service.d.ts.map +1 -0
  88. package/build/services/previews/use-preview.service.js +24 -0
  89. package/build/services/previews/use-preview.service.js.map +1 -0
  90. package/build/services/previews/use-running.service.d.ts +4 -0
  91. package/build/services/previews/use-running.service.d.ts.map +1 -0
  92. package/build/services/previews/use-running.service.js +21 -0
  93. package/build/services/previews/use-running.service.js.map +1 -0
  94. package/build/services/publish/bundle.service.d.ts +11 -0
  95. package/build/services/publish/bundle.service.d.ts.map +1 -0
  96. package/build/services/publish/bundle.service.js +46 -0
  97. package/build/services/publish/bundle.service.js.map +1 -0
  98. package/build/services/publish/commands.service.d.ts +23 -0
  99. package/build/services/publish/commands.service.d.ts.map +1 -0
  100. package/build/services/publish/commands.service.js +130 -0
  101. package/build/services/publish/commands.service.js.map +1 -0
  102. package/build/services/publish/eas-env.service.d.ts +9 -0
  103. package/build/services/publish/eas-env.service.d.ts.map +1 -0
  104. package/build/services/publish/eas-env.service.js +49 -0
  105. package/build/services/publish/eas-env.service.js.map +1 -0
  106. package/build/services/registry/registry.service.d.ts +27 -0
  107. package/build/services/registry/registry.service.d.ts.map +1 -0
  108. package/build/services/registry/registry.service.js +89 -0
  109. package/build/services/registry/registry.service.js.map +1 -0
  110. package/build/services/s3/s3.service.d.ts +59 -0
  111. package/build/services/s3/s3.service.d.ts.map +1 -0
  112. package/build/services/s3/s3.service.js +154 -0
  113. package/build/services/s3/s3.service.js.map +1 -0
  114. package/build/services/start/start.service.d.ts +48 -0
  115. package/build/services/start/start.service.d.ts.map +1 -0
  116. package/build/services/start/start.service.js +172 -0
  117. package/build/services/start/start.service.js.map +1 -0
  118. package/build/stores/dev-preview-servers/dev-preview-servers.store.d.ts +21 -0
  119. package/build/stores/dev-preview-servers/dev-preview-servers.store.d.ts.map +1 -0
  120. package/build/stores/dev-preview-servers/dev-preview-servers.store.js +91 -0
  121. package/build/stores/dev-preview-servers/dev-preview-servers.store.js.map +1 -0
  122. package/build/stores/previews/previews.store.d.ts +36 -0
  123. package/build/stores/previews/previews.store.d.ts.map +1 -0
  124. package/build/stores/previews/previews.store.js +134 -0
  125. package/build/stores/previews/previews.store.js.map +1 -0
  126. package/build/utils/config/config.util.d.ts +39 -0
  127. package/build/utils/config/config.util.d.ts.map +1 -0
  128. package/build/utils/config/config.util.js +24 -0
  129. package/build/utils/config/config.util.js.map +1 -0
  130. package/build/utils/config/expo-config.util.d.ts +30 -0
  131. package/build/utils/config/expo-config.util.d.ts.map +1 -0
  132. package/build/utils/config/expo-config.util.js +43 -0
  133. package/build/utils/config/expo-config.util.js.map +1 -0
  134. package/build/utils/dev-preview-servers/merge-servers.util.d.ts +47 -0
  135. package/build/utils/dev-preview-servers/merge-servers.util.d.ts.map +1 -0
  136. package/build/utils/dev-preview-servers/merge-servers.util.js +37 -0
  137. package/build/utils/dev-preview-servers/merge-servers.util.js.map +1 -0
  138. package/build/utils/preview-list/theme.util.d.ts +5 -0
  139. package/build/utils/preview-list/theme.util.d.ts.map +1 -0
  140. package/build/utils/preview-list/theme.util.js +12 -0
  141. package/build/utils/preview-list/theme.util.js.map +1 -0
  142. package/build/utils/previews/layout.util.d.ts +89 -0
  143. package/build/utils/previews/layout.util.d.ts.map +1 -0
  144. package/build/utils/previews/layout.util.js +30 -0
  145. package/build/utils/previews/layout.util.js.map +1 -0
  146. package/build/utils/previews/preview-items.util.d.ts +23 -0
  147. package/build/utils/previews/preview-items.util.d.ts.map +1 -0
  148. package/build/utils/previews/preview-items.util.js +39 -0
  149. package/build/utils/previews/preview-items.util.js.map +1 -0
  150. package/build/utils/previews/preview-title.util.d.ts +2 -0
  151. package/build/utils/previews/preview-title.util.d.ts.map +1 -0
  152. package/build/utils/previews/preview-title.util.js +6 -0
  153. package/build/utils/previews/preview-title.util.js.map +1 -0
  154. package/build/utils/previews/running.util.d.ts +23 -0
  155. package/build/utils/previews/running.util.d.ts.map +1 -0
  156. package/build/utils/previews/running.util.js +23 -0
  157. package/build/utils/previews/running.util.js.map +1 -0
  158. package/build/utils/publish/changelog.util.d.ts +5 -0
  159. package/build/utils/publish/changelog.util.d.ts.map +1 -0
  160. package/build/utils/publish/changelog.util.js +19 -0
  161. package/build/utils/publish/changelog.util.js.map +1 -0
  162. package/build/utils/publish/indexes.util.d.ts +12 -0
  163. package/build/utils/publish/indexes.util.d.ts.map +1 -0
  164. package/build/utils/publish/indexes.util.js +52 -0
  165. package/build/utils/publish/indexes.util.js.map +1 -0
  166. package/build/utils/s3/sigv4.util.d.ts +24 -0
  167. package/build/utils/s3/sigv4.util.d.ts.map +1 -0
  168. package/build/utils/s3/sigv4.util.js +67 -0
  169. package/build/utils/s3/sigv4.util.js.map +1 -0
  170. package/expo-module.config.json +10 -0
  171. package/ios/AxonpackPreview.podspec +30 -0
  172. package/ios/AxonpackPreviewModule.swift +55 -0
  173. package/ios/PreviewReactDelegateHandler.swift +147 -0
  174. package/ios/PreviewState.swift +236 -0
  175. package/notes/README.md +36 -0
  176. package/notes/bundles.md +35 -0
  177. package/notes/dev-preview-servers.md +67 -0
  178. package/notes/distribution.md +138 -0
  179. package/notes/native-loader.md +56 -0
  180. package/notes/storage.md +145 -0
  181. package/notes/testing.md +97 -0
  182. package/package.json +107 -4
@@ -0,0 +1,106 @@
1
+ import { Platform as RNPlatform } from "react-native";
2
+ import { DEFAULT_PORT_RANGE, DEFAULT_REGISTRY_PORT, } from "../../constants/dev-preview-servers/dev-preview-servers.const";
3
+ // Generous on purpose. Android's HTTP client runs 5 requests per host at a time, so most of the
4
+ // 19 probes wait in its queue, and the emulator's NAT takes about 400 ms even to refuse a closed
5
+ // port. A tighter limit dropped live servers on the emulator. A closed port on a real computer
6
+ // still fails at once, so a round only gets slow on a host that drops packets.
7
+ const PROBE_MS = 2000;
8
+ /**
9
+ * Runs one request, body read included, and aborts it after `ms`. The timer is cleared once the
10
+ * request settles, so a fast answer leaves nothing pending: a poll round sends 20 of these.
11
+ */
12
+ async function within(ms, request) {
13
+ const controller = new AbortController();
14
+ const timer = setTimeout(() => controller.abort(), ms);
15
+ try {
16
+ return await request(controller.signal);
17
+ }
18
+ finally {
19
+ clearTimeout(timer);
20
+ }
21
+ }
22
+ async function isMetro(host, port) {
23
+ try {
24
+ const text = await within(PROBE_MS, async (signal) => (await fetch(`http://${host}:${port}/status`, { signal })).text());
25
+ return text.includes("packager-status:running");
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ /** The Expo manifest a dev server gives for this platform: its bundle URL and app config. */
32
+ async function readManifest(host, port, platform) {
33
+ const manifest = await within(3000, async (signal) => (await fetch(`http://${host}:${port}/`, {
34
+ headers: { "expo-platform": platform, accept: "application/json" },
35
+ signal,
36
+ })).json());
37
+ return {
38
+ url: String(manifest?.launchAsset?.url ?? ""),
39
+ expo: (manifest?.extra?.expoClient ?? {}),
40
+ };
41
+ }
42
+ async function readRegistry(host, port) {
43
+ try {
44
+ const body = await within(PROBE_MS, async (signal) => (await fetch(`http://${host}:${port}/servers`, { signal })).json());
45
+ return body.servers;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /** The host the app's own Metro is on, which is where its other servers are too. */
52
+ function devServerHost() {
53
+ try {
54
+ const getDevServer = require("react-native/Libraries/Core/Devtools/getDevServer").default;
55
+ return new URL(getDevServer().url).hostname;
56
+ }
57
+ catch {
58
+ return "localhost";
59
+ }
60
+ }
61
+ /**
62
+ * Where to look. On Android, 10.0.2.2 is the computer as the emulator sees it, so plain servers
63
+ * are found there without an `adb reverse` per port.
64
+ */
65
+ export function candidateHosts() {
66
+ const hosts = [devServerHost()];
67
+ if (RNPlatform.OS === "android")
68
+ hosts.push("10.0.2.2");
69
+ return [...new Set(hosts)];
70
+ }
71
+ const manifests = new Map();
72
+ /** One look: every Metro in the range on the candidate hosts, plus the registry. */
73
+ export async function discover(platform) {
74
+ const [from, to] = DEFAULT_PORT_RANGE;
75
+ const hosts = candidateHosts();
76
+ let registry = [];
77
+ for (const host of hosts) {
78
+ const found = await readRegistry(host, DEFAULT_REGISTRY_PORT);
79
+ if (found) {
80
+ registry = found;
81
+ break;
82
+ }
83
+ }
84
+ const ports = new Set(registry.map((r) => r.port));
85
+ for (let port = from; port <= to; port++)
86
+ ports.add(port);
87
+ const scanned = [];
88
+ await Promise.all([...ports].map(async (port) => {
89
+ for (const host of hosts) {
90
+ if (!(await isMetro(host, port)))
91
+ continue;
92
+ const id = `${host}:${port}`;
93
+ // Read once per server; a closed port drops its entry so a new server there is read fresh.
94
+ if (!manifests.has(id))
95
+ manifests.set(id, readManifest(host, port, platform).catch(() => null));
96
+ const manifest = await manifests.get(id);
97
+ if (manifest?.url)
98
+ scanned.push({ host, port, ...manifest });
99
+ return;
100
+ }
101
+ for (const host of hosts)
102
+ manifests.delete(`${host}:${port}`);
103
+ }));
104
+ return { registry, scanned };
105
+ }
106
+ //# sourceMappingURL=discover.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.service.js","sourceRoot":"","sources":["../../../src/services/dev-preview-servers/discover.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform as RNPlatform } from \"react-native\";\n\nimport type { Platform } from \"../../utils/previews/layout.util\";\nimport {\n DEFAULT_PORT_RANGE,\n DEFAULT_REGISTRY_PORT,\n type RegisteredServer,\n} from \"../../constants/dev-preview-servers/dev-preview-servers.const\";\nimport type { ScannedServer } from \"../../utils/dev-preview-servers/merge-servers.util\";\n\n// Generous on purpose. Android's HTTP client runs 5 requests per host at a time, so most of the\n// 19 probes wait in its queue, and the emulator's NAT takes about 400 ms even to refuse a closed\n// port. A tighter limit dropped live servers on the emulator. A closed port on a real computer\n// still fails at once, so a round only gets slow on a host that drops packets.\nconst PROBE_MS = 2000;\n\n/**\n * Runs one request, body read included, and aborts it after `ms`. The timer is cleared once the\n * request settles, so a fast answer leaves nothing pending: a poll round sends 20 of these.\n */\nasync function within<T>(\n ms: number,\n request: (signal: AbortSignal) => Promise<T>,\n): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), ms);\n try {\n return await request(controller.signal);\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function isMetro(host: string, port: number): Promise<boolean> {\n try {\n const text = await within(PROBE_MS, async (signal) =>\n (await fetch(`http://${host}:${port}/status`, { signal })).text(),\n );\n return text.includes(\"packager-status:running\");\n } catch {\n return false;\n }\n}\n\n/** The Expo manifest a dev server gives for this platform: its bundle URL and app config. */\nasync function readManifest(host: string, port: number, platform: Platform) {\n const manifest = await within(3000, async (signal) =>\n (\n await fetch(`http://${host}:${port}/`, {\n headers: { \"expo-platform\": platform, accept: \"application/json\" },\n signal,\n })\n ).json(),\n );\n return {\n url: String(manifest?.launchAsset?.url ?? \"\"),\n expo: (manifest?.extra?.expoClient ?? {}) as ScannedServer[\"expo\"],\n };\n}\n\nasync function readRegistry(\n host: string,\n port: number,\n): Promise<RegisteredServer[] | null> {\n try {\n const body = await within(PROBE_MS, async (signal) =>\n (await fetch(`http://${host}:${port}/servers`, { signal })).json(),\n );\n return (body as { servers: RegisteredServer[] }).servers;\n } catch {\n return null;\n }\n}\n\n/** The host the app's own Metro is on, which is where its other servers are too. */\nfunction devServerHost(): string {\n try {\n const getDevServer =\n require(\"react-native/Libraries/Core/Devtools/getDevServer\").default;\n return new URL(getDevServer().url).hostname;\n } catch {\n return \"localhost\";\n }\n}\n\n/**\n * Where to look. On Android, 10.0.2.2 is the computer as the emulator sees it, so plain servers\n * are found there without an `adb reverse` per port.\n */\nexport function candidateHosts(): string[] {\n const hosts = [devServerHost()];\n if (RNPlatform.OS === \"android\") hosts.push(\"10.0.2.2\");\n return [...new Set(hosts)];\n}\n\nconst manifests = new Map<\n string,\n Promise<Awaited<ReturnType<typeof readManifest>> | null>\n>();\n\n/** One look: every Metro in the range on the candidate hosts, plus the registry. */\nexport async function discover(\n platform: Platform,\n): Promise<{ registry: RegisteredServer[]; scanned: ScannedServer[] }> {\n const [from, to] = DEFAULT_PORT_RANGE;\n const hosts = candidateHosts();\n let registry: RegisteredServer[] = [];\n for (const host of hosts) {\n const found = await readRegistry(host, DEFAULT_REGISTRY_PORT);\n if (found) {\n registry = found;\n break;\n }\n }\n const ports = new Set<number>(registry.map((r) => r.port));\n for (let port = from; port <= to; port++) ports.add(port);\n\n const scanned: ScannedServer[] = [];\n await Promise.all(\n [...ports].map(async (port) => {\n for (const host of hosts) {\n if (!(await isMetro(host, port))) continue;\n const id = `${host}:${port}`;\n // Read once per server; a closed port drops its entry so a new server there is read fresh.\n if (!manifests.has(id))\n manifests.set(\n id,\n readManifest(host, port, platform).catch(() => null),\n );\n const manifest = await manifests.get(id);\n if (manifest?.url) scanned.push({ host, port, ...manifest });\n return;\n }\n for (const host of hosts) manifests.delete(`${host}:${port}`);\n }),\n );\n return { registry, scanned };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { Compatibility } from "../../utils/previews/layout.util";
2
+ /**
3
+ * The key this native build lists previews under, as the config plugin baked it in at prebuild:
4
+ * `version-<app version>` or `fingerprint-<hash>`. Null without the plugin or the native module.
5
+ */
6
+ export declare function buildKey(): string | null;
7
+ /** The compatibility setting the build was made with, for showing; null without the plugin. */
8
+ export declare function buildCompatibility(): Compatibility | null;
9
+ /** The app's Expo slug, baked in by the config plugin; it tells this app's dev preview servers apart. */
10
+ export declare function buildSlug(): string | null;
11
+ //# sourceMappingURL=build-info.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-info.service.d.ts","sourceRoot":"","sources":["../../../src/services/native/build-info.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAQtE;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAIxC;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,IAAI,aAAa,GAAG,IAAI,CAIzD;AAED,yGAAyG;AACzG,wBAAgB,SAAS,IAAI,MAAM,GAAG,IAAI,CAIzC"}
@@ -0,0 +1,18 @@
1
+ import { requireOptionalNativeModule } from "expo";
2
+ /**
3
+ * The key this native build lists previews under, as the config plugin baked it in at prebuild:
4
+ * `version-<app version>` or `fingerprint-<hash>`. Null without the plugin or the native module.
5
+ */
6
+ export function buildKey() {
7
+ return (requireOptionalNativeModule("AxonpackPreview")?.key ?? null);
8
+ }
9
+ /** The compatibility setting the build was made with, for showing; null without the plugin. */
10
+ export function buildCompatibility() {
11
+ const value = requireOptionalNativeModule("AxonpackPreview")?.compatibility;
12
+ return value === "appVersion" || value === "fingerprint" ? value : null;
13
+ }
14
+ /** The app's Expo slug, baked in by the config plugin; it tells this app's dev preview servers apart. */
15
+ export function buildSlug() {
16
+ return (requireOptionalNativeModule("AxonpackPreview")?.slug ?? null);
17
+ }
18
+ //# sourceMappingURL=build-info.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-info.service.js","sourceRoot":"","sources":["../../../src/services/native/build-info.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,MAAM,CAAC;AAUnD;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,CACL,2BAA2B,CAAe,iBAAiB,CAAC,EAAE,GAAG,IAAI,IAAI,CAC1E,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,kBAAkB;IAChC,MAAM,KAAK,GACT,2BAA2B,CAAe,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAC9E,OAAO,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,2BAA2B,CAAe,iBAAiB,CAAC,EAAE,IAAI,IAAI,IAAI,CAC3E,CAAC;AACJ,CAAC","sourcesContent":["import { requireOptionalNativeModule } from \"expo\";\n\nimport type { Compatibility } from \"../../utils/previews/layout.util\";\n\ntype NativeModule = {\n compatibility?: string | null;\n key?: string | null;\n slug?: string | null;\n};\n\n/**\n * The key this native build lists previews under, as the config plugin baked it in at prebuild:\n * `version-<app version>` or `fingerprint-<hash>`. Null without the plugin or the native module.\n */\nexport function buildKey(): string | null {\n return (\n requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\")?.key ?? null\n );\n}\n\n/** The compatibility setting the build was made with, for showing; null without the plugin. */\nexport function buildCompatibility(): Compatibility | null {\n const value =\n requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\")?.compatibility;\n return value === \"appVersion\" || value === \"fingerprint\" ? value : null;\n}\n\n/** The app's Expo slug, baked in by the config plugin; it tells this app's dev preview servers apart. */\nexport function buildSlug(): string | null {\n return (\n requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\")?.slug ?? null\n );\n}\n"]}
@@ -0,0 +1,24 @@
1
+ export type NativeState = {
2
+ bundle?: string | null;
3
+ broken?: string[];
4
+ /** The chosen local Metro server's bundle URL (debug builds only). */
5
+ localServer?: string | null;
6
+ };
7
+ type NativeModule = {
8
+ select(bundle: string | null, key: string | null): void;
9
+ reload(): void;
10
+ markLaunched(): void;
11
+ selectLocalServer(url: string | null, hostPort: string | null): void;
12
+ state(): NativeState;
13
+ };
14
+ /** Undefined where the module isn't linked (Expo Go, web), and the package then does nothing. */
15
+ export declare const nativeModule: () => NativeModule | undefined;
16
+ /** The id of the preview the next start loads, from `<id>/<platform>/<bundle>`. */
17
+ export declare const selectedPreviewId: (state: NativeState) => string | null;
18
+ /**
19
+ * Tells the native side this start worked. Until it is called, a crash makes the next launch fall
20
+ * back to the base bundle and mark the preview broken. The provider calls it once mounted.
21
+ */
22
+ export declare function markLaunched(): void;
23
+ export {};
24
+ //# sourceMappingURL=native.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.service.d.ts","sourceRoot":"","sources":["../../../src/services/native/native.service.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACxD,MAAM,IAAI,IAAI,CAAC;IACf,YAAY,IAAI,IAAI,CAAC;IACrB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACrE,KAAK,IAAI,WAAW,CAAC;CACtB,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,YAAY,QAAO,YAAY,GAAG,SAC4B,CAAC;AAE5E,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,KAAG,MAAM,GAAG,IACd,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -0,0 +1,13 @@
1
+ import { requireOptionalNativeModule } from "expo";
2
+ /** Undefined where the module isn't linked (Expo Go, web), and the package then does nothing. */
3
+ export const nativeModule = () => requireOptionalNativeModule("AxonpackPreview") ?? undefined;
4
+ /** The id of the preview the next start loads, from `<id>/<platform>/<bundle>`. */
5
+ export const selectedPreviewId = (state) => state.bundle ? state.bundle.split("/")[0] : null;
6
+ /**
7
+ * Tells the native side this start worked. Until it is called, a crash makes the next launch fall
8
+ * back to the base bundle and mark the preview broken. The provider calls it once mounted.
9
+ */
10
+ export function markLaunched() {
11
+ nativeModule()?.markLaunched();
12
+ }
13
+ //# sourceMappingURL=native.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.service.js","sourceRoot":"","sources":["../../../src/services/native/native.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,MAAM,CAAC;AAiBnD,iGAAiG;AACjG,MAAM,CAAC,MAAM,YAAY,GAAG,GAA6B,EAAE,CACzD,2BAA2B,CAAe,iBAAiB,CAAC,IAAI,SAAS,CAAC;AAE5E,mFAAmF;AACnF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAkB,EAAiB,EAAE,CACrE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,YAAY,EAAE,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC","sourcesContent":["import { requireOptionalNativeModule } from \"expo\";\n\nexport type NativeState = {\n bundle?: string | null;\n broken?: string[];\n /** The chosen local Metro server's bundle URL (debug builds only). */\n localServer?: string | null;\n};\n\ntype NativeModule = {\n select(bundle: string | null, key: string | null): void;\n reload(): void;\n markLaunched(): void;\n selectLocalServer(url: string | null, hostPort: string | null): void;\n state(): NativeState;\n};\n\n/** Undefined where the module isn't linked (Expo Go, web), and the package then does nothing. */\nexport const nativeModule = (): NativeModule | undefined =>\n requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\") ?? undefined;\n\n/** The id of the preview the next start loads, from `<id>/<platform>/<bundle>`. */\nexport const selectedPreviewId = (state: NativeState): string | null =>\n state.bundle ? state.bundle.split(\"/\")[0] : null;\n\n/**\n * Tells the native side this start worked. Until it is called, a crash makes the next launch fall\n * back to the base bundle and mark the preview broken. The provider calls it once mounted.\n */\nexport function markLaunched(): void {\n nativeModule()?.markLaunched();\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { IndexEntry } from "../../utils/previews/layout.util";
2
+ import { PreviewConfig } from "../../utils/config/config.util";
3
+ import { LocalPreview } from "./local-previews.service";
4
+ export type DownloadProgress = {
5
+ bytes: number;
6
+ totalBytes: number;
7
+ };
8
+ /**
9
+ * Downloads a preview into `preview/<id>/<platform>/`. Files go into a hidden folder first
10
+ * and every one is checked against its sha256; only a complete, checked folder is renamed into
11
+ * place. So an interrupted or corrupt download never looks like a preview.
12
+ */
13
+ export declare function downloadPreview(config: PreviewConfig, entry: IndexEntry, onProgress?: (progress: DownloadProgress) => void): Promise<LocalPreview>;
14
+ //# sourceMappingURL=download.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/download.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAW,MAAM,kCAAkC,CAAC;AAGvE,OAAO,EACL,aAAa,EAGd,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,YAAY,EAIb,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,UAAU,EACjB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,GAChD,OAAO,CAAC,YAAY,CAAC,CAqDvB"}
@@ -0,0 +1,66 @@
1
+ import { Directory, File } from "expo-file-system";
2
+ import { fileKey } from "../../utils/previews/layout.util";
3
+ import { s3Fetch } from "../s3/s3.service";
4
+ import { sha256Hex } from "../../utils/s3/sigv4.util";
5
+ import { currentPlatform, toS3Config, } from "../../utils/config/config.util";
6
+ import { previewDirectory, rootDirectory, writeLocalPreview, } from "./local-previews.service";
7
+ import { PreviewError, fetchManifest } from "./remote.service";
8
+ /**
9
+ * Downloads a preview into `preview/<id>/<platform>/`. Files go into a hidden folder first
10
+ * and every one is checked against its sha256; only a complete, checked folder is renamed into
11
+ * place. So an interrupted or corrupt download never looks like a preview.
12
+ */
13
+ export async function downloadPreview(config, entry, onProgress) {
14
+ const platform = currentPlatform();
15
+ if (!platform)
16
+ throw new PreviewError("Previews run on Android and iOS only.");
17
+ const s3 = toS3Config(config);
18
+ const manifest = await fetchManifest(config, entry.manifest);
19
+ const totalBytes = manifest.files.reduce((sum, f) => sum + f.size, 0);
20
+ const parent = new Directory(rootDirectory(), entry.id);
21
+ const partial = new Directory(parent, `.${platform}-partial`);
22
+ if (partial.exists)
23
+ partial.delete();
24
+ partial.create({ intermediates: true });
25
+ try {
26
+ let bytes = 0;
27
+ onProgress?.({ bytes, totalBytes });
28
+ for (const file of manifest.files) {
29
+ const res = await s3Fetch(s3, {
30
+ method: "GET",
31
+ key: fileKey(file.sha256),
32
+ });
33
+ if (!res.ok)
34
+ throw new PreviewError(`Download of ${file.path} failed (${res.status}).`);
35
+ const data = new Uint8Array(await res.arrayBuffer());
36
+ if (sha256Hex(data) !== file.sha256) {
37
+ throw new PreviewError(`${file.path} did not match its checksum.`);
38
+ }
39
+ const target = new File(partial, ...file.path.split("/"));
40
+ target.create({ intermediates: true, overwrite: true });
41
+ await target.write(data);
42
+ bytes += file.size;
43
+ onProgress?.({ bytes, totalBytes });
44
+ }
45
+ const destination = previewDirectory(entry.id, platform);
46
+ if (destination.exists)
47
+ destination.delete();
48
+ partial.rename(platform);
49
+ }
50
+ catch (error) {
51
+ if (partial.exists)
52
+ partial.delete();
53
+ throw error;
54
+ }
55
+ const local = {
56
+ id: entry.id,
57
+ platform,
58
+ key: manifest.key,
59
+ revision: entry.revision,
60
+ downloadedAt: new Date().toISOString(),
61
+ entry,
62
+ };
63
+ await writeLocalPreview(local);
64
+ return local;
65
+ }
66
+ //# sourceMappingURL=download.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.service.js","sourceRoot":"","sources":["../../../src/services/previews/download.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Directory, File } from \"expo-file-system\";\n\nimport { IndexEntry, fileKey } from \"../../utils/previews/layout.util\";\nimport { s3Fetch } from \"../s3/s3.service\";\nimport { sha256Hex } from \"../../utils/s3/sigv4.util\";\nimport {\n PreviewConfig,\n currentPlatform,\n toS3Config,\n} from \"../../utils/config/config.util\";\nimport {\n LocalPreview,\n previewDirectory,\n rootDirectory,\n writeLocalPreview,\n} from \"./local-previews.service\";\nimport { PreviewError, fetchManifest } from \"./remote.service\";\n\nexport type DownloadProgress = { bytes: number; totalBytes: number };\n\n/**\n * Downloads a preview into `preview/<id>/<platform>/`. Files go into a hidden folder first\n * and every one is checked against its sha256; only a complete, checked folder is renamed into\n * place. So an interrupted or corrupt download never looks like a preview.\n */\nexport async function downloadPreview(\n config: PreviewConfig,\n entry: IndexEntry,\n onProgress?: (progress: DownloadProgress) => void,\n): Promise<LocalPreview> {\n const platform = currentPlatform();\n if (!platform)\n throw new PreviewError(\"Previews run on Android and iOS only.\");\n const s3 = toS3Config(config);\n const manifest = await fetchManifest(config, entry.manifest);\n const totalBytes = manifest.files.reduce((sum, f) => sum + f.size, 0);\n\n const parent = new Directory(rootDirectory(), entry.id);\n const partial = new Directory(parent, `.${platform}-partial`);\n if (partial.exists) partial.delete();\n partial.create({ intermediates: true });\n\n try {\n let bytes = 0;\n onProgress?.({ bytes, totalBytes });\n for (const file of manifest.files) {\n const res = await s3Fetch(s3, {\n method: \"GET\",\n key: fileKey(file.sha256),\n });\n if (!res.ok)\n throw new PreviewError(\n `Download of ${file.path} failed (${res.status}).`,\n );\n const data = new Uint8Array(await res.arrayBuffer());\n if (sha256Hex(data) !== file.sha256) {\n throw new PreviewError(`${file.path} did not match its checksum.`);\n }\n const target = new File(partial, ...file.path.split(\"/\"));\n target.create({ intermediates: true, overwrite: true });\n await target.write(data);\n bytes += file.size;\n onProgress?.({ bytes, totalBytes });\n }\n const destination = previewDirectory(entry.id, platform);\n if (destination.exists) destination.delete();\n partial.rename(platform);\n } catch (error) {\n if (partial.exists) partial.delete();\n throw error;\n }\n\n const local: LocalPreview = {\n id: entry.id,\n platform,\n key: manifest.key,\n revision: entry.revision,\n downloadedAt: new Date().toISOString(),\n entry,\n };\n await writeLocalPreview(local);\n return local;\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { Directory } from "expo-file-system";
2
+ import type { IndexEntry, Platform } from "../../utils/previews/layout.util";
3
+ /**
4
+ * A downloaded preview, as recorded next to its files. It keeps the index entry it was
5
+ * downloaded from, so the list still has a title and changelog after the preview is unpublished.
6
+ */
7
+ export type LocalPreview = {
8
+ id: string;
9
+ platform: Platform;
10
+ /** The build key it was published for; it runs only on a build with the same key. */
11
+ key: string;
12
+ revision: string;
13
+ downloadedAt: string;
14
+ entry: IndexEntry;
15
+ };
16
+ /** The same folder the native side reads: Documents on iOS, the files folder on Android. */
17
+ export declare const rootDirectory: () => Directory;
18
+ export declare const previewDirectory: (id: string, platform: Platform) => Directory;
19
+ export declare function writeLocalPreview(preview: LocalPreview): Promise<void>;
20
+ export declare function readLocalPreview(id: string, platform: Platform): Promise<LocalPreview | null>;
21
+ /** Every downloaded preview for this platform, whatever build it was made for. */
22
+ export declare function listLocalPreviews(platform: Platform): Promise<LocalPreview[]>;
23
+ /** Removes the download's files and its record. The folder for the id goes once it is empty. */
24
+ export declare function deleteLocalPreview(id: string, platform: Platform): void;
25
+ //# sourceMappingURL=local-previews.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-previews.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/local-previews.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAe,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE7E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,qFAAqF;IACrF,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,aAAa,iBAAiD,CAAC;AAE5E,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,EAAE,UAAU,QAAQ,cACjB,CAAC;AAK/C,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAQ9B;AAED,kFAAkF;AAClF,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAUzB;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAOvE"}
@@ -0,0 +1,50 @@
1
+ import { Directory, File, Paths } from "expo-file-system";
2
+ /** The same folder the native side reads: Documents on iOS, the files folder on Android. */
3
+ export const rootDirectory = () => new Directory(Paths.document, "preview");
4
+ export const previewDirectory = (id, platform) => new Directory(rootDirectory(), id, platform);
5
+ const metaFile = (id, platform) => new File(rootDirectory(), id, `${platform}.json`);
6
+ export async function writeLocalPreview(preview) {
7
+ const file = metaFile(preview.id, preview.platform);
8
+ if (!file.exists)
9
+ file.create({ intermediates: true });
10
+ await file.write(JSON.stringify(preview));
11
+ }
12
+ export async function readLocalPreview(id, platform) {
13
+ const file = metaFile(id, platform);
14
+ if (!file.exists || !previewDirectory(id, platform).exists)
15
+ return null;
16
+ try {
17
+ return JSON.parse(await file.text());
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ /** Every downloaded preview for this platform, whatever build it was made for. */
24
+ export async function listLocalPreviews(platform) {
25
+ const root = rootDirectory();
26
+ if (!root.exists)
27
+ return [];
28
+ const previews = [];
29
+ for (const item of root.list()) {
30
+ if (!(item instanceof Directory) || item.name.startsWith("."))
31
+ continue;
32
+ const preview = await readLocalPreview(item.name, platform);
33
+ if (preview)
34
+ previews.push(preview);
35
+ }
36
+ return previews;
37
+ }
38
+ /** Removes the download's files and its record. The folder for the id goes once it is empty. */
39
+ export function deleteLocalPreview(id, platform) {
40
+ const folder = previewDirectory(id, platform);
41
+ if (folder.exists)
42
+ folder.delete();
43
+ const meta = metaFile(id, platform);
44
+ if (meta.exists)
45
+ meta.delete();
46
+ const parent = new Directory(rootDirectory(), id);
47
+ if (parent.exists && parent.list().length === 0)
48
+ parent.delete();
49
+ }
50
+ //# sourceMappingURL=local-previews.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-previews.service.js","sourceRoot":"","sources":["../../../src/services/previews/local-previews.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\n\nimport type { IndexEntry, Platform } from \"../../utils/previews/layout.util\";\n\n/**\n * A downloaded preview, as recorded next to its files. It keeps the index entry it was\n * downloaded from, so the list still has a title and changelog after the preview is unpublished.\n */\nexport type LocalPreview = {\n id: string;\n platform: Platform;\n /** The build key it was published for; it runs only on a build with the same key. */\n key: string;\n revision: string;\n downloadedAt: string;\n entry: IndexEntry;\n};\n\n/** The same folder the native side reads: Documents on iOS, the files folder on Android. */\nexport const rootDirectory = () => new Directory(Paths.document, \"preview\");\n\nexport const previewDirectory = (id: string, platform: Platform) =>\n new Directory(rootDirectory(), id, platform);\n\nconst metaFile = (id: string, platform: Platform) =>\n new File(rootDirectory(), id, `${platform}.json`);\n\nexport async function writeLocalPreview(preview: LocalPreview): Promise<void> {\n const file = metaFile(preview.id, preview.platform);\n if (!file.exists) file.create({ intermediates: true });\n await file.write(JSON.stringify(preview));\n}\n\nexport async function readLocalPreview(\n id: string,\n platform: Platform,\n): Promise<LocalPreview | null> {\n const file = metaFile(id, platform);\n if (!file.exists || !previewDirectory(id, platform).exists) return null;\n try {\n return JSON.parse(await file.text()) as LocalPreview;\n } catch {\n return null;\n }\n}\n\n/** Every downloaded preview for this platform, whatever build it was made for. */\nexport async function listLocalPreviews(\n platform: Platform,\n): Promise<LocalPreview[]> {\n const root = rootDirectory();\n if (!root.exists) return [];\n const previews: LocalPreview[] = [];\n for (const item of root.list()) {\n if (!(item instanceof Directory) || item.name.startsWith(\".\")) continue;\n const preview = await readLocalPreview(item.name, platform);\n if (preview) previews.push(preview);\n }\n return previews;\n}\n\n/** Removes the download's files and its record. The folder for the id goes once it is empty. */\nexport function deleteLocalPreview(id: string, platform: Platform): void {\n const folder = previewDirectory(id, platform);\n if (folder.exists) folder.delete();\n const meta = metaFile(id, platform);\n if (meta.exists) meta.delete();\n const parent = new Directory(rootDirectory(), id);\n if (parent.exists && parent.list().length === 0) parent.delete();\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Restarts JS. The native side picks the bundle again on the way up, so this is how a new choice
3
+ * of preview takes effect. False where the module isn't linked (Expo Go, web).
4
+ */
5
+ export declare function reload(): boolean;
6
+ //# sourceMappingURL=reload.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/reload.service.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAKhC"}
@@ -0,0 +1,13 @@
1
+ import { requireOptionalNativeModule } from "expo";
2
+ /**
3
+ * Restarts JS. The native side picks the bundle again on the way up, so this is how a new choice
4
+ * of preview takes effect. False where the module isn't linked (Expo Go, web).
5
+ */
6
+ export function reload() {
7
+ const native = requireOptionalNativeModule("AxonpackPreview");
8
+ if (!native)
9
+ return false;
10
+ native.reload();
11
+ return true;
12
+ }
13
+ //# sourceMappingURL=reload.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.service.js","sourceRoot":"","sources":["../../../src/services/previews/reload.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,MAAM,CAAC;AAInD;;;GAGG;AACH,MAAM,UAAU,MAAM;IACpB,MAAM,MAAM,GAAG,2BAA2B,CAAe,iBAAiB,CAAC,CAAC;IAC5E,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,CAAC;IAChB,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { requireOptionalNativeModule } from \"expo\";\n\ntype NativeModule = { reload(): void };\n\n/**\n * Restarts JS. The native side picks the bundle again on the way up, so this is how a new choice\n * of preview takes effect. False where the module isn't linked (Expo Go, web).\n */\nexport function reload(): boolean {\n const native = requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\");\n if (!native) return false;\n native.reload();\n return true;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { Manifest, PlatformIndex } from "../../utils/previews/layout.util";
2
+ import { PreviewConfig } from "../../utils/config/config.util";
3
+ export declare class PreviewError extends Error {
4
+ }
5
+ /**
6
+ * The previews published for this exact native build. An empty list when nothing has been
7
+ * published for it yet. Throws when the build cannot be identified or the bucket says no.
8
+ */
9
+ export declare function fetchPreviews(config: PreviewConfig): Promise<PlatformIndex>;
10
+ export declare function fetchManifest(config: PreviewConfig, key: string): Promise<Manifest>;
11
+ //# sourceMappingURL=remote.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/remote.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,aAAa,EAGd,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,aAAa,EAId,MAAM,gCAAgC,CAAC;AAExC,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C;;;GAGG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CAwBxB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,QAAQ,CAAC,CAInB"}
@@ -0,0 +1,34 @@
1
+ import { compatibilityOfKey, platformIndexKey, } from "../../utils/previews/layout.util";
2
+ import { getJson } from "../s3/s3.service";
3
+ import { currentPlatform, resolveBuildKey, toS3Config, } from "../../utils/config/config.util";
4
+ export class PreviewError extends Error {
5
+ }
6
+ /**
7
+ * The previews published for this exact native build. An empty list when nothing has been
8
+ * published for it yet. Throws when the build cannot be identified or the bucket says no.
9
+ */
10
+ export async function fetchPreviews(config) {
11
+ const platform = currentPlatform();
12
+ const key = resolveBuildKey(config);
13
+ if (!platform)
14
+ throw new PreviewError("Previews run on Android and iOS only.");
15
+ if (!key) {
16
+ throw new PreviewError('This build has no compatibility key. Add "@axonpack/preview" to the app config plugins and rebuild, or pass `buildKey`.');
17
+ }
18
+ const found = await getJson(toS3Config(config), platformIndexKey(platform, key));
19
+ return (found?.data ?? {
20
+ schema: 1,
21
+ platform,
22
+ key,
23
+ compatibility: compatibilityOfKey(key),
24
+ updatedAt: "",
25
+ previews: [],
26
+ });
27
+ }
28
+ export async function fetchManifest(config, key) {
29
+ const found = await getJson(toS3Config(config), key);
30
+ if (!found)
31
+ throw new PreviewError(`The preview's manifest ${key} is gone.`);
32
+ return found.data;
33
+ }
34
+ //# sourceMappingURL=remote.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.service.js","sourceRoot":"","sources":["../../../src/services/previews/remote.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAEL,eAAe,EACf,eAAe,EACf,UAAU,GACX,MAAM,gCAAgC,CAAC;AAExC,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG;AAE1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAqB;IAErB,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,YAAY,CAAC,uCAAuC,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CACpB,yHAAyH,CAC1H,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,OAAO,CACzB,UAAU,CAAC,MAAM,CAAC,EAClB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAChC,CAAC;IACF,OAAO,CACL,KAAK,EAAE,IAAI,IAAI;QACb,MAAM,EAAE,CAAC;QACT,QAAQ;QACR,GAAG;QACH,aAAa,EAAE,kBAAkB,CAAC,GAAG,CAAC;QACtC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,EAAE;KACb,CACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAqB,EACrB,GAAW;IAEX,MAAM,KAAK,GAAG,MAAM,OAAO,CAAW,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,YAAY,CAAC,0BAA0B,GAAG,WAAW,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC","sourcesContent":["import {\n Manifest,\n PlatformIndex,\n compatibilityOfKey,\n platformIndexKey,\n} from \"../../utils/previews/layout.util\";\nimport { getJson } from \"../s3/s3.service\";\nimport {\n PreviewConfig,\n currentPlatform,\n resolveBuildKey,\n toS3Config,\n} from \"../../utils/config/config.util\";\n\nexport class PreviewError extends Error {}\n\n/**\n * The previews published for this exact native build. An empty list when nothing has been\n * published for it yet. Throws when the build cannot be identified or the bucket says no.\n */\nexport async function fetchPreviews(\n config: PreviewConfig,\n): Promise<PlatformIndex> {\n const platform = currentPlatform();\n const key = resolveBuildKey(config);\n if (!platform)\n throw new PreviewError(\"Previews run on Android and iOS only.\");\n if (!key) {\n throw new PreviewError(\n 'This build has no compatibility key. Add \"@axonpack/preview\" to the app config plugins and rebuild, or pass `buildKey`.',\n );\n }\n const found = await getJson<PlatformIndex>(\n toS3Config(config),\n platformIndexKey(platform, key),\n );\n return (\n found?.data ?? {\n schema: 1,\n platform,\n key,\n compatibility: compatibilityOfKey(key),\n updatedAt: \"\",\n previews: [],\n }\n );\n}\n\nexport async function fetchManifest(\n config: PreviewConfig,\n key: string,\n): Promise<Manifest> {\n const found = await getJson<Manifest>(toS3Config(config), key);\n if (!found) throw new PreviewError(`The preview's manifest ${key} is gone.`);\n return found.data;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /** Where a preview's bundle sits, relative to the preview folder. */
2
+ export declare const previewBundlePath: (id: string) => string;
3
+ /**
4
+ * Chooses what the next start or `reload()` loads: a preview by id, or null for the base bundle.
5
+ * `key` is the build key the preview was published for; with it, a later build with another key
6
+ * loads the base bundle instead.
7
+ * A preview that is not on the device falls back to the base bundle. False where the native module
8
+ * isn't linked.
9
+ */
10
+ export declare function selectPreview(id: string | null, key?: string): boolean;
11
+ //# sourceMappingURL=select.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/select.service.ts"],"names":[],"mappings":"AAYA,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAAI,IAAI,MAAM,KAAG,MACR,CAAC;AAExC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAQtE"}
@@ -0,0 +1,25 @@
1
+ import { requireOptionalNativeModule } from "expo";
2
+ import { Platform } from "react-native";
3
+ // What `expo export:embed` names the bundle on each platform, so a preview folder holds exactly
4
+ // what the native build would embed.
5
+ const BUNDLE_FILE = Platform.select({
6
+ ios: "main.jsbundle",
7
+ default: "index.android.bundle",
8
+ });
9
+ /** Where a preview's bundle sits, relative to the preview folder. */
10
+ export const previewBundlePath = (id) => `${id}/${Platform.OS}/${BUNDLE_FILE}`;
11
+ /**
12
+ * Chooses what the next start or `reload()` loads: a preview by id, or null for the base bundle.
13
+ * `key` is the build key the preview was published for; with it, a later build with another key
14
+ * loads the base bundle instead.
15
+ * A preview that is not on the device falls back to the base bundle. False where the native module
16
+ * isn't linked.
17
+ */
18
+ export function selectPreview(id, key) {
19
+ const native = requireOptionalNativeModule("AxonpackPreview");
20
+ if (!native)
21
+ return false;
22
+ native.select(id === null ? null : previewBundlePath(id), id === null ? null : (key ?? null));
23
+ return true;
24
+ }
25
+ //# sourceMappingURL=select.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.service.js","sourceRoot":"","sources":["../../../src/services/previews/select.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,MAAM,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIxC,gGAAgG;AAChG,qCAAqC;AACrC,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC;IAClC,GAAG,EAAE,eAAe;IACpB,OAAO,EAAE,sBAAsB;CAChC,CAAC,CAAC;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAAU,EAAU,EAAE,CACtD,GAAG,EAAE,IAAI,QAAQ,CAAC,EAAE,IAAI,WAAW,EAAE,CAAC;AAExC;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,EAAiB,EAAE,GAAY;IAC3D,MAAM,MAAM,GAAG,2BAA2B,CAAe,iBAAiB,CAAC,CAAC;IAC5E,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,CAAC,MAAM,CACX,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAC1C,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CACnC,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { requireOptionalNativeModule } from \"expo\";\nimport { Platform } from \"react-native\";\n\ntype NativeModule = { select(bundle: string | null, key: string | null): void };\n\n// What `expo export:embed` names the bundle on each platform, so a preview folder holds exactly\n// what the native build would embed.\nconst BUNDLE_FILE = Platform.select({\n ios: \"main.jsbundle\",\n default: \"index.android.bundle\",\n});\n\n/** Where a preview's bundle sits, relative to the preview folder. */\nexport const previewBundlePath = (id: string): string =>\n `${id}/${Platform.OS}/${BUNDLE_FILE}`;\n\n/**\n * Chooses what the next start or `reload()` loads: a preview by id, or null for the base bundle.\n * `key` is the build key the preview was published for; with it, a later build with another key\n * loads the base bundle instead.\n * A preview that is not on the device falls back to the base bundle. False where the native module\n * isn't linked.\n */\nexport function selectPreview(id: string | null, key?: string): boolean {\n const native = requireOptionalNativeModule<NativeModule>(\"AxonpackPreview\");\n if (!native) return false;\n native.select(\n id === null ? null : previewBundlePath(id),\n id === null ? null : (key ?? null),\n );\n return true;\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /** The list and its actions, for the built-in list or a screen of your own. */
2
+ export declare function usePreview(): {
3
+ enabled: boolean;
4
+ devPreviewServers: boolean;
5
+ refresh: () => Promise<void>;
6
+ download: (id: string) => Promise<void>;
7
+ switchTo: (id: string | null) => Promise<void>;
8
+ remove: (id: string) => Promise<void>;
9
+ removeIncompatible: () => Promise<void>;
10
+ items: import("../..").PreviewItem[];
11
+ incompatible: import("./local-previews.service").LocalPreview[];
12
+ loading: boolean;
13
+ error: string | null;
14
+ buildKey: string | null;
15
+ remote: import("../../utils/previews/layout.util").IndexEntry[] | null;
16
+ local: import("./local-previews.service").LocalPreview[];
17
+ downloading: Record<string, number>;
18
+ activeId: string | null;
19
+ broken: string[];
20
+ };
21
+ //# sourceMappingURL=use-preview.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-preview.service.d.ts","sourceRoot":"","sources":["../../../src/services/previews/use-preview.service.ts"],"names":[],"mappings":"AASA,+EAA+E;AAC/E,wBAAgB,UAAU;;;;mBAaL,MAAM;mBACN,MAAM,GAAG,IAAI;iBACf,MAAM;;;;;;;;;;;;EAKxB"}
@@ -0,0 +1,24 @@
1
+ import { useContext, useMemo } from "react";
2
+ import { ConfigContext } from "../../components/preview-provider/preview-provider.component";
3
+ import { previewItems } from "../../utils/previews/preview-items.util";
4
+ import { previewsStore, usePreviewsState, } from "../../stores/previews/previews.store";
5
+ /** The list and its actions, for the built-in list or a screen of your own. */
6
+ export function usePreview() {
7
+ const config = useContext(ConfigContext);
8
+ if (!config)
9
+ throw new Error("usePreview needs a <PreviewProvider> above it.");
10
+ const state = usePreviewsState();
11
+ const lists = useMemo(() => previewItems(state), [state]);
12
+ return useMemo(() => ({
13
+ ...state,
14
+ ...lists,
15
+ enabled: config.enabled,
16
+ devPreviewServers: config.devPreviewServers === true,
17
+ refresh: () => previewsStore.refresh(config),
18
+ download: (id) => previewsStore.download(config, id),
19
+ switchTo: (id) => previewsStore.switchTo(config, id),
20
+ remove: (id) => previewsStore.remove(id),
21
+ removeIncompatible: () => previewsStore.removeIncompatible(),
22
+ }), [state, lists, config]);
23
+ }
24
+ //# sourceMappingURL=use-preview.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-preview.service.js","sourceRoot":"","sources":["../../../src/services/previews/use-preview.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EACL,aAAa,EACb,gBAAgB,GACjB,MAAM,sCAAsC,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM;QACT,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,GAAG,KAAK;QACR,GAAG,KAAK;QACR,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,KAAK,IAAI;QACpD,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC;QAC5C,QAAQ,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5D,QAAQ,EAAE,CAAC,EAAiB,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QACnE,MAAM,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,kBAAkB,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE;KAC7D,CAAC,EACF,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CACvB,CAAC;AACJ,CAAC","sourcesContent":["import { useContext, useMemo } from \"react\";\n\nimport { ConfigContext } from \"../../components/preview-provider/preview-provider.component\";\nimport { previewItems } from \"../../utils/previews/preview-items.util\";\nimport {\n previewsStore,\n usePreviewsState,\n} from \"../../stores/previews/previews.store\";\n\n/** The list and its actions, for the built-in list or a screen of your own. */\nexport function usePreview() {\n const config = useContext(ConfigContext);\n if (!config)\n throw new Error(\"usePreview needs a <PreviewProvider> above it.\");\n const state = usePreviewsState();\n const lists = useMemo(() => previewItems(state), [state]);\n return useMemo(\n () => ({\n ...state,\n ...lists,\n enabled: config.enabled,\n devPreviewServers: config.devPreviewServers === true,\n refresh: () => previewsStore.refresh(config),\n download: (id: string) => previewsStore.download(config, id),\n switchTo: (id: string | null) => previewsStore.switchTo(config, id),\n remove: (id: string) => previewsStore.remove(id),\n removeIncompatible: () => previewsStore.removeIncompatible(),\n }),\n [state, lists, config],\n );\n}\n"]}