@scalebun-release/cli 2.5.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 (210) hide show
  1. package/bin/scalebun.js +2 -0
  2. package/lib/api/client.d.ts +52 -0
  3. package/lib/api/client.d.ts.map +1 -0
  4. package/lib/api/client.js +335 -0
  5. package/lib/api/client.js.map +1 -0
  6. package/lib/commands/ci-init.d.ts +3 -0
  7. package/lib/commands/ci-init.d.ts.map +1 -0
  8. package/lib/commands/ci-init.js +348 -0
  9. package/lib/commands/ci-init.js.map +1 -0
  10. package/lib/commands/doctor.d.ts +31 -0
  11. package/lib/commands/doctor.d.ts.map +1 -0
  12. package/lib/commands/doctor.js +559 -0
  13. package/lib/commands/doctor.js.map +1 -0
  14. package/lib/commands/launch.d.ts +3 -0
  15. package/lib/commands/launch.d.ts.map +1 -0
  16. package/lib/commands/launch.js +23 -0
  17. package/lib/commands/launch.js.map +1 -0
  18. package/lib/commands/link.d.ts +16 -0
  19. package/lib/commands/link.d.ts.map +1 -0
  20. package/lib/commands/link.js +164 -0
  21. package/lib/commands/link.js.map +1 -0
  22. package/lib/commands/login.d.ts +12 -0
  23. package/lib/commands/login.d.ts.map +1 -0
  24. package/lib/commands/login.js +124 -0
  25. package/lib/commands/login.js.map +1 -0
  26. package/lib/commands/migrate-codepush.d.ts +13 -0
  27. package/lib/commands/migrate-codepush.d.ts.map +1 -0
  28. package/lib/commands/migrate-codepush.js +98 -0
  29. package/lib/commands/migrate-codepush.js.map +1 -0
  30. package/lib/commands/ota-bundle.d.ts +22 -0
  31. package/lib/commands/ota-bundle.d.ts.map +1 -0
  32. package/lib/commands/ota-bundle.js +253 -0
  33. package/lib/commands/ota-bundle.js.map +1 -0
  34. package/lib/commands/ota-channels.d.ts +3 -0
  35. package/lib/commands/ota-channels.d.ts.map +1 -0
  36. package/lib/commands/ota-channels.js +231 -0
  37. package/lib/commands/ota-channels.js.map +1 -0
  38. package/lib/commands/ota-delete-bundle.d.ts +16 -0
  39. package/lib/commands/ota-delete-bundle.d.ts.map +1 -0
  40. package/lib/commands/ota-delete-bundle.js +96 -0
  41. package/lib/commands/ota-delete-bundle.js.map +1 -0
  42. package/lib/commands/ota-inspect.d.ts +3 -0
  43. package/lib/commands/ota-inspect.d.ts.map +1 -0
  44. package/lib/commands/ota-inspect.js +258 -0
  45. package/lib/commands/ota-inspect.js.map +1 -0
  46. package/lib/commands/ota-keys.d.ts +91 -0
  47. package/lib/commands/ota-keys.d.ts.map +1 -0
  48. package/lib/commands/ota-keys.js +765 -0
  49. package/lib/commands/ota-keys.js.map +1 -0
  50. package/lib/commands/ota-promote.d.ts +3 -0
  51. package/lib/commands/ota-promote.d.ts.map +1 -0
  52. package/lib/commands/ota-promote.js +194 -0
  53. package/lib/commands/ota-promote.js.map +1 -0
  54. package/lib/commands/ota-publish.d.ts +26 -0
  55. package/lib/commands/ota-publish.d.ts.map +1 -0
  56. package/lib/commands/ota-publish.js +892 -0
  57. package/lib/commands/ota-publish.js.map +1 -0
  58. package/lib/commands/ota-rollout.d.ts +3 -0
  59. package/lib/commands/ota-rollout.d.ts.map +1 -0
  60. package/lib/commands/ota-rollout.js +265 -0
  61. package/lib/commands/ota-rollout.js.map +1 -0
  62. package/lib/commands/ota-sign.d.ts +19 -0
  63. package/lib/commands/ota-sign.d.ts.map +1 -0
  64. package/lib/commands/ota-sign.js +245 -0
  65. package/lib/commands/ota-sign.js.map +1 -0
  66. package/lib/commands/push-apns.d.ts +3 -0
  67. package/lib/commands/push-apns.d.ts.map +1 -0
  68. package/lib/commands/push-apns.js +166 -0
  69. package/lib/commands/push-apns.js.map +1 -0
  70. package/lib/commands/quickstart.d.ts +4 -0
  71. package/lib/commands/quickstart.d.ts.map +1 -0
  72. package/lib/commands/quickstart.js +672 -0
  73. package/lib/commands/quickstart.js.map +1 -0
  74. package/lib/commands/shell.d.ts +28 -0
  75. package/lib/commands/shell.d.ts.map +1 -0
  76. package/lib/commands/shell.js +512 -0
  77. package/lib/commands/shell.js.map +1 -0
  78. package/lib/commands/tls-pins.d.ts +27 -0
  79. package/lib/commands/tls-pins.d.ts.map +1 -0
  80. package/lib/commands/tls-pins.js +209 -0
  81. package/lib/commands/tls-pins.js.map +1 -0
  82. package/lib/index.d.ts +2 -0
  83. package/lib/index.d.ts.map +1 -0
  84. package/lib/index.js +233 -0
  85. package/lib/index.js.map +1 -0
  86. package/lib/ui/banner.d.ts +23 -0
  87. package/lib/ui/banner.d.ts.map +1 -0
  88. package/lib/ui/banner.js +162 -0
  89. package/lib/ui/banner.js.map +1 -0
  90. package/lib/ui/graphics.d.ts +18 -0
  91. package/lib/ui/graphics.d.ts.map +1 -0
  92. package/lib/ui/graphics.js +134 -0
  93. package/lib/ui/graphics.js.map +1 -0
  94. package/lib/ui/mark.d.ts +4 -0
  95. package/lib/ui/mark.d.ts.map +1 -0
  96. package/lib/ui/mark.js +21 -0
  97. package/lib/ui/mark.js.map +1 -0
  98. package/lib/ui/menu.d.ts +109 -0
  99. package/lib/ui/menu.d.ts.map +1 -0
  100. package/lib/ui/menu.js +314 -0
  101. package/lib/ui/menu.js.map +1 -0
  102. package/lib/ui/sixel.d.ts +6 -0
  103. package/lib/ui/sixel.d.ts.map +1 -0
  104. package/lib/ui/sixel.js +9 -0
  105. package/lib/ui/sixel.js.map +1 -0
  106. package/lib/utils/apps.d.ts +29 -0
  107. package/lib/utils/apps.d.ts.map +1 -0
  108. package/lib/utils/apps.js +19 -0
  109. package/lib/utils/apps.js.map +1 -0
  110. package/lib/utils/browser.d.ts +9 -0
  111. package/lib/utils/browser.d.ts.map +1 -0
  112. package/lib/utils/browser.js +39 -0
  113. package/lib/utils/browser.js.map +1 -0
  114. package/lib/utils/bundle.d.ts +153 -0
  115. package/lib/utils/bundle.d.ts.map +1 -0
  116. package/lib/utils/bundle.js +459 -0
  117. package/lib/utils/bundle.js.map +1 -0
  118. package/lib/utils/bundleStore.d.ts +128 -0
  119. package/lib/utils/bundleStore.d.ts.map +1 -0
  120. package/lib/utils/bundleStore.js +250 -0
  121. package/lib/utils/bundleStore.js.map +1 -0
  122. package/lib/utils/context.d.ts +88 -0
  123. package/lib/utils/context.d.ts.map +1 -0
  124. package/lib/utils/context.js +247 -0
  125. package/lib/utils/context.js.map +1 -0
  126. package/lib/utils/credentials.d.ts +59 -0
  127. package/lib/utils/credentials.d.ts.map +1 -0
  128. package/lib/utils/credentials.js +147 -0
  129. package/lib/utils/credentials.js.map +1 -0
  130. package/lib/utils/guide.d.ts +63 -0
  131. package/lib/utils/guide.d.ts.map +1 -0
  132. package/lib/utils/guide.js +191 -0
  133. package/lib/utils/guide.js.map +1 -0
  134. package/lib/utils/hash.d.ts +3 -0
  135. package/lib/utils/hash.d.ts.map +1 -0
  136. package/lib/utils/hash.js +44 -0
  137. package/lib/utils/hash.js.map +1 -0
  138. package/lib/utils/native-id.d.ts +17 -0
  139. package/lib/utils/native-id.d.ts.map +1 -0
  140. package/lib/utils/native-id.js +105 -0
  141. package/lib/utils/native-id.js.map +1 -0
  142. package/lib/utils/output.d.ts +96 -0
  143. package/lib/utils/output.d.ts.map +1 -0
  144. package/lib/utils/output.js +135 -0
  145. package/lib/utils/output.js.map +1 -0
  146. package/lib/utils/prompt.d.ts +106 -0
  147. package/lib/utils/prompt.d.ts.map +1 -0
  148. package/lib/utils/prompt.js +457 -0
  149. package/lib/utils/prompt.js.map +1 -0
  150. package/lib/utils/provenance.d.ts +30 -0
  151. package/lib/utils/provenance.d.ts.map +1 -0
  152. package/lib/utils/provenance.js +157 -0
  153. package/lib/utils/provenance.js.map +1 -0
  154. package/lib/utils/resolve.d.ts +81 -0
  155. package/lib/utils/resolve.d.ts.map +1 -0
  156. package/lib/utils/resolve.js +139 -0
  157. package/lib/utils/resolve.js.map +1 -0
  158. package/lib/utils/sdkBin.d.ts +18 -0
  159. package/lib/utils/sdkBin.d.ts.map +1 -0
  160. package/lib/utils/sdkBin.js +76 -0
  161. package/lib/utils/sdkBin.js.map +1 -0
  162. package/lib/utils/signer.d.ts +94 -0
  163. package/lib/utils/signer.d.ts.map +1 -0
  164. package/lib/utils/signer.js +230 -0
  165. package/lib/utils/signer.js.map +1 -0
  166. package/lib/utils/signing.d.ts +102 -0
  167. package/lib/utils/signing.d.ts.map +1 -0
  168. package/lib/utils/signing.js +229 -0
  169. package/lib/utils/signing.js.map +1 -0
  170. package/lib/utils/transportSecurity.d.ts +36 -0
  171. package/lib/utils/transportSecurity.d.ts.map +1 -0
  172. package/lib/utils/transportSecurity.js +74 -0
  173. package/lib/utils/transportSecurity.js.map +1 -0
  174. package/lib/utils/trustAnchor.d.ts +109 -0
  175. package/lib/utils/trustAnchor.d.ts.map +1 -0
  176. package/lib/utils/trustAnchor.js +169 -0
  177. package/lib/utils/trustAnchor.js.map +1 -0
  178. package/lib/utils/ui.d.ts +12 -0
  179. package/lib/utils/ui.d.ts.map +1 -0
  180. package/lib/utils/ui.js +60 -0
  181. package/lib/utils/ui.js.map +1 -0
  182. package/lib/utils/validate.d.ts +62 -0
  183. package/lib/utils/validate.d.ts.map +1 -0
  184. package/lib/utils/validate.js +189 -0
  185. package/lib/utils/validate.js.map +1 -0
  186. package/lib/vendor/ota-protocol/base64.d.ts +11 -0
  187. package/lib/vendor/ota-protocol/base64.d.ts.map +1 -0
  188. package/lib/vendor/ota-protocol/base64.js +39 -0
  189. package/lib/vendor/ota-protocol/base64.js.map +1 -0
  190. package/lib/vendor/ota-protocol/codec.d.ts +26 -0
  191. package/lib/vendor/ota-protocol/codec.d.ts.map +1 -0
  192. package/lib/vendor/ota-protocol/codec.js +349 -0
  193. package/lib/vendor/ota-protocol/codec.js.map +1 -0
  194. package/lib/vendor/ota-protocol/hex.d.ts +8 -0
  195. package/lib/vendor/ota-protocol/hex.d.ts.map +1 -0
  196. package/lib/vendor/ota-protocol/hex.js +31 -0
  197. package/lib/vendor/ota-protocol/hex.js.map +1 -0
  198. package/lib/vendor/ota-protocol/index.d.ts +17 -0
  199. package/lib/vendor/ota-protocol/index.d.ts.map +1 -0
  200. package/lib/vendor/ota-protocol/index.js +41 -0
  201. package/lib/vendor/ota-protocol/index.js.map +1 -0
  202. package/lib/vendor/ota-protocol/limits.d.ts +39 -0
  203. package/lib/vendor/ota-protocol/limits.d.ts.map +1 -0
  204. package/lib/vendor/ota-protocol/limits.js +42 -0
  205. package/lib/vendor/ota-protocol/limits.js.map +1 -0
  206. package/lib/vendor/ota-protocol/types.d.ts +73 -0
  207. package/lib/vendor/ota-protocol/types.d.ts.map +1 -0
  208. package/lib/vendor/ota-protocol/types.js +24 -0
  209. package/lib/vendor/ota-protocol/types.js.map +1 -0
  210. package/package.json +37 -0
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * HTTPS enforcement for everything the CLI authenticates to.
4
+ *
5
+ * WHAT WAS WRONG. The API client picked its transport straight off the URL
6
+ * scheme — `parsed.protocol === 'https:' ? https : http` — with nothing
7
+ * requiring HTTPS in the first place, and every request attaches
8
+ * `Authorization: Bearer <token>`. `scalebun login` accepts an arbitrary
9
+ * `--api-url` and POSTs an email and password to it. So a CLI pointed at an
10
+ * `http://` URL — by a typo, a copied CI snippet, or a tampered config —
11
+ * would put a personal access token, or the user's actual password, on the
12
+ * wire in cleartext, with no warning and no failure.
13
+ *
14
+ * THE RULE. Credentials only travel over TLS. The single exception is
15
+ * loopback, because local development against `http://localhost:3000` is a
16
+ * real workflow and traffic that never leaves the machine cannot be
17
+ * intercepted on the network. Anything else is refused before a socket is
18
+ * opened, rather than warned about — a warning printed above a successful
19
+ * request is not a control.
20
+ *
21
+ * Loopback is matched on resolved literals, not on name. `localhost` and the
22
+ * reserved 127/8 and ::1 literals are loopback by definition; an arbitrary
23
+ * hostname that merely happens to resolve to 127.0.0.1 today is not, because
24
+ * whoever controls the DNS answer controls where the token goes tomorrow.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.InsecureApiUrlError = void 0;
28
+ exports.assertSecureApiUrl = assertSecureApiUrl;
29
+ /** Hostnames that are loopback by specification rather than by resolution. */
30
+ const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
31
+ function isLoopback(hostname) {
32
+ const h = hostname.toLowerCase();
33
+ if (LOOPBACK_HOSTNAMES.has(h))
34
+ return true;
35
+ // The whole of 127.0.0.0/8 is loopback, not just 127.0.0.1.
36
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
37
+ }
38
+ class InsecureApiUrlError extends Error {
39
+ constructor(url, hostname) {
40
+ super(`Refusing to send credentials over an unencrypted connection to ${url}\n` +
41
+ ` The CLI attaches your access token to every request, and "http://" would\n` +
42
+ ` put it on the wire in cleartext where anyone on the network can read it.\n` +
43
+ `\n` +
44
+ ` Use https://${hostname} instead.\n` +
45
+ ` (Plain http:// is allowed only for localhost / 127.0.0.1 during local development.)`);
46
+ this.name = 'InsecureApiUrlError';
47
+ }
48
+ }
49
+ exports.InsecureApiUrlError = InsecureApiUrlError;
50
+ /**
51
+ * Validate an API base URL before it is used or persisted.
52
+ *
53
+ * @returns the normalised URL (trailing slashes stripped)
54
+ * @throws InsecureApiUrlError for a non-loopback `http://` URL
55
+ * @throws Error for a URL that is malformed or uses a non-HTTP scheme
56
+ */
57
+ function assertSecureApiUrl(rawUrl) {
58
+ let parsed;
59
+ try {
60
+ parsed = new URL(rawUrl);
61
+ }
62
+ catch {
63
+ throw new Error(`Not a valid API URL: ${rawUrl}`);
64
+ }
65
+ if (parsed.protocol === 'https:')
66
+ return rawUrl.replace(/\/+$/, '');
67
+ if (parsed.protocol === 'http:') {
68
+ if (isLoopback(parsed.hostname))
69
+ return rawUrl.replace(/\/+$/, '');
70
+ throw new InsecureApiUrlError(rawUrl, parsed.hostname);
71
+ }
72
+ throw new Error(`Unsupported API URL scheme "${parsed.protocol}" in ${rawUrl} — expected https://`);
73
+ }
74
+ //# sourceMappingURL=transportSecurity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transportSecurity.js","sourceRoot":"","sources":["../../src/utils/transportSecurity.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAiCH,gDAkBC;AAjDD,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAE/E,SAAS,UAAU,CAAC,QAAgB;IAClC,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,4DAA4D;IAC5D,OAAO,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,GAAW,EAAE,QAAgB;QACvC,KAAK,CACH,kEAAkE,GAAG,IAAI;YACvE,8EAA8E;YAC9E,8EAA8E;YAC9E,IAAI;YACJ,iBAAiB,QAAQ,aAAa;YACtC,uFAAuF,CAC1F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAZD,kDAYC;AAED;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,MAAc;IAC/C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEpE,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACnE,MAAM,IAAI,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,QAAQ,QAAQ,MAAM,sBAAsB,CACnF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The native trust anchor: the public half of your signing key, compiled into
3
+ * the app so the device can refuse anything it did not authorise.
4
+ *
5
+ * Each platform reads a different file from a different place, and both fail
6
+ * the same way when it is missing — an empty registry, which means every
7
+ * release is refused with UNKNOWN_KEY_ID.
8
+ *
9
+ * Android android/app/src/main/assets/scalebun_ota_keys.json
10
+ * In `assets/`, NOT `res/raw/`. The registry reads it with
11
+ * `context.assets.open()`; a file in res/raw is invisible to that
12
+ * call, so enforcement is silently off — the app still runs, still
13
+ * updates, and simply verifies nothing.
14
+ *
15
+ * iOS ios/<App>/ScaleBunOtaKeys.json
16
+ * Read with `Bundle.main.url(forResource:withExtension:)`, so it
17
+ * must be a member of the app target's Copy Bundle Resources phase.
18
+ * Writing the file is not enough: Xcode ships only what the target
19
+ * references, and a file sitting beside the sources that nobody
20
+ * added is not in the .app at all. isInXcodeResources() below exists
21
+ * to catch exactly that.
22
+ *
23
+ * The filenames differ by platform, and the iOS one is `.json`. Earlier CLI
24
+ * output told people to add `ScaleBunOtaKeys.plist`, which no code has ever
25
+ * read — following it produced UNKNOWN_KEY_ID with nothing to point at.
26
+ */
27
+ export interface TrustAnchor {
28
+ /**
29
+ * The PROJECT id, not the app id. The verifier compares this against the
30
+ * signed release's projectId and refuses on PROJECT_MISMATCH. Both ids are
31
+ * `cm…` cuids and are trivially confused, which is exactly why callers should
32
+ * be handed this value rather than asked for it.
33
+ */
34
+ projectId: string;
35
+ keys: Array<{
36
+ keyId: string;
37
+ publicKey: string;
38
+ }>;
39
+ }
40
+ export declare const ANDROID_ANCHOR_RELATIVE: string;
41
+ /** `ScaleBunOtaKeyRegistry.defaultResource` + ".json", on the iOS side. */
42
+ export declare const IOS_ANCHOR_FILENAME = "ScaleBunOtaKeys.json";
43
+ export interface WriteAnchorResult {
44
+ path: string;
45
+ /** Key ids trusted after the write, in file order. */
46
+ keyIds: string[];
47
+ /** True when an existing anchor was read and its other keys preserved. */
48
+ merged: boolean;
49
+ }
50
+ export interface WriteIosAnchorResult extends WriteAnchorResult {
51
+ /**
52
+ * False when project.pbxproj does not mention the file yet.
53
+ *
54
+ * This is the iOS-specific trap, and the reason the flag exists: the anchor
55
+ * can be written perfectly and still not ship, because Xcode copies only what
56
+ * the target references. The device then finds no resource, builds an empty
57
+ * registry, and refuses every release with UNKNOWN_KEY_ID — while a
58
+ * correct-looking file sits right there in the source tree.
59
+ */
60
+ inXcodeResources: boolean;
61
+ }
62
+ export declare function writeAndroidTrustAnchor(opts: {
63
+ cwd: string;
64
+ projectId: string;
65
+ keyId: string;
66
+ publicKeyPem: string;
67
+ }): WriteAnchorResult;
68
+ /** `ios/<App>.xcodeproj`, or null when there is no iOS project here. */
69
+ export declare function findXcodeProject(cwd: string): string | null;
70
+ /**
71
+ * The folder the app's own sources live in — `ios/<App>/`.
72
+ *
73
+ * Xcode groups mirror the filesystem here by React Native convention, so that
74
+ * is where a new resource belongs if it is going to be easy to add to the
75
+ * target. Falls back to whichever folder holds the AppDelegate, for projects
76
+ * renamed away from their directory.
77
+ */
78
+ export declare function iosAppSourceDir(cwd: string): string | null;
79
+ /**
80
+ * Is the anchor already referenced by the Xcode project?
81
+ *
82
+ * A substring check over project.pbxproj rather than a real parse: the question
83
+ * is only "has anyone added this filename", and erring toward "no" costs one
84
+ * extra printing of the instructions.
85
+ */
86
+ export declare function isInXcodeResources(cwd: string): boolean;
87
+ /**
88
+ * Add a key to the iOS trust anchor, creating the file if needed.
89
+ *
90
+ * Writes into `ios/<App>/` and reports whether the project references it yet.
91
+ * Adding it to the target is left to the caller: that means editing
92
+ * project.pbxproj, and a corrupted pbxproj costs far more than the single drag
93
+ * it would save.
94
+ */
95
+ export declare function writeIosTrustAnchor(opts: {
96
+ cwd: string;
97
+ projectId: string;
98
+ keyId: string;
99
+ publicKeyPem: string;
100
+ }): WriteIosAnchorResult;
101
+ /**
102
+ * Does this look like a native Android project we can write into? Used to skip
103
+ * the anchor with a clear message instead of scattering directories into
104
+ * whatever folder the user happened to run from.
105
+ */
106
+ export declare function hasAndroidProject(cwd: string): boolean;
107
+ /** The iOS counterpart: an `ios/` folder with an actual Xcode project in it. */
108
+ export declare function hasIosProject(cwd: string): boolean;
109
+ //# sourceMappingURL=trustAnchor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustAnchor.d.ts","sourceRoot":"","sources":["../../src/utils/trustAnchor.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED,eAAO,MAAM,uBAAuB,QAOnC,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D;;;;;;;;OAQG;IACH,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AA2CD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,iBAAiB,CAEpB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK3D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB1D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,oBAAoB,CASvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD"}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.IOS_ANCHOR_FILENAME = exports.ANDROID_ANCHOR_RELATIVE = void 0;
37
+ exports.writeAndroidTrustAnchor = writeAndroidTrustAnchor;
38
+ exports.findXcodeProject = findXcodeProject;
39
+ exports.iosAppSourceDir = iosAppSourceDir;
40
+ exports.isInXcodeResources = isInXcodeResources;
41
+ exports.writeIosTrustAnchor = writeIosTrustAnchor;
42
+ exports.hasAndroidProject = hasAndroidProject;
43
+ exports.hasIosProject = hasIosProject;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ exports.ANDROID_ANCHOR_RELATIVE = path.join('android', 'app', 'src', 'main', 'assets', 'scalebun_ota_keys.json');
47
+ /** `ScaleBunOtaKeyRegistry.defaultResource` + ".json", on the iOS side. */
48
+ exports.IOS_ANCHOR_FILENAME = 'ScaleBunOtaKeys.json';
49
+ /**
50
+ * Add a key to an anchor file, creating it if needed.
51
+ *
52
+ * Merges rather than overwrites: during a rotation two keys are valid at once,
53
+ * and clobbering the old one strands every device still running the previous
54
+ * build — they would refuse the next release and have no way back. A key with
55
+ * the same keyId is replaced in place so re-running is idempotent.
56
+ */
57
+ function writeAnchorAt(anchorPath, opts) {
58
+ fs.mkdirSync(path.dirname(anchorPath), { recursive: true });
59
+ let keys = [];
60
+ let merged = false;
61
+ if (fs.existsSync(anchorPath)) {
62
+ try {
63
+ const existing = JSON.parse(fs.readFileSync(anchorPath, 'utf8'));
64
+ if (Array.isArray(existing?.keys)) {
65
+ keys = existing.keys.filter((k) => k && typeof k.keyId === 'string' && typeof k.publicKey === 'string');
66
+ merged = keys.length > 0;
67
+ }
68
+ }
69
+ catch {
70
+ // Unreadable or hand-mangled: start clean rather than fail setup. The
71
+ // file is regenerable — the private key is the irreplaceable half.
72
+ keys = [];
73
+ }
74
+ }
75
+ keys = keys.filter((k) => k.keyId !== opts.keyId);
76
+ keys.push({ keyId: opts.keyId, publicKey: opts.publicKeyPem.trim() });
77
+ const anchor = { projectId: opts.projectId, keys };
78
+ fs.writeFileSync(anchorPath, JSON.stringify(anchor, null, 2) + '\n');
79
+ return { path: anchorPath, keyIds: keys.map((k) => k.keyId), merged };
80
+ }
81
+ function writeAndroidTrustAnchor(opts) {
82
+ return writeAnchorAt(path.join(opts.cwd, exports.ANDROID_ANCHOR_RELATIVE), opts);
83
+ }
84
+ /** `ios/<App>.xcodeproj`, or null when there is no iOS project here. */
85
+ function findXcodeProject(cwd) {
86
+ const iosDir = path.join(cwd, 'ios');
87
+ if (!fs.existsSync(iosDir))
88
+ return null;
89
+ const proj = fs.readdirSync(iosDir).find((name) => name.endsWith('.xcodeproj'));
90
+ return proj ? path.join(iosDir, proj) : null;
91
+ }
92
+ /**
93
+ * The folder the app's own sources live in — `ios/<App>/`.
94
+ *
95
+ * Xcode groups mirror the filesystem here by React Native convention, so that
96
+ * is where a new resource belongs if it is going to be easy to add to the
97
+ * target. Falls back to whichever folder holds the AppDelegate, for projects
98
+ * renamed away from their directory.
99
+ */
100
+ function iosAppSourceDir(cwd) {
101
+ const proj = findXcodeProject(cwd);
102
+ if (!proj)
103
+ return null;
104
+ const iosDir = path.join(cwd, 'ios');
105
+ const byName = path.join(iosDir, path.basename(proj, '.xcodeproj'));
106
+ if (fs.existsSync(byName) && fs.statSync(byName).isDirectory())
107
+ return byName;
108
+ for (const entry of fs.readdirSync(iosDir)) {
109
+ const dir = path.join(iosDir, entry);
110
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
111
+ continue;
112
+ const hasDelegate = fs
113
+ .readdirSync(dir)
114
+ .some((f) => /^AppDelegate\.(swift|mm?|h)$/.test(f));
115
+ if (hasDelegate)
116
+ return dir;
117
+ }
118
+ return null;
119
+ }
120
+ /**
121
+ * Is the anchor already referenced by the Xcode project?
122
+ *
123
+ * A substring check over project.pbxproj rather than a real parse: the question
124
+ * is only "has anyone added this filename", and erring toward "no" costs one
125
+ * extra printing of the instructions.
126
+ */
127
+ function isInXcodeResources(cwd) {
128
+ const proj = findXcodeProject(cwd);
129
+ if (!proj)
130
+ return false;
131
+ const pbx = path.join(proj, 'project.pbxproj');
132
+ if (!fs.existsSync(pbx))
133
+ return false;
134
+ try {
135
+ return fs.readFileSync(pbx, 'utf8').includes(exports.IOS_ANCHOR_FILENAME);
136
+ }
137
+ catch {
138
+ return false;
139
+ }
140
+ }
141
+ /**
142
+ * Add a key to the iOS trust anchor, creating the file if needed.
143
+ *
144
+ * Writes into `ios/<App>/` and reports whether the project references it yet.
145
+ * Adding it to the target is left to the caller: that means editing
146
+ * project.pbxproj, and a corrupted pbxproj costs far more than the single drag
147
+ * it would save.
148
+ */
149
+ function writeIosTrustAnchor(opts) {
150
+ const dir = iosAppSourceDir(opts.cwd);
151
+ if (!dir) {
152
+ throw new Error('No ios/<App> source directory found — is this a React Native project with a native iOS app?');
153
+ }
154
+ const res = writeAnchorAt(path.join(dir, exports.IOS_ANCHOR_FILENAME), opts);
155
+ return { ...res, inXcodeResources: isInXcodeResources(opts.cwd) };
156
+ }
157
+ /**
158
+ * Does this look like a native Android project we can write into? Used to skip
159
+ * the anchor with a clear message instead of scattering directories into
160
+ * whatever folder the user happened to run from.
161
+ */
162
+ function hasAndroidProject(cwd) {
163
+ return fs.existsSync(path.join(cwd, 'android', 'app'));
164
+ }
165
+ /** The iOS counterpart: an `ios/` folder with an actual Xcode project in it. */
166
+ function hasIosProject(cwd) {
167
+ return iosAppSourceDir(cwd) !== null;
168
+ }
169
+ //# sourceMappingURL=trustAnchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustAnchor.js","sourceRoot":"","sources":["../../src/utils/trustAnchor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The status box as a string.
3
+ *
4
+ * The interactive session redraws the whole screen on every keypress, and it
5
+ * does that with ONE write — a sequence of console.log calls would tear
6
+ * visibly as the terminal painted each line. Returning text lets the session
7
+ * compose a frame; `renderStatus` below keeps the print-and-exit path
8
+ * unchanged, so the two can never drift.
9
+ */
10
+ export declare function statusText(): string;
11
+ export declare function renderStatus(): void;
12
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/utils/ui.ts"],"names":[],"mappings":"AAYA;;;;;;;;GAQG;AACH,wBAAgB,UAAU,IAAI,MAAM,CA8BnC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.statusText = statusText;
7
+ exports.renderStatus = renderStatus;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const credentials_1 = require("./credentials");
10
+ // NOTE: the splash art (mark + wordmark) now lives in ../ui/banner.ts. This file
11
+ // keeps only the status box and command table — the body of the no-arg command.
12
+ // Column arithmetic must measure the RENDERED width, not the string length —
13
+ // coloured content carries ANSI escapes that take zero columns. Strip them first,
14
+ // or the right border lands on a different column on every row.
15
+ const STRIP_ANSI = /\x1b\[[0-9;]*m/g;
16
+ const visibleLen = (s) => s.replace(STRIP_ANSI, '').length;
17
+ /**
18
+ * The status box as a string.
19
+ *
20
+ * The interactive session redraws the whole screen on every keypress, and it
21
+ * does that with ONE write — a sequence of console.log calls would tear
22
+ * visibly as the terminal painted each line. Returning text lets the session
23
+ * compose a frame; `renderStatus` below keeps the print-and-exit path
24
+ * unchanged, so the two can never drift.
25
+ */
26
+ function statusText() {
27
+ const out = [];
28
+ const creds = (0, credentials_1.readCredentials)();
29
+ const verbose = process.argv.slice(2).includes('--verbose');
30
+ const inner = 58;
31
+ const border = (l, r) => out.push(chalk_1.default.gray(l + '─'.repeat(inner + 2) + r));
32
+ const row = (content) => {
33
+ const pad = ' '.repeat(Math.max(0, inner - visibleLen(content)));
34
+ out.push(`${chalk_1.default.gray('│')} ${content}${pad} ${chalk_1.default.gray('│')}`);
35
+ };
36
+ border('┌', '┐');
37
+ if (creds) {
38
+ row(chalk_1.default.bold.green('● AUTHENTICATED'));
39
+ // The account you're signed in as (captured at login; absent for pre-1.10.1
40
+ // logins and the CI env-var path).
41
+ if (creds.email)
42
+ row(chalk_1.default.dim(creds.email));
43
+ // Show the API target only when it is NOT the default production backend
44
+ // (someone pointed at staging / self-hosted / localhost needs to see where
45
+ // their deploy is going), or under --verbose. On production it is noise.
46
+ if (verbose || creds.apiUrl !== credentials_1.DEFAULT_API_URL) {
47
+ row(`${chalk_1.default.dim('API')} ${chalk_1.default.cyan(creds.apiUrl)}`);
48
+ }
49
+ }
50
+ else {
51
+ row(`${chalk_1.default.bold.yellow('○ NOT LOGGED IN')} ${chalk_1.default.dim('· run')} ${chalk_1.default.cyan('scalebun login')}`);
52
+ }
53
+ border('└', '┘');
54
+ out.push('');
55
+ return out.join('\n') + '\n';
56
+ }
57
+ function renderStatus() {
58
+ process.stdout.write(statusText());
59
+ }
60
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/utils/ui.ts"],"names":[],"mappings":";;;;;AAqBA,gCA8BC;AAED,oCAEC;AAvDD,kDAA0B;AAC1B,+CAAiE;AAEjE,iFAAiF;AACjF,gFAAgF;AAEhF,6EAA6E;AAC7E,kFAAkF;AAClF,gEAAgE;AAChE,MAAM,UAAU,GAAG,iBAAiB,CAAC;AACrC,MAAM,UAAU,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;AAE3E;;;;;;;;GAQG;AACH,SAAgB,UAAU;IACxB,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,IAAA,6BAAe,GAAE,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,EAAE,CAAC;IAEjB,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,eAAK,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjE,GAAG,CAAC,IAAI,CAAC,GAAG,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,GAAG,IAAI,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC;IAEF,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjB,IAAI,KAAK,EAAE,CAAC;QACV,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACzC,4EAA4E;QAC5E,mCAAmC;QACnC,IAAI,KAAK,CAAC,KAAK;YAAE,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,6BAAe,EAAE,CAAC;YAChD,GAAG,CAAC,GAAG,eAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,eAAK,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,eAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACb,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAC/B,CAAC;AAED,SAAgB,YAAY;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,62 @@
1
+ /** The allowed value closest to `raw`, when one is close enough to mean it. */
2
+ export declare function nearest(raw: string, allowed: readonly string[]): string | null;
3
+ export interface EnumOpts {
4
+ /** Accept any letter case, normalising to the allowed spelling. */
5
+ caseInsensitive?: boolean;
6
+ /** Extra guidance printed under the error. */
7
+ details?: string[];
8
+ }
9
+ /**
10
+ * One of `allowed`, or exit.
11
+ *
12
+ * Replaces `if (opts.x === 'known')` chains, where an unrecognised value is
13
+ * indistinguishable from the flag not being passed at all.
14
+ */
15
+ export declare function checkedEnum<T extends string>(flag: string, raw: string, allowed: readonly T[], opts?: EnumOpts): T;
16
+ export interface IntOpts {
17
+ min?: number;
18
+ max?: number;
19
+ /** Used when the flag was omitted entirely. Never used to paper over junk. */
20
+ fallback?: number;
21
+ details?: string[];
22
+ }
23
+ /**
24
+ * A whole number in range, or exit.
25
+ *
26
+ * `Number('12abc')` is NaN and `Number('')` is 0 — both of which a `|| default`
27
+ * turns into a plausible-looking value the user never asked for. Everything
28
+ * that is not cleanly an integer is refused here instead.
29
+ */
30
+ export declare function checkedInt(flag: string, raw: unknown, opts?: IntOpts): number;
31
+ /**
32
+ * A rollout percentage, 0-100.
33
+ *
34
+ * A trailing `%` is accepted because it is the obvious thing to type for a
35
+ * value the help text calls a percentage, and refusing it teaches nothing.
36
+ */
37
+ export declare function checkedPercent(flag: string, raw: unknown, fallback?: number): number;
38
+ /**
39
+ * An identifier, or exit.
40
+ *
41
+ * Catches the two ways ids arrive broken: an empty value from an unquoted
42
+ * shell variable that did not expand (`--app-id $APP` with APP unset becomes
43
+ * `--app-id` swallowing the NEXT flag), and a value with whitespace or a
44
+ * leading dash from a mis-quoted paste. Both otherwise reach the API and come
45
+ * back as a confusing 404.
46
+ */
47
+ export declare function checkedId(flag: string, raw: unknown): string;
48
+ /**
49
+ * A semver range the server will accept, or exit.
50
+ *
51
+ * Deliberately loose. Ranges legitimately look like `1.0.0`, `^1.0.0`,
52
+ * `>=1.0.0`, `1.x`, `>=1.0.0 <2.0.0` and `*`, and a strict grammar here would
53
+ * reject valid input the API would have taken. The check is only for the
54
+ * shapes that are certainly wrong — empty, or containing no version at all —
55
+ * because those come back as a 400 describing a field the user never typed.
56
+ */
57
+ export declare function checkedVersionRange(flag: string, raw: unknown): string;
58
+ /** Non-empty free text, or exit. Trims, so a whitespace-only value is empty. */
59
+ export declare function checkedText(flag: string, raw: unknown, opts?: {
60
+ max?: number;
61
+ }): string;
62
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAoDA,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAc9E;AAED,MAAM,WAAW,QAAQ;IACvB,mEAAmE;IACnE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,IAAI,GAAE,QAAa,GAClB,CAAC,CAcH;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,GAAE,OAAY,GAAG,MAAM,CAgCjF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAgB5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAiBtE;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAO3F"}