@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 @@
1
+ {"version":3,"file":"preview-items.util.d.ts","sourceRoot":"","sources":["../../../src/utils/previews/preview-items.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAE1E,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,aAAa,GACb,YAAY,GACZ,kBAAkB,GAClB,aAAa,GACb,QAAQ,CAAC;AAEb,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,qFAAqF;IACrF,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG;IAClD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B,CAqCA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The list QA sees for this build: everything the bucket offers, plus downloads it no longer lists
3
+ * (unpublished). Downloads for another build go to `incompatible` instead; nothing expires.
4
+ */
5
+ export function previewItems(state) {
6
+ const compatible = state.local.filter((p) => p.key === state.buildKey);
7
+ const incompatible = state.local.filter((p) => p.key !== state.buildKey);
8
+ const localById = new Map(compatible.map((p) => [p.id, p]));
9
+ const remote = state.remote ?? [];
10
+ const item = (entry, local, listed) => {
11
+ const progress = state.downloading[entry.id] ?? null;
12
+ let status = "available";
13
+ if (progress !== null)
14
+ status = "downloading";
15
+ else if (local && state.broken.includes(entry.id))
16
+ status = "broken";
17
+ else if (local && !listed && state.remote !== null)
18
+ status = "unpublished";
19
+ else if (local && listed && local.revision !== entry.revision)
20
+ status = "update-available";
21
+ else if (local)
22
+ status = "downloaded";
23
+ return {
24
+ id: entry.id,
25
+ entry,
26
+ status,
27
+ progress,
28
+ active: state.activeId === entry.id,
29
+ local,
30
+ };
31
+ };
32
+ const items = remote.map((entry) => item(entry, localById.get(entry.id) ?? null, true));
33
+ for (const local of compatible) {
34
+ if (!remote.some((entry) => entry.id === local.id))
35
+ items.push(item(local.entry, local, false));
36
+ }
37
+ return { items, incompatible };
38
+ }
39
+ //# sourceMappingURL=preview-items.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-items.util.js","sourceRoot":"","sources":["../../../src/utils/previews/preview-items.util.ts"],"names":[],"mappings":"AAuBA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAoB;IAI/C,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvE,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;IAElC,MAAM,IAAI,GAAG,CACX,KAAiB,EACjB,KAA0B,EAC1B,MAAe,EACF,EAAE;QACf,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;QACrD,IAAI,MAAM,GAAkB,WAAW,CAAC;QACxC,IAAI,QAAQ,KAAK,IAAI;YAAE,MAAM,GAAG,aAAa,CAAC;aACzC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,MAAM,GAAG,QAAQ,CAAC;aAChE,IAAI,KAAK,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI;YAAE,MAAM,GAAG,aAAa,CAAC;aACtE,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;YAC3D,MAAM,GAAG,kBAAkB,CAAC;aACzB,IAAI,KAAK;YAAE,MAAM,GAAG,YAAY,CAAC;QACtC,OAAO;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK;YACL,MAAM;YACN,QAAQ;YACR,MAAM,EAAE,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,EAAE;YACnC,KAAK;SACN,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACjC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CACnD,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC","sourcesContent":["import type { IndexEntry } from \"./layout.util\";\nimport type { LocalPreview } from \"../../services/previews/local-previews.service\";\nimport type { PreviewsState } from \"../../stores/previews/previews.store\";\n\nexport type PreviewStatus =\n | \"available\"\n | \"downloading\"\n | \"downloaded\"\n | \"update-available\"\n | \"unpublished\"\n | \"broken\";\n\nexport type PreviewItem = {\n id: string;\n /** The bucket's entry, or the one saved with the download once it is unpublished. */\n entry: IndexEntry;\n status: PreviewStatus;\n /** 0 to 1 while downloading. */\n progress: number | null;\n active: boolean;\n local: LocalPreview | null;\n};\n\n/**\n * The list QA sees for this build: everything the bucket offers, plus downloads it no longer lists\n * (unpublished). Downloads for another build go to `incompatible` instead; nothing expires.\n */\nexport function previewItems(state: PreviewsState): {\n items: PreviewItem[];\n incompatible: LocalPreview[];\n} {\n const compatible = state.local.filter((p) => p.key === state.buildKey);\n const incompatible = state.local.filter((p) => p.key !== state.buildKey);\n const localById = new Map(compatible.map((p) => [p.id, p]));\n const remote = state.remote ?? [];\n\n const item = (\n entry: IndexEntry,\n local: LocalPreview | null,\n listed: boolean,\n ): PreviewItem => {\n const progress = state.downloading[entry.id] ?? null;\n let status: PreviewStatus = \"available\";\n if (progress !== null) status = \"downloading\";\n else if (local && state.broken.includes(entry.id)) status = \"broken\";\n else if (local && !listed && state.remote !== null) status = \"unpublished\";\n else if (local && listed && local.revision !== entry.revision)\n status = \"update-available\";\n else if (local) status = \"downloaded\";\n return {\n id: entry.id,\n entry,\n status,\n progress,\n active: state.activeId === entry.id,\n local,\n };\n };\n\n const items = remote.map((entry) =>\n item(entry, localById.get(entry.id) ?? null, true),\n );\n for (const local of compatible) {\n if (!remote.some((entry) => entry.id === local.id))\n items.push(item(local.entry, local, false));\n }\n return { items, incompatible };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function previewTitle(branch: string): string;
2
+ //# sourceMappingURL=preview-title.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-title.util.d.ts","sourceRoot":"","sources":["../../../src/utils/previews/preview-title.util.ts"],"names":[],"mappings":"AACA,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGnD"}
@@ -0,0 +1,6 @@
1
+ // The preview id is the branch name, so this is what QA reads when no `--title` was given.
2
+ export function previewTitle(branch) {
3
+ const words = branch.replace(/[-_/]+/g, " ").trim();
4
+ return words.charAt(0).toUpperCase() + words.slice(1);
5
+ }
6
+ //# sourceMappingURL=preview-title.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-title.util.js","sourceRoot":"","sources":["../../../src/utils/previews/preview-title.util.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC","sourcesContent":["// The preview id is the branch name, so this is what QA reads when no `--title` was given.\nexport function previewTitle(branch: string): string {\n const words = branch.replace(/[-_/]+/g, \" \").trim();\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { DevPreviewServer } from "../dev-preview-servers/merge-servers.util";
2
+ import type { PreviewItem } from "./preview-items.util";
3
+ /** What this start runs: a local Metro server, a downloaded preview, or the app's own bundle. */
4
+ export type Running = {
5
+ kind: "local";
6
+ url: string;
7
+ hostPort: string;
8
+ /** Null until the dev preview servers list has found it. */
9
+ server: DevPreviewServer | null;
10
+ } | {
11
+ kind: "preview";
12
+ id: string;
13
+ title: string;
14
+ } | {
15
+ kind: "base";
16
+ };
17
+ export declare function runningNow(input: {
18
+ items: PreviewItem[];
19
+ activeId: string | null;
20
+ localUrl: string | null;
21
+ servers: DevPreviewServer[];
22
+ }): Running;
23
+ //# sourceMappingURL=running.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running.util.d.ts","sourceRoot":"","sources":["../../../src/utils/previews/running.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,iGAAiG;AACjG,MAAM,MAAM,OAAO,GACf;IACE,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjC,GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B,GAAG,OAAO,CAsBV"}
@@ -0,0 +1,23 @@
1
+ export function runningNow(input) {
2
+ const { localUrl } = input;
3
+ // A dev preview server wins over a picked preview on the native side, so it wins here too.
4
+ if (localUrl) {
5
+ const server = input.servers.find((s) => s.url === localUrl) ?? null;
6
+ return {
7
+ kind: "local",
8
+ url: localUrl,
9
+ hostPort: server?.hostPort ?? localUrl.replace(/^\w+:\/\/([^/]+).*$/, "$1"),
10
+ server,
11
+ };
12
+ }
13
+ if (input.activeId) {
14
+ const item = input.items.find((i) => i.active);
15
+ return {
16
+ kind: "preview",
17
+ id: input.activeId,
18
+ title: item?.entry.title ?? input.activeId,
19
+ };
20
+ }
21
+ return { kind: "base" };
22
+ }
23
+ //# sourceMappingURL=running.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running.util.js","sourceRoot":"","sources":["../../../src/utils/previews/running.util.ts"],"names":[],"mappings":"AAeA,MAAM,UAAU,UAAU,CAAC,KAK1B;IACC,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3B,2FAA2F;IAC3F,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,IAAI,IAAI,CAAC;QACrE,OAAO;YACL,IAAI,EAAE,OAAO;YACb,GAAG,EAAE,QAAQ;YACb,QAAQ,EACN,MAAM,EAAE,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC;YACnE,MAAM;SACP,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO;YACL,IAAI,EAAE,SAAS;YACf,EAAE,EAAE,KAAK,CAAC,QAAQ;YAClB,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ;SAC3C,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC","sourcesContent":["import type { DevPreviewServer } from \"../dev-preview-servers/merge-servers.util\";\nimport type { PreviewItem } from \"./preview-items.util\";\n\n/** What this start runs: a local Metro server, a downloaded preview, or the app's own bundle. */\nexport type Running =\n | {\n kind: \"local\";\n url: string;\n hostPort: string;\n /** Null until the dev preview servers list has found it. */\n server: DevPreviewServer | null;\n }\n | { kind: \"preview\"; id: string; title: string }\n | { kind: \"base\" };\n\nexport function runningNow(input: {\n items: PreviewItem[];\n activeId: string | null;\n localUrl: string | null;\n servers: DevPreviewServer[];\n}): Running {\n const { localUrl } = input;\n // A dev preview server wins over a picked preview on the native side, so it wins here too.\n if (localUrl) {\n const server = input.servers.find((s) => s.url === localUrl) ?? null;\n return {\n kind: \"local\",\n url: localUrl,\n hostPort:\n server?.hostPort ?? localUrl.replace(/^\\w+:\\/\\/([^/]+).*$/, \"$1\"),\n server,\n };\n }\n if (input.activeId) {\n const item = input.items.find((i) => i.active);\n return {\n kind: \"preview\",\n id: input.activeId,\n title: item?.entry.title ?? input.activeId,\n };\n }\n return { kind: \"base\" };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ /** One line per commit subject, newest first, the way `git log` lists them. */
2
+ export declare function changelogFromSubjects(subjects: string[]): string;
3
+ /** The index carries a short form, so the list stays small; the manifest keeps the whole text. */
4
+ export declare function shortChangelog(changelog: string, maxLines?: number, maxChars?: number): string;
5
+ //# sourceMappingURL=changelog.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.util.d.ts","sourceRoot":"","sources":["../../../src/utils/publish/changelog.util.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAMhE;AAED,kGAAkG;AAClG,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAI,EACZ,QAAQ,SAAM,GACb,MAAM,CAQR"}
@@ -0,0 +1,19 @@
1
+ /** One line per commit subject, newest first, the way `git log` lists them. */
2
+ export function changelogFromSubjects(subjects) {
3
+ return subjects
4
+ .map((s) => s.trim())
5
+ .filter(Boolean)
6
+ .map((s) => `- ${s}`)
7
+ .join("\n");
8
+ }
9
+ /** The index carries a short form, so the list stays small; the manifest keeps the whole text. */
10
+ export function shortChangelog(changelog, maxLines = 3, maxChars = 280) {
11
+ const lines = changelog.split("\n").filter((l) => l.trim());
12
+ let short = lines.slice(0, maxLines).join("\n");
13
+ if (short.length > maxChars)
14
+ short = `${short.slice(0, maxChars - 1).trimEnd()}…`;
15
+ else if (lines.length > maxLines)
16
+ short += `\n… ${lines.length - maxLines} more`;
17
+ return short;
18
+ }
19
+ //# sourceMappingURL=changelog.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.util.js","sourceRoot":"","sources":["../../../src/utils/publish/changelog.util.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB,CAAC,QAAkB;IACtD,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;SACpB,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,QAAQ,GAAG,CAAC,EACZ,QAAQ,GAAG,GAAG;IAEd,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ;QACzB,KAAK,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC;SAClD,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ;QAC9B,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,GAAG,QAAQ,OAAO,CAAC;IACjD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/** One line per commit subject, newest first, the way `git log` lists them. */\nexport function changelogFromSubjects(subjects: string[]): string {\n return subjects\n .map((s) => s.trim())\n .filter(Boolean)\n .map((s) => `- ${s}`)\n .join(\"\\n\");\n}\n\n/** The index carries a short form, so the list stays small; the manifest keeps the whole text. */\nexport function shortChangelog(\n changelog: string,\n maxLines = 3,\n maxChars = 280,\n): string {\n const lines = changelog.split(\"\\n\").filter((l) => l.trim());\n let short = lines.slice(0, maxLines).join(\"\\n\");\n if (short.length > maxChars)\n short = `${short.slice(0, maxChars - 1).trimEnd()}…`;\n else if (lines.length > maxLines)\n short += `\\n… ${lines.length - maxLines} more`;\n return short;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import { Compatibility, PlatformIndex, IndexEntry, Platform, RootIndex } from "../previews/layout.util";
2
+ /** Adds the entry, or replaces the one with the same id. */
3
+ export declare function upsertPreview(index: PlatformIndex | null, platform: Platform, build: {
4
+ key: string;
5
+ compatibility: Compatibility;
6
+ }, entry: IndexEntry, now: string): PlatformIndex;
7
+ export declare function removePreview(index: PlatformIndex, id: string, now: string): PlatformIndex;
8
+ /** Mirrors one platform index into the top-level list; a build left with no previews drops out. */
9
+ export declare function syncRootVersion(root: RootIndex | null, index: PlatformIndex, now: string): RootIndex;
10
+ /** Every platform index that lists the id, from the top-level list. */
11
+ export declare const indexesListing: (root: RootIndex | null, id: string) => string[];
12
+ //# sourceMappingURL=indexes.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.util.d.ts","sourceRoot":"","sources":["../../../src/utils/publish/indexes.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,SAAS,EAIV,MAAM,yBAAyB,CAAC;AAKjC,4DAA4D;AAC5D,wBAAgB,aAAa,CAC3B,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,EACpD,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,GACV,aAAa,CAUf;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,aAAa,EACpB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,GACV,aAAa,CAMf;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,GAAG,IAAI,EACtB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,MAAM,GACV,SAAS,CA0BX;AAED,uEAAuE;AACvE,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,GAAG,IAAI,EAAE,IAAI,MAAM,KAAG,MAAM,EAGlD,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { SCHEMA, platformIndexKey, } from "../previews/layout.util";
2
+ const newestFirst = (a, b) => b.publishedAt.localeCompare(a.publishedAt);
3
+ /** Adds the entry, or replaces the one with the same id. */
4
+ export function upsertPreview(index, platform, build, entry, now) {
5
+ const others = (index?.previews ?? []).filter((p) => p.id !== entry.id);
6
+ return {
7
+ schema: SCHEMA,
8
+ platform,
9
+ key: build.key,
10
+ compatibility: build.compatibility,
11
+ updatedAt: now,
12
+ previews: [...others, entry].sort(newestFirst),
13
+ };
14
+ }
15
+ export function removePreview(index, id, now) {
16
+ return {
17
+ ...index,
18
+ updatedAt: now,
19
+ previews: index.previews.filter((p) => p.id !== id),
20
+ };
21
+ }
22
+ /** Mirrors one platform index into the top-level list; a build left with no previews drops out. */
23
+ export function syncRootVersion(root, index, now) {
24
+ const versions = (root?.versions ?? []).filter((v) => !(v.platform === index.platform && v.key === index.key));
25
+ if (index.previews.length > 0) {
26
+ const latest = index.previews[0];
27
+ const version = {
28
+ platform: index.platform,
29
+ key: index.key,
30
+ compatibility: index.compatibility,
31
+ appVersion: latest.appVersion,
32
+ buildNumber: latest.buildNumber,
33
+ fingerprint: latest.fingerprint,
34
+ count: index.previews.length,
35
+ updatedAt: now,
36
+ index: platformIndexKey(index.platform, index.key),
37
+ previews: index.previews.map(({ id, title, publishedAt }) => ({
38
+ id,
39
+ title,
40
+ publishedAt,
41
+ })),
42
+ };
43
+ versions.push(version);
44
+ }
45
+ versions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
46
+ return { schema: SCHEMA, updatedAt: now, versions };
47
+ }
48
+ /** Every platform index that lists the id, from the top-level list. */
49
+ export const indexesListing = (root, id) => (root?.versions ?? [])
50
+ .filter((v) => v.previews.some((p) => p.id === id))
51
+ .map((v) => v.index);
52
+ //# sourceMappingURL=indexes.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.util.js","sourceRoot":"","sources":["../../../src/utils/publish/indexes.util.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Compatibility,\n PlatformIndex,\n IndexEntry,\n Platform,\n RootIndex,\n RootVersion,\n SCHEMA,\n platformIndexKey,\n} from \"../previews/layout.util\";\n\nconst newestFirst = (a: { publishedAt: string }, b: { publishedAt: string }) =>\n b.publishedAt.localeCompare(a.publishedAt);\n\n/** Adds the entry, or replaces the one with the same id. */\nexport function upsertPreview(\n index: PlatformIndex | null,\n platform: Platform,\n build: { key: string; compatibility: Compatibility },\n entry: IndexEntry,\n now: string,\n): PlatformIndex {\n const others = (index?.previews ?? []).filter((p) => p.id !== entry.id);\n return {\n schema: SCHEMA,\n platform,\n key: build.key,\n compatibility: build.compatibility,\n updatedAt: now,\n previews: [...others, entry].sort(newestFirst),\n };\n}\n\nexport function removePreview(\n index: PlatformIndex,\n id: string,\n now: string,\n): PlatformIndex {\n return {\n ...index,\n updatedAt: now,\n previews: index.previews.filter((p) => p.id !== id),\n };\n}\n\n/** Mirrors one platform index into the top-level list; a build left with no previews drops out. */\nexport function syncRootVersion(\n root: RootIndex | null,\n index: PlatformIndex,\n now: string,\n): RootIndex {\n const versions = (root?.versions ?? []).filter(\n (v) => !(v.platform === index.platform && v.key === index.key),\n );\n if (index.previews.length > 0) {\n const latest = index.previews[0];\n const version: RootVersion = {\n platform: index.platform,\n key: index.key,\n compatibility: index.compatibility,\n appVersion: latest.appVersion,\n buildNumber: latest.buildNumber,\n fingerprint: latest.fingerprint,\n count: index.previews.length,\n updatedAt: now,\n index: platformIndexKey(index.platform, index.key),\n previews: index.previews.map(({ id, title, publishedAt }) => ({\n id,\n title,\n publishedAt,\n })),\n };\n versions.push(version);\n }\n versions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n return { schema: SCHEMA, updatedAt: now, versions };\n}\n\n/** Every platform index that lists the id, from the top-level list. */\nexport const indexesListing = (root: RootIndex | null, id: string): string[] =>\n (root?.versions ?? [])\n .filter((v) => v.previews.some((p) => p.id === id))\n .map((v) => v.index);\n"]}
@@ -0,0 +1,24 @@
1
+ export type Credentials = {
2
+ accessKeyId: string;
3
+ secretAccessKey: string;
4
+ sessionToken?: string;
5
+ };
6
+ export type SignInput = {
7
+ method: string;
8
+ url: string;
9
+ region: string;
10
+ credentials: Credentials;
11
+ /** Headers to send and sign. Host comes from the URL. */
12
+ headers?: Record<string, string>;
13
+ /** Hex sha256 of the body, or "UNSIGNED-PAYLOAD". Defaults to the hash of an empty body. */
14
+ payloadHash?: string;
15
+ service?: string;
16
+ date?: Date;
17
+ /** S3 needs `x-amz-content-sha256`; the generic AWS test suite signs without it. */
18
+ addContentSha256?: boolean;
19
+ };
20
+ export declare const EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
21
+ export declare const sha256Hex: (data: Uint8Array | string) => string;
22
+ /** Returns the headers to send: the input headers plus the date, hash and Authorization. */
23
+ export declare function signRequest(input: SignInput): Record<string, string>;
24
+ //# sourceMappingURL=sigv4.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.util.d.ts","sourceRoot":"","sources":["../../../src/utils/s3/sigv4.util.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,YAAY,qEAC2C,CAAC;AAErE,eAAO,MAAM,SAAS,GAAI,MAAM,UAAU,GAAG,MAAM,KAAG,MACmB,CAAC;AAsC1E,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgDpE"}
@@ -0,0 +1,67 @@
1
+ import { hmac } from "@noble/hashes/hmac.js";
2
+ import { sha256 } from "@noble/hashes/sha2.js";
3
+ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
4
+ export const EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
5
+ export const sha256Hex = (data) => bytesToHex(sha256(typeof data === "string" ? utf8ToBytes(data) : data));
6
+ const hmacBytes = (key, data) => hmac(sha256, typeof key === "string" ? utf8ToBytes(key) : key, utf8ToBytes(data));
7
+ // RFC 3986: everything but unreserved characters is percent-encoded, which encodeURIComponent
8
+ // almost does; it leaves !'()* alone.
9
+ const encode = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
10
+ const canonicalPath = (pathname) => pathname
11
+ .split("/")
12
+ .map((segment) => encode(decodeURIComponent(segment)))
13
+ .join("/") || "/";
14
+ const canonicalQuery = (search) => [...search.entries()]
15
+ .map(([key, value]) => [encode(key), encode(value)])
16
+ .sort(([ak, av], [bk, bv]) => ak === bk ? (av < bv ? -1 : 1) : ak < bk ? -1 : 1)
17
+ .map(([key, value]) => `${key}=${value}`)
18
+ .join("&");
19
+ const amzDate = (date) => date
20
+ .toISOString()
21
+ .replace(/[-:]/g, "")
22
+ .replace(/\.\d{3}/, "");
23
+ /** Returns the headers to send: the input headers plus the date, hash and Authorization. */
24
+ export function signRequest(input) {
25
+ const url = new URL(input.url);
26
+ const service = input.service ?? "s3";
27
+ const payloadHash = input.payloadHash ?? EMPTY_SHA256;
28
+ const timestamp = amzDate(input.date ?? new Date());
29
+ const day = timestamp.slice(0, 8);
30
+ const headers = { ...input.headers };
31
+ headers["x-amz-date"] = timestamp;
32
+ if (input.addContentSha256 ?? true)
33
+ headers["x-amz-content-sha256"] = payloadHash;
34
+ if (input.credentials.sessionToken) {
35
+ headers["x-amz-security-token"] = input.credentials.sessionToken;
36
+ }
37
+ const signed = new Map();
38
+ signed.set("host", url.host);
39
+ for (const [name, value] of Object.entries(headers)) {
40
+ signed.set(name.toLowerCase(), value.trim().replace(/\s+/g, " "));
41
+ }
42
+ const names = [...signed.keys()].sort();
43
+ const signedHeaders = names.join(";");
44
+ const canonicalRequest = [
45
+ input.method.toUpperCase(),
46
+ canonicalPath(url.pathname),
47
+ canonicalQuery(url.searchParams),
48
+ names.map((name) => `${name}:${signed.get(name)}\n`).join(""),
49
+ signedHeaders,
50
+ payloadHash,
51
+ ].join("\n");
52
+ const scope = `${day}/${input.region}/${service}/aws4_request`;
53
+ const stringToSign = [
54
+ "AWS4-HMAC-SHA256",
55
+ timestamp,
56
+ scope,
57
+ sha256Hex(canonicalRequest),
58
+ ].join("\n");
59
+ let key = hmacBytes(`AWS4${input.credentials.secretAccessKey}`, day);
60
+ key = hmacBytes(key, input.region);
61
+ key = hmacBytes(key, service);
62
+ key = hmacBytes(key, "aws4_request");
63
+ const signature = bytesToHex(hmacBytes(key, stringToSign));
64
+ headers.Authorization = `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
65
+ return headers;
66
+ }
67
+ //# sourceMappingURL=sigv4.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.util.js","sourceRoot":"","sources":["../../../src/utils/s3/sigv4.util.ts"],"names":[],"mappings":"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","sourcesContent":["import { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToHex, utf8ToBytes } from \"@noble/hashes/utils.js\";\n\n// AWS Signature Version 4, header form. Pure JS so the same code signs uploads in the CLI (Node)\n// and private-bucket downloads in the app (Hermes, which has no WebCrypto).\n\nexport type Credentials = {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n};\n\nexport type SignInput = {\n method: string;\n url: string;\n region: string;\n credentials: Credentials;\n /** Headers to send and sign. Host comes from the URL. */\n headers?: Record<string, string>;\n /** Hex sha256 of the body, or \"UNSIGNED-PAYLOAD\". Defaults to the hash of an empty body. */\n payloadHash?: string;\n service?: string;\n date?: Date;\n /** S3 needs `x-amz-content-sha256`; the generic AWS test suite signs without it. */\n addContentSha256?: boolean;\n};\n\nexport const EMPTY_SHA256 =\n \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\";\n\nexport const sha256Hex = (data: Uint8Array | string): string =>\n bytesToHex(sha256(typeof data === \"string\" ? utf8ToBytes(data) : data));\n\nconst hmacBytes = (key: Uint8Array | string, data: string): Uint8Array =>\n hmac(\n sha256,\n typeof key === \"string\" ? utf8ToBytes(key) : key,\n utf8ToBytes(data),\n );\n\n// RFC 3986: everything but unreserved characters is percent-encoded, which encodeURIComponent\n// almost does; it leaves !'()* alone.\nconst encode = (value: string): string =>\n encodeURIComponent(value).replace(\n /[!'()*]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n\nconst canonicalPath = (pathname: string): string =>\n pathname\n .split(\"/\")\n .map((segment) => encode(decodeURIComponent(segment)))\n .join(\"/\") || \"/\";\n\nconst canonicalQuery = (search: URLSearchParams): string =>\n [...search.entries()]\n .map(([key, value]) => [encode(key), encode(value)])\n .sort(([ak, av], [bk, bv]) =>\n ak === bk ? (av < bv ? -1 : 1) : ak < bk ? -1 : 1,\n )\n .map(([key, value]) => `${key}=${value}`)\n .join(\"&\");\n\nconst amzDate = (date: Date): string =>\n date\n .toISOString()\n .replace(/[-:]/g, \"\")\n .replace(/\\.\\d{3}/, \"\");\n\n/** Returns the headers to send: the input headers plus the date, hash and Authorization. */\nexport function signRequest(input: SignInput): Record<string, string> {\n const url = new URL(input.url);\n const service = input.service ?? \"s3\";\n const payloadHash = input.payloadHash ?? EMPTY_SHA256;\n const timestamp = amzDate(input.date ?? new Date());\n const day = timestamp.slice(0, 8);\n\n const headers: Record<string, string> = { ...input.headers };\n headers[\"x-amz-date\"] = timestamp;\n if (input.addContentSha256 ?? true)\n headers[\"x-amz-content-sha256\"] = payloadHash;\n if (input.credentials.sessionToken) {\n headers[\"x-amz-security-token\"] = input.credentials.sessionToken;\n }\n\n const signed = new Map<string, string>();\n signed.set(\"host\", url.host);\n for (const [name, value] of Object.entries(headers)) {\n signed.set(name.toLowerCase(), value.trim().replace(/\\s+/g, \" \"));\n }\n const names = [...signed.keys()].sort();\n const signedHeaders = names.join(\";\");\n\n const canonicalRequest = [\n input.method.toUpperCase(),\n canonicalPath(url.pathname),\n canonicalQuery(url.searchParams),\n names.map((name) => `${name}:${signed.get(name)}\\n`).join(\"\"),\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n\n const scope = `${day}/${input.region}/${service}/aws4_request`;\n const stringToSign = [\n \"AWS4-HMAC-SHA256\",\n timestamp,\n scope,\n sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n\n let key = hmacBytes(`AWS4${input.credentials.secretAccessKey}`, day);\n key = hmacBytes(key, input.region);\n key = hmacBytes(key, service);\n key = hmacBytes(key, \"aws4_request\");\n const signature = bytesToHex(hmacBytes(key, stringToSign));\n\n headers.Authorization = `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ {
2
+ "platforms": ["apple", "android"],
3
+ "apple": {
4
+ "modules": ["AxonpackPreviewModule"],
5
+ "reactDelegateHandlers": ["PreviewReactDelegateHandler"]
6
+ },
7
+ "android": {
8
+ "modules": ["expo.modules.axonpackpreview.AxonpackPreviewModule"]
9
+ }
10
+ }
@@ -0,0 +1,30 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'AxonpackPreview'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = {
14
+ :ios => '16.4',
15
+ :tvos => '16.4'
16
+ }
17
+ s.swift_version = '5.9'
18
+ s.source = { git: 'https://github.com/axonpack/axonpack.git' }
19
+ s.static_framework = true
20
+
21
+ s.dependency 'ExpoModulesCore'
22
+ # RCTReloadCommand, to restart into a newly chosen bundle.
23
+ s.dependency 'React-Core'
24
+
25
+ s.source_files = "**/*.{h,m,swift}"
26
+ s.pod_target_xcconfig = {
27
+ 'DEFINES_MODULE' => 'YES',
28
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule'
29
+ }
30
+ end
@@ -0,0 +1,55 @@
1
+ import ExpoModulesCore
2
+ import React
3
+
4
+ public final class AxonpackPreviewModule: Module {
5
+ public func definition() -> ModuleDefinition {
6
+ Name("AxonpackPreview")
7
+
8
+ // What the config plugin wrote at prebuild; nil when the plugin is not in the app config.
9
+ Constants([
10
+ "compatibility": Bundle.main.object(forInfoDictionaryKey: "AxonpackPreviewCompatibility") as? String,
11
+ "key": Bundle.main.object(forInfoDictionaryKey: "AxonpackPreviewKey") as? String,
12
+ "slug": Bundle.main.object(forInfoDictionaryKey: "AxonpackPreviewSlug") as? String
13
+ ])
14
+
15
+ OnCreate {
16
+ DispatchQueue.main.async {
17
+ PreviewReactDelegateHandler.startOnSelectedPreview()
18
+ }
19
+ }
20
+
21
+ Function("select") { (bundle: String?, key: String?) in
22
+ try PreviewState.select(bundle, key: key)
23
+ }
24
+
25
+ // `hostPort` is what Android needs; iOS loads the bundle URL itself.
26
+ Function("selectLocalServer") { (url: String?, _: String?) in
27
+ PreviewState.selectLocalServer(url)
28
+ }
29
+
30
+ Function("state") {
31
+ PreviewState.read()
32
+ }
33
+
34
+ Function("markLaunched") {
35
+ PreviewState.confirmStart()
36
+ }
37
+
38
+ Function("reload") {
39
+ guard PreviewState.newArchitecture else {
40
+ PreviewState.warnOldArchitecture()
41
+ return
42
+ }
43
+ // The reload asks the bundle URL block again, and that block reads the current choice.
44
+ DispatchQueue.main.async {
45
+ PreviewReactDelegateHandler.reload()
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ final class PreviewPathOutsideException: Exception {
52
+ override var reason: String {
53
+ "The bundle path must stay inside the preview folder"
54
+ }
55
+ }
@@ -0,0 +1,147 @@
1
+ import ExpoModulesCore
2
+ import React
3
+
4
+ /**
5
+ Makes the app's bundle URL ask `state.json` every time React reads it: at the first start and on
6
+ every reload. A selected preview wins; otherwise the app's own URL is used, so Metro in a debug
7
+ build, the embedded bundle in a release build, or expo-updates' update loads exactly as without
8
+ this package.
9
+
10
+ React keeps the URL block it was created with, so it is swapped before the first root view exists,
11
+ which is when this handler runs. Returning nil lets the normal root view be made with the new block.
12
+ expo-updates is ordered ahead of every other handler and answers itself, so with it active this
13
+ handler is not asked; the module then reloads into a selected preview once it starts
14
+ (`startOnSelectedPreview`).
15
+ */
16
+ public final class PreviewReactDelegateHandler: ExpoReactDelegateHandler {
17
+ private typealias BundleURLBlock = @convention(block) () -> URL?
18
+
19
+ private static var installed = false
20
+ private static var hopped = false
21
+ private static weak var rootViewFactory: NSObject?
22
+ /// What the block handed React last, to tell whether the running bundle is the selected one.
23
+ private static var lastServed: URL?
24
+
25
+ public override func createReactRootView(
26
+ reactDelegate: ExpoReactDelegate,
27
+ moduleName: String,
28
+ initialProperties: [AnyHashable: Any]?,
29
+ launchOptions: [UIApplication.LaunchOptionsKey: Any]?
30
+ ) -> UIView? {
31
+ Self.install(factory: reactDelegate.reactNativeFactory as? NSObject)
32
+ return nil
33
+ }
34
+
35
+ private static func install(factory: NSObject?) {
36
+ if installed {
37
+ return
38
+ }
39
+ // React's factory and its configuration are reached by key, the way Expo's own
40
+ // `recreateRootView` reads `_configuration`, so this needs no header from React's app delegate pod.
41
+ guard
42
+ let factory,
43
+ factory.responds(to: NSSelectorFromString("rootViewFactory")),
44
+ let rootViewFactory = factory.value(forKey: "rootViewFactory") as? NSObject,
45
+ let configuration = rootViewFactory.value(forKey: "_configuration") as? NSObject,
46
+ configuration.responds(to: NSSelectorFromString("bundleURLBlock")),
47
+ let current = configuration.value(forKey: "bundleURLBlock")
48
+ else {
49
+ previewLog.warning("Could not reach React's bundle URL, so previews are off and the base bundle loads")
50
+ return
51
+ }
52
+ Self.rootViewFactory = rootViewFactory
53
+ let base = unsafeBitCast(current as AnyObject, to: BundleURLBlock.self)
54
+ let block: BundleURLBlock = {
55
+ // A local Metro server wins over a downloaded preview and the default (debug builds only).
56
+ let url = PreviewState.localServerURL ?? PreviewState.bundleToLoad() ?? base()
57
+ Self.lastServed = url
58
+ return url
59
+ }
60
+ configuration.setValue(block, forKey: "bundleURLBlock")
61
+ installed = true
62
+ }
63
+
64
+ /**
65
+ For an app whose start this handler did not see (expo-updates answered first): if a preview is
66
+ selected but the base is what started, reload once into the preview. Once per process, so a
67
+ preview that cannot load never turns into a reload loop.
68
+ */
69
+ static func startOnSelectedPreview() {
70
+ // The plain lookup, not `bundleToLoad`: that would count this start as a preview start and the
71
+ // reload below as a second one.
72
+ guard
73
+ !hopped,
74
+ PreviewState.localServerURL == nil,
75
+ let preview = PreviewState.selectedBundle(),
76
+ PreviewState.newArchitecture
77
+ else {
78
+ return
79
+ }
80
+ if installed, lastServed?.standardizedFileURL == preview.standardizedFileURL {
81
+ return
82
+ }
83
+ hopped = true
84
+ reload()
85
+ }
86
+
87
+ /**
88
+ Restarts JS into the current choice. React's bundle manager reads its bundle configuration before
89
+ anything else, and the one Expo builds deliberately says nothing for a file URL. So a preview is
90
+ put there as React's own file configuration, and the base gets React's default one, which hands
91
+ the choice back to the app's URL: Metro, the embedded bundle, or expo-updates' update. This works
92
+ whether or not the block above was installed. Each step is checked, since a missing key throws.
93
+ */
94
+ static func reload() {
95
+ PreviewState.nextStart()
96
+ guard
97
+ let host = reactHost(),
98
+ let ivar = class_getInstanceVariable(type(of: host), "_bundleManager"),
99
+ let manager = object_getIvar(host, ivar) as? NSObject,
100
+ manager.responds(to: NSSelectorFromString("setBundleConfig:")),
101
+ let configuration = bundleConfiguration(
102
+ for: PreviewState.localServerURL == nil ? PreviewState.bundleToLoad() : nil
103
+ )
104
+ else {
105
+ previewLog.warning("Could not reach React's bundle manager, so the reload keeps the current bundle")
106
+ RCTTriggerReloadCommandListeners("preview: switch bundle")
107
+ return
108
+ }
109
+ manager.setValue(configuration, forKey: "bundleConfig")
110
+ RCTTriggerReloadCommandListeners("preview: switch bundle")
111
+ }
112
+
113
+ private static func reactHost() -> NSObject? {
114
+ var factory = rootViewFactory
115
+ if factory == nil {
116
+ // The handler was not asked, so find React's factory where Expo's app delegate template keeps it.
117
+ let appDelegate = UIApplication.shared.delegate as? NSObject
118
+ if appDelegate?.responds(to: NSSelectorFromString("reactNativeFactory")) == true,
119
+ let reactNativeFactory = appDelegate?.value(forKey: "reactNativeFactory") as? NSObject,
120
+ reactNativeFactory.responds(to: NSSelectorFromString("rootViewFactory")) {
121
+ factory = reactNativeFactory.value(forKey: "rootViewFactory") as? NSObject
122
+ }
123
+ }
124
+ guard let factory, factory.responds(to: NSSelectorFromString("reactHost")) else {
125
+ return nil
126
+ }
127
+ return factory.value(forKey: "reactHost") as? NSObject
128
+ }
129
+
130
+ private static func bundleConfiguration(for preview: URL?) -> NSObject? {
131
+ guard
132
+ let type = NSClassFromString("RCTBundleConfiguration") as? NSObject.Type,
133
+ type.responds(to: NSSelectorFromString("defaultConfiguration")),
134
+ let configuration = type.perform(NSSelectorFromString("defaultConfiguration"))?.takeUnretainedValue() as? NSObject
135
+ else {
136
+ return nil
137
+ }
138
+ guard let preview else {
139
+ return configuration
140
+ }
141
+ guard class_getInstanceVariable(type, "_bundleFilePath") != nil else {
142
+ return nil
143
+ }
144
+ configuration.setValue(preview as NSURL, forKey: "bundleFilePath")
145
+ return configuration
146
+ }
147
+ }