@limrun/ui 0.13.3-rc.1 → 0.13.3

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 (50) hide show
  1. package/dist/app-store-relay/index.cjs +4 -4
  2. package/dist/app-store-relay/index.js +175 -119
  3. package/dist/app-store-relay/react.cjs +1 -1
  4. package/dist/app-store-relay/react.js +2 -2
  5. package/dist/{browser-storage-BJ__DGJ6.mjs → browser-storage-Cx9eW-bm.mjs} +48 -45
  6. package/dist/browser-storage-jJaIht4K.js +1 -0
  7. package/dist/{client-CnbCWvCs.js → client-ASLtHFLh.js} +1 -1
  8. package/dist/{client-Bk1N3XIF.mjs → client-CGdh8yoJ.mjs} +33 -27
  9. package/dist/core/device-install/apple/provisioning.d.ts +4 -4
  10. package/dist/device-build/index.cjs +1 -1
  11. package/dist/device-build/index.js +5 -5
  12. package/dist/device-build/react.cjs +1 -1
  13. package/dist/device-build/react.js +15 -13
  14. package/dist/device-install/demo/demo.d.ts +0 -0
  15. package/dist/device-install/index.cjs +1 -1
  16. package/dist/device-install/index.d.ts +1 -1
  17. package/dist/device-install/index.js +134 -121
  18. package/dist/device-install/react.cjs +1 -1
  19. package/dist/device-install/react.js +55 -50
  20. package/dist/{index-BXg7HdIs.mjs → index-DFdCvi2_.mjs} +3 -7
  21. package/dist/limbuild-client-CgEncrgx.js +1 -0
  22. package/dist/{limbuild-client-CFJhYsRx.mjs → limbuild-client-D7U7w342.mjs} +4 -2
  23. package/dist/{provisioning-CdseoMJQ.mjs → provisioning-DKbkBO-p.mjs} +11 -11
  24. package/dist/{provisioning-D2ZZQeyX.js → provisioning-Va0PH4Kk.js} +1 -1
  25. package/package.json +1 -1
  26. package/src/app-store-relay/index.test.ts +1 -5
  27. package/src/app-store-relay/index.ts +109 -42
  28. package/src/app-store-relay/react.ts +6 -2
  29. package/src/core/device-install/apple/client.ts +29 -22
  30. package/src/core/device-install/apple/crypto.ts +9 -6
  31. package/src/core/device-install/apple/gsa-srp.ts +3 -7
  32. package/src/core/device-install/apple/provisioning.ts +16 -18
  33. package/src/core/device-install/apple/relay.ts +5 -1
  34. package/src/core/device-install/operations/limbuild-client.ts +8 -1
  35. package/src/core/device-install/operations/operations.ts +3 -1
  36. package/src/core/device-install/operations/relay-client.ts +57 -27
  37. package/src/core/device-install/operations/usbmux.ts +3 -3
  38. package/src/core/device-install/operations/webusb.ts +9 -4
  39. package/src/core/device-install/storage/browser-storage.ts +20 -6
  40. package/src/core/device-install/types.ts +1 -7
  41. package/src/device-build/react.ts +4 -1
  42. package/src/device-install/README.md +423 -0
  43. package/src/device-install/demo/demo.css +493 -0
  44. package/src/device-install/demo/demo.tsx +1069 -0
  45. package/src/device-install/demo/index.html +12 -0
  46. package/src/device-install/index.ts +7 -7
  47. package/src/device-install/react.ts +26 -17
  48. package/src/index.ts +1 -1
  49. package/dist/browser-storage-C1jQLXat.js +0 -1
  50. package/dist/limbuild-client-C_CMNLYV.js +0 -1
@@ -1,88 +1,93 @@
1
- import { useState as d, useRef as b, useCallback as u, useEffect as D } from "react";
2
- import { closeDeviceRelayTarget as l, requestUSBAccess as N, pairDevice as q, startDeviceInstall as C } from "./index.js";
3
- import { j as T, k as j } from "../browser-storage-BJ__DGJ6.mjs";
1
+ import { useState as u, useRef as h, useCallback as l, useEffect as D } from "react";
2
+ import { closeDeviceRelayTarget as d, requestUSBAccess as N, pairDevice as q, startDeviceInstall as C } from "./index.js";
3
+ import { j as T, k as j } from "../browser-storage-Cx9eW-bm.mjs";
4
4
  function F({
5
5
  apiUrl: a,
6
6
  token: v,
7
- log: e = x
7
+ log: R = x
8
8
  }) {
9
- const [r, m] = d(), [o, y] = d(), [h, n] = d(), [P, c] = d(), [S, f] = d(!1), w = b(void 0), R = b(void 0), i = u(async () => {
10
- w.current?.close(), w.current = void 0, await l(R.current, e);
11
- }, [e]);
9
+ const [e, m] = u(), [n, y] = u(), [g, s] = u(), [P, i] = u(), [S, f] = u(!1), w = h(void 0), b = h(void 0), r = h(R);
10
+ r.current = R;
11
+ const c = l(async () => {
12
+ w.current?.close(), w.current = void 0, await d(b.current, r.current);
13
+ }, []);
12
14
  D(() => {
13
- R.current = r;
14
- }, [r]), D(() => () => {
15
- i();
16
- }, [i]);
17
- const A = u(async () => {
18
- n("usb"), c(void 0), f(!1);
15
+ b.current = e;
16
+ }, [e]), D(() => () => {
17
+ c();
18
+ }, [c]);
19
+ const A = l(async () => {
20
+ s("usb"), i(void 0), f(!1);
19
21
  let t;
20
22
  try {
21
- await i(), t = await N({ log: e });
22
- const s = await T(t.hello.serialNumber);
23
- return m(t), y(s), e(s ? "Pair record found" : "No pair record found", t.hello.serialNumber), t;
24
- } catch (s) {
25
- await l(t, e), m(void 0), y(void 0), c(p(s));
23
+ await c(), t = await N({ log: r.current });
24
+ const o = await T(t.hello.serialNumber);
25
+ return m(t), y(o), r.current(
26
+ o ? "Pair record found" : "No pair record found",
27
+ t.hello.serialNumber
28
+ ), t;
29
+ } catch (o) {
30
+ await d(t, r.current), m(void 0), y(void 0), i(p(o));
26
31
  return;
27
32
  } finally {
28
- n(void 0);
33
+ s(void 0);
29
34
  }
30
- }, [i, e]), B = u(async () => {
31
- if (!a || !r)
35
+ }, [c]), B = l(async () => {
36
+ if (!a || !e)
32
37
  throw new Error("Select a USB device before pairing.");
33
- n("pair"), c(void 0), f(!1);
38
+ s("pair"), i(void 0), f(!1);
34
39
  try {
35
- await i();
40
+ await c();
36
41
  const t = await q({
37
42
  limbuildApiUrl: a,
38
43
  token: v,
39
- log: e,
40
- target: r
41
- }), s = await j(t.pairRecord, {
42
- productName: r.hello.productName
44
+ log: r.current,
45
+ target: e
46
+ }), o = await j(t.pairRecord, {
47
+ productName: e.hello.productName
43
48
  });
44
- return t.relay.close(), await l(r, e), y(s), f(!1), e("Device paired", "The pair record was stored locally in this browser."), s;
49
+ return t.relay.close(), await d(e, r.current), y(o), f(!1), r.current("Device paired", "The pair record was stored locally in this browser."), o;
45
50
  } catch (t) {
46
- await l(r, e), f(!0), c(p(t));
51
+ await d(e, r.current), f(!0), i(p(t));
47
52
  return;
48
53
  } finally {
49
- n(void 0);
54
+ s(void 0);
50
55
  }
51
- }, [a, i, r, e, v]), E = u(async () => {
52
- if (!a || !r || !o)
56
+ }, [a, c, e, v]), E = l(async () => {
57
+ if (!a || !e || !n)
53
58
  throw new Error("Select and pair a USB device before starting installation.");
54
- n("install"), c(void 0);
59
+ s("install"), i(void 0);
55
60
  try {
56
- return await i(), w.current = await C({
61
+ return await c(), w.current = await C({
57
62
  limbuildApiUrl: a,
58
63
  token: v,
59
- log: e,
60
- target: r,
61
- pairRecord: o
62
- }), e("Device install started", "Installation will continue through the connected iPhone."), w.current;
64
+ log: r.current,
65
+ target: e,
66
+ pairRecord: n
67
+ }), r.current("Device install started", "Installation will continue through the connected iPhone."), w.current;
63
68
  } catch (t) {
64
- await l(r, e), c(p(t));
69
+ await d(e, r.current), i(p(t));
65
70
  return;
66
71
  } finally {
67
- n(void 0);
72
+ s(void 0);
68
73
  }
69
- }, [a, i, r, e, o, v]), I = u(() => {
70
- i(), e("Device relay stopped");
71
- }, [i, e]);
74
+ }, [a, c, e, n, v]), I = l(() => {
75
+ c(), r.current("Device relay stopped");
76
+ }, [c]);
72
77
  return {
73
- device: r,
74
- pairRecord: o,
75
- busyAction: h,
78
+ device: e,
79
+ pairRecord: n,
80
+ busyAction: g,
76
81
  error: P,
77
82
  pairConfirmationRequired: S,
78
- hasPairRecord: !!o,
79
- canPair: !!a && !h && !!r,
80
- canInstall: !!a && !h && !!r && !!o,
83
+ hasPairRecord: !!n,
84
+ canPair: !!a && !g && !!e,
85
+ canInstall: !!a && !g && !!e && !!n,
81
86
  requestUSBAccess: A,
82
87
  pairBrowser: B,
83
88
  startInstallation: E,
84
89
  stopRelay: I,
85
- clearError: () => c(void 0)
90
+ clearError: () => i(void 0)
86
91
  };
87
92
  }
88
93
  function x() {
@@ -228,13 +228,9 @@ class kn {
228
228
  async function vn(t, e, r, d) {
229
229
  let T = new Uint8Array(await wt.hash(Na.h, Vt(Ir(e))));
230
230
  t === "s2k_fo" && (T = Ir(wt.toHex(T)));
231
- const x = await crypto.subtle.importKey(
232
- "raw",
233
- Vt(T),
234
- { name: "PBKDF2" },
235
- !1,
236
- ["deriveBits"]
237
- ), l = await crypto.subtle.deriveBits(
231
+ const x = await crypto.subtle.importKey("raw", Vt(T), { name: "PBKDF2" }, !1, [
232
+ "deriveBits"
233
+ ]), l = await crypto.subtle.deriveBits(
238
234
  {
239
235
  name: "PBKDF2",
240
236
  hash: { name: "SHA-256" },
@@ -0,0 +1 @@
1
+ "use strict";async function l(o,t){const a=new URL(`${o}/info`);t&&a.searchParams.set("token",t);const e=await fetch(a.toString(),{headers:t?{Authorization:`Bearer ${t}`}:void 0});if(!e.ok){const n=await e.text();throw new Error(`Info request failed: HTTP ${e.status} ${n}`)}return await e.json()}async function u({limbuildApiUrl:o,token:t,certificateP12Base64:a,certificatePassword:e,provisioningProfileBase64:n}){const d=new URL(`${o}/exec`);t&&d.searchParams.set("token",t);const r=await fetch(d.toString(),{method:"POST",headers:{"Content-Type":"application/json",...t?{Authorization:`Bearer ${t}`}:{}},body:JSON.stringify({command:"xcodebuild",xcodebuild:{sdk:"iphoneos"},signing:{certificateP12Base64:a,...e?{certificatePassword:e}:{},provisioningProfileBase64:n}})});if(!r.ok){const s=await r.text();throw new Error(`Build request failed: HTTP ${r.status} ${s}`)}return await r.json()}async function f({limbuildApiUrl:o,execId:t,token:a}){const e=new URL(`${o}/exec/${encodeURIComponent(t)}/ios/ota`);a&&e.searchParams.set("token",a);const n=await fetch(e.toString(),{headers:a?{Authorization:`Bearer ${a}`}:void 0});if(!n.ok){const d=await n.text();throw new Error(`OTA install metadata failed: HTTP ${n.status} ${d}`)}return await n.json()}function h({limbuildApiUrl:o,execId:t,token:a,onLine:e,onStatus:n,onError:d}){const r=new URL(`${o}/exec/${t}/events`);a&&r.searchParams.set("token",a);const s=new EventSource(r.toString());return n("running"),s.addEventListener("command",i=>e({type:"command",data:i.data})),s.addEventListener("stdout",i=>e({type:"stdout",data:i.data})),s.addEventListener("stderr",i=>e({type:"stderr",data:i.data})),s.addEventListener("exitCode",i=>{const c=parseInt(i.data,10);n(c===0?"succeeded":c<0?"cancelled":"failed"),s.close()}),s.onerror=()=>{s.close(),n("failed"),d?.(new Error("Build log stream closed before completion."))},()=>s.close()}exports.fetchLimbuildInfo=l;exports.getIOSOTAInstall=f;exports.startSignedDeviceBuild=u;exports.watchBuildLogEvents=h;
@@ -66,9 +66,11 @@ function w({
66
66
  const s = new EventSource(r.toString());
67
67
  return n("running"), s.addEventListener("command", (i) => e({ type: "command", data: i.data })), s.addEventListener("stdout", (i) => e({ type: "stdout", data: i.data })), s.addEventListener("stderr", (i) => e({ type: "stderr", data: i.data })), s.addEventListener("exitCode", (i) => {
68
68
  const c = parseInt(i.data, 10);
69
- n(c === 0 ? "succeeded" : c < 0 ? "cancelled" : "failed"), s.close();
69
+ n(
70
+ c === 0 ? "succeeded" : c < 0 ? "cancelled" : "failed"
71
+ ), s.close();
70
72
  }), s.onerror = () => {
71
- s.close(), d?.(new Error("Build log stream closed before completion."));
73
+ s.close(), n("failed"), d?.(new Error("Build log stream closed before completion."));
72
74
  }, () => s.close();
73
75
  }
74
76
  export {
@@ -1,4 +1,4 @@
1
- import { e as c, a as u, n as d, b as f, c as l, i as r } from "./browser-storage-BJ__DGJ6.mjs";
1
+ import { e as c, a as u, n as d, b as f, c as l, i as r } from "./browser-storage-Cx9eW-bm.mjs";
2
2
  function g() {
3
3
  return {
4
4
  method: "POST",
@@ -6,10 +6,16 @@ function g() {
6
6
  payload: {}
7
7
  };
8
8
  }
9
- function v({ bundleID: e, teamID: i = "" }) {
9
+ function v({
10
+ bundleID: e,
11
+ teamID: i = ""
12
+ }) {
10
13
  return n("/account/ios/identifiers/listAppIds.action", i, { sort: "name=asc" });
11
14
  }
12
- function P({ deviceUDID: e, teamID: i = "" }) {
15
+ function P({
16
+ deviceUDID: e,
17
+ teamID: i = ""
18
+ }) {
13
19
  return n("/account/ios/device/listDevices.action", i, {
14
20
  sort: "name=asc",
15
21
  includeRemovedDevices: !1
@@ -79,10 +85,7 @@ function q({
79
85
  }
80
86
  };
81
87
  }
82
- function b({
83
- csrPEM: e,
84
- teamID: i = ""
85
- }) {
88
+ function b({ csrPEM: e, teamID: i = "" }) {
86
89
  return {
87
90
  method: "POST",
88
91
  path: "/account/ios/certificate/submitCertificateRequest.action",
@@ -93,10 +96,7 @@ function b({
93
96
  }
94
97
  };
95
98
  }
96
- function C({
97
- csrPEM: e,
98
- teamID: i = ""
99
- }) {
99
+ function C({ csrPEM: e, teamID: i = "" }) {
100
100
  return {
101
101
  method: "POST",
102
102
  path: "/account/ios/certificate/submitCertificateRequest.action",
@@ -1 +1 @@
1
- "use strict";const a=require("./browser-storage-C1jQLXat.js");function c(){return{method:"POST",path:"/account/listTeams.action",payload:{}}}function u({bundleID:e,teamID:t=""}){return s("/account/ios/identifiers/listAppIds.action",t,{sort:"name=asc"})}function d({deviceUDID:e,teamID:t=""}){return s("/account/ios/device/listDevices.action",t,{sort:"name=asc",includeRemovedDevices:!1})}function f(e=""){return s("/account/ios/certificate/listCertRequests.action",e,{sort:"name=asc",types:"83Q87W3TGH,5QPB9NHCEI"})}function l(e=""){return s("/account/ios/certificate/listCertRequests.action",e,{sort:"name=asc",types:"WXV89964HE,R58UK2EWSO"})}function p({bundleID:e,teamID:t=""}){return s("/account/ios/profile/listProvisioningProfiles.action",t,{search:e,sort:"name=asc"})}function m({bundleID:e,teamID:t=""}){return s("/account/ios/profile/listProvisioningProfiles.action",t,{search:e,sort:"name=asc",distributionType:"adhoc"})}function R({deviceUDID:e,teamID:t="",name:i="Limrun iPhone"}){return{method:"POST",path:"/account/ios/device/addDevices.action",payload:{teamId:t,deviceNames:i,deviceNumbers:a.normalizeUDID(e),deviceClasses:"iphone",register:"single"}}}function g({bundleID:e,teamID:t="",name:i}){return{method:"POST",path:"/account/ios/identifiers/addAppId.action",payload:{teamId:t,name:i??e,identifier:e,type:"explicit"}}}function q({csrPEM:e,teamID:t=""}){return{method:"POST",path:"/account/ios/certificate/submitCertificateRequest.action",payload:{teamId:t,type:"83Q87W3TGH",csrContent:e}}}function v({csrPEM:e,teamID:t=""}){return{method:"POST",path:"/account/ios/certificate/submitCertificateRequest.action",payload:{teamId:t,type:"WXV89964HE",csrContent:e}}}function P(e,t=""){return{method:"GET",path:"/account/ios/certificate/downloadCertificateContent.action",payload:{teamId:t,certificateId:e,type:"83Q87W3TGH"}}}function D(e,t=""){return{method:"GET",path:"/account/ios/certificate/downloadCertificateContent.action",payload:{teamId:t,certificateId:e,type:"WXV89964HE"}}}function I({bundleID:e,teamID:t="",appIDID:i,certificateID:o,deviceIDs:n,name:r}){return{method:"POST",path:"/account/ios/profile/createProvisioningProfile.action",payload:{teamId:t,provisioningProfileName:r??`Limrun ${e}`,certificateIds:[o],appIdId:i,deviceIds:n,distributionType:"limited",subPlatform:"ios"}}}function h({bundleID:e,teamID:t="",appIDID:i,certificateID:o,deviceIDs:n,name:r}){return{method:"POST",path:"/account/ios/profile/createProvisioningProfile.action",payload:{teamId:t,provisioningProfileName:r??`Limrun Ad Hoc ${e}`,certificateIds:[o],appIdId:i,deviceIds:n,distributionType:"adhoc",subPlatform:"ios"}}}function C(e,t=""){return{method:"GET",path:"/account/ios/profile/downloadProfileContent",payload:{teamId:t,provisioningProfileId:e}}}async function b({bundleID:e,deviceUDID:t,teamID:i,signingMode:o}){const n=await a.getSigningAssets({bundleID:e,deviceUDID:t,signingMode:o});if(!(!n||!S(n,{bundleID:e,deviceUDID:t,teamID:i,signingMode:o})))return n}async function y(e){return a.putSigningAssets({...e,certificateFileName:e.certificateID?`${e.certificateID}.p12`:`apple-${e.signingMode??"development"}.p12`,certificatePassword:e.certificatePassword||void 0,signingMode:e.signingMode,profileFileName:e.profile.uuid?`${e.profile.uuid}.mobileprovision`:`${e.signingMode??"development"}.mobileprovision`})}function S(e,{bundleID:t,deviceUDID:i,teamID:o,signingMode:n="development"}){return(e.signingMode??"development")!==n||!a.profileMatchesBundleID(e.profile,t)||o&&e.teamID&&e.teamID!==o||i&&!a.profileContainsDevice(e.profile,i)||e.profile.expirationDate&&new Date(e.profile.expirationDate).getTime()<=Date.now()?!1:a.normalizeBundleID(e.bundleID)===a.normalizeBundleID(t)}function s(e,t,i={}){const o={pageNumber:1,pageSize:200,...i};return t&&(o.teamId=t),{method:"POST",path:e,payload:o}}exports.createAdHocProfileRequest=h;exports.createBundleIDRequest=g;exports.createDevelopmentProfileRequest=I;exports.downloadCertificateRequest=P;exports.downloadDistributionCertificateRequest=D;exports.downloadProfileRequest=C;exports.findAdHocProfilesRequest=m;exports.findBundleIDRequest=u;exports.findDevelopmentCertificatesRequest=f;exports.findDevelopmentProfilesRequest=p;exports.findDeviceRequest=d;exports.findDistributionCertificatesRequest=l;exports.getReusableAppleSigningAssets=b;exports.listTeamsRequest=c;exports.putAppleGeneratedSigningAssets=y;exports.registerDeviceRequest=R;exports.submitDevelopmentCSRRequest=q;exports.submitDistributionCSRRequest=v;
1
+ "use strict";const a=require("./browser-storage-jJaIht4K.js");function c(){return{method:"POST",path:"/account/listTeams.action",payload:{}}}function u({bundleID:e,teamID:t=""}){return s("/account/ios/identifiers/listAppIds.action",t,{sort:"name=asc"})}function d({deviceUDID:e,teamID:t=""}){return s("/account/ios/device/listDevices.action",t,{sort:"name=asc",includeRemovedDevices:!1})}function f(e=""){return s("/account/ios/certificate/listCertRequests.action",e,{sort:"name=asc",types:"83Q87W3TGH,5QPB9NHCEI"})}function l(e=""){return s("/account/ios/certificate/listCertRequests.action",e,{sort:"name=asc",types:"WXV89964HE,R58UK2EWSO"})}function p({bundleID:e,teamID:t=""}){return s("/account/ios/profile/listProvisioningProfiles.action",t,{search:e,sort:"name=asc"})}function m({bundleID:e,teamID:t=""}){return s("/account/ios/profile/listProvisioningProfiles.action",t,{search:e,sort:"name=asc",distributionType:"adhoc"})}function R({deviceUDID:e,teamID:t="",name:i="Limrun iPhone"}){return{method:"POST",path:"/account/ios/device/addDevices.action",payload:{teamId:t,deviceNames:i,deviceNumbers:a.normalizeUDID(e),deviceClasses:"iphone",register:"single"}}}function g({bundleID:e,teamID:t="",name:i}){return{method:"POST",path:"/account/ios/identifiers/addAppId.action",payload:{teamId:t,name:i??e,identifier:e,type:"explicit"}}}function q({csrPEM:e,teamID:t=""}){return{method:"POST",path:"/account/ios/certificate/submitCertificateRequest.action",payload:{teamId:t,type:"83Q87W3TGH",csrContent:e}}}function v({csrPEM:e,teamID:t=""}){return{method:"POST",path:"/account/ios/certificate/submitCertificateRequest.action",payload:{teamId:t,type:"WXV89964HE",csrContent:e}}}function P(e,t=""){return{method:"GET",path:"/account/ios/certificate/downloadCertificateContent.action",payload:{teamId:t,certificateId:e,type:"83Q87W3TGH"}}}function D(e,t=""){return{method:"GET",path:"/account/ios/certificate/downloadCertificateContent.action",payload:{teamId:t,certificateId:e,type:"WXV89964HE"}}}function I({bundleID:e,teamID:t="",appIDID:i,certificateID:o,deviceIDs:n,name:r}){return{method:"POST",path:"/account/ios/profile/createProvisioningProfile.action",payload:{teamId:t,provisioningProfileName:r??`Limrun ${e}`,certificateIds:[o],appIdId:i,deviceIds:n,distributionType:"limited",subPlatform:"ios"}}}function h({bundleID:e,teamID:t="",appIDID:i,certificateID:o,deviceIDs:n,name:r}){return{method:"POST",path:"/account/ios/profile/createProvisioningProfile.action",payload:{teamId:t,provisioningProfileName:r??`Limrun Ad Hoc ${e}`,certificateIds:[o],appIdId:i,deviceIds:n,distributionType:"adhoc",subPlatform:"ios"}}}function C(e,t=""){return{method:"GET",path:"/account/ios/profile/downloadProfileContent",payload:{teamId:t,provisioningProfileId:e}}}async function b({bundleID:e,deviceUDID:t,teamID:i,signingMode:o}){const n=await a.getSigningAssets({bundleID:e,deviceUDID:t,signingMode:o});if(!(!n||!S(n,{bundleID:e,deviceUDID:t,teamID:i,signingMode:o})))return n}async function y(e){return a.putSigningAssets({...e,certificateFileName:e.certificateID?`${e.certificateID}.p12`:`apple-${e.signingMode??"development"}.p12`,certificatePassword:e.certificatePassword||void 0,signingMode:e.signingMode,profileFileName:e.profile.uuid?`${e.profile.uuid}.mobileprovision`:`${e.signingMode??"development"}.mobileprovision`})}function S(e,{bundleID:t,deviceUDID:i,teamID:o,signingMode:n="development"}){return(e.signingMode??"development")!==n||!a.profileMatchesBundleID(e.profile,t)||o&&e.teamID&&e.teamID!==o||i&&!a.profileContainsDevice(e.profile,i)||e.profile.expirationDate&&new Date(e.profile.expirationDate).getTime()<=Date.now()?!1:a.normalizeBundleID(e.bundleID)===a.normalizeBundleID(t)}function s(e,t,i={}){const o={pageNumber:1,pageSize:200,...i};return t&&(o.teamId=t),{method:"POST",path:e,payload:o}}exports.createAdHocProfileRequest=h;exports.createBundleIDRequest=g;exports.createDevelopmentProfileRequest=I;exports.downloadCertificateRequest=P;exports.downloadDistributionCertificateRequest=D;exports.downloadProfileRequest=C;exports.findAdHocProfilesRequest=m;exports.findBundleIDRequest=u;exports.findDevelopmentCertificatesRequest=f;exports.findDevelopmentProfilesRequest=p;exports.findDeviceRequest=d;exports.findDistributionCertificatesRequest=l;exports.getReusableAppleSigningAssets=b;exports.listTeamsRequest=c;exports.putAppleGeneratedSigningAssets=y;exports.registerDeviceRequest=R;exports.submitDevelopmentCSRRequest=q;exports.submitDistributionCSRRequest=v;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limrun/ui",
3
- "version": "0.13.3-rc.1",
3
+ "version": "0.13.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,11 +1,7 @@
1
1
  // @vitest-environment node
2
2
 
3
3
  import { afterEach, describe, expect, test, vi } from 'vitest';
4
- import {
5
- assertAppleDeveloperPortalResponseOK,
6
- deleteAppleProfile,
7
- listAppleTeams,
8
- } from './index';
4
+ import { assertAppleDeveloperPortalResponseOK, deleteAppleProfile, listAppleTeams } from './index';
9
5
 
10
6
  describe('App Store relay primitives', () => {
11
7
  afterEach(() => {
@@ -170,7 +170,12 @@ export async function listAppleTeams(options: AppleRelayClientOptions) {
170
170
  ...options,
171
171
  request: listTeamsRequest(),
172
172
  label: 'Apple Developer team list',
173
- mapBody: (body) => uniqueAppleTeams([...(body?.teams ?? []), ...(body?.availableProviders ?? []), ...(body?.provider ? [body.provider] : [])]),
173
+ mapBody: (body) =>
174
+ uniqueAppleTeams([
175
+ ...(body?.teams ?? []),
176
+ ...(body?.availableProviders ?? []),
177
+ ...(body?.provider ? [body.provider] : []),
178
+ ]),
174
179
  });
175
180
  }
176
181
 
@@ -182,9 +187,9 @@ export async function listAppleCertificates({
182
187
  return requestAppleProvisioning<Array<Record<string, unknown>>>({
183
188
  ...options,
184
189
  request:
185
- certificateKind === 'distribution'
186
- ? findDistributionCertificatesRequest(teamId)
187
- : findDevelopmentCertificatesRequest(teamId),
190
+ certificateKind === 'distribution' ?
191
+ findDistributionCertificatesRequest(teamId)
192
+ : findDevelopmentCertificatesRequest(teamId),
188
193
  label: 'Apple Developer certificate list',
189
194
  mapBody: (body) => body?.certRequests ?? [],
190
195
  });
@@ -199,10 +204,13 @@ export async function createAppleCertificate({
199
204
  return requestAppleProvisioning<Record<string, unknown> | undefined>({
200
205
  ...options,
201
206
  request:
202
- certificateKind === 'distribution'
203
- ? submitDistributionCSRRequest({ csrPEM, teamID: teamId })
204
- : submitDevelopmentCSRRequest({ csrPEM, teamID: teamId }),
205
- label: certificateKind === 'distribution' ? 'Apple Distribution certificate creation' : 'Apple Development certificate creation',
207
+ certificateKind === 'distribution' ?
208
+ submitDistributionCSRRequest({ csrPEM, teamID: teamId })
209
+ : submitDevelopmentCSRRequest({ csrPEM, teamID: teamId }),
210
+ label:
211
+ certificateKind === 'distribution' ?
212
+ 'Apple Distribution certificate creation'
213
+ : 'Apple Development certificate creation',
206
214
  mapBody: (body) => body?.certRequest,
207
215
  });
208
216
  }
@@ -216,9 +224,9 @@ export async function downloadAppleCertificate({
216
224
  return requestAppleProvisioning<AppleRelayResponse>({
217
225
  ...options,
218
226
  request:
219
- certificateKind === 'distribution'
220
- ? downloadDistributionCertificateRequest(certificateId, teamId)
221
- : downloadCertificateRequest(certificateId, teamId),
227
+ certificateKind === 'distribution' ?
228
+ downloadDistributionCertificateRequest(certificateId, teamId)
229
+ : downloadCertificateRequest(certificateId, teamId),
222
230
  label: 'Apple certificate download',
223
231
  validatePortalResponse: false,
224
232
  mapBody: (_body, response) => response,
@@ -247,16 +255,36 @@ export async function deleteAppleCertificate({
247
255
  });
248
256
  }
249
257
 
250
- export async function listAppleBundleIDs({ teamId = '', search = '', ...options }: ListAppleBundleIDsOptions) {
258
+ export async function listAppleBundleIDs({
259
+ teamId = '',
260
+ search = '',
261
+ ...options
262
+ }: ListAppleBundleIDsOptions) {
263
+ const query = search.trim().toLowerCase();
251
264
  return requestAppleProvisioning<AppleDeveloperPortalAppID[]>({
252
265
  ...options,
253
266
  request: findBundleIDRequest({ bundleID: search, teamID: teamId }),
254
267
  label: 'Apple bundle ID list',
255
- mapBody: (body) => body?.appIds ?? [],
268
+ // Apple's listAppIds.action returns the full paginated list and does not
269
+ // honor a server-side search filter, so apply the `search` filter here.
270
+ mapBody: (body) => {
271
+ const appIds = body?.appIds ?? [];
272
+ if (!query) return appIds;
273
+ return appIds.filter((appId) =>
274
+ [appId.identifier, appId.bundleId, appId.name].some(
275
+ (value) => typeof value === 'string' && value.toLowerCase().includes(query),
276
+ ),
277
+ );
278
+ },
256
279
  });
257
280
  }
258
281
 
259
- export async function createAppleBundleID({ teamId = '', bundleId, name, ...options }: CreateAppleBundleIDOptions) {
282
+ export async function createAppleBundleID({
283
+ teamId = '',
284
+ bundleId,
285
+ name,
286
+ ...options
287
+ }: CreateAppleBundleIDOptions) {
260
288
  return requestAppleProvisioning<Record<string, unknown> | undefined>({
261
289
  ...options,
262
290
  request: createBundleIDRequest({ bundleID: bundleId, teamID: teamId, name }),
@@ -302,16 +330,39 @@ export async function deleteAppleBundleID({ teamId = '', appIdId, ...options }:
302
330
  });
303
331
  }
304
332
 
305
- export async function listAppleDevices({ teamId = '', deviceUDID = '', ...options }: ListAppleDevicesOptions) {
333
+ export async function listAppleDevices({
334
+ teamId = '',
335
+ deviceUDID = '',
336
+ ...options
337
+ }: ListAppleDevicesOptions) {
338
+ const wantedUDID = normalizeAppleUDID(deviceUDID);
306
339
  return requestAppleProvisioning<AppleDeveloperPortalDevice[]>({
307
340
  ...options,
308
341
  request: findDeviceRequest({ deviceUDID, teamID: teamId }),
309
342
  label: 'Apple device list',
310
- mapBody: (body) => body?.devices ?? [],
343
+ // Apple's listDevices.action returns the full paginated list and does not
344
+ // honor a server-side UDID filter, so apply the `deviceUDID` filter here.
345
+ mapBody: (body) => {
346
+ const devices = body?.devices ?? [];
347
+ if (!wantedUDID) return devices;
348
+ return devices.filter((device) => normalizeAppleUDID(device.deviceNumber) === wantedUDID);
349
+ },
311
350
  });
312
351
  }
313
352
 
314
- export async function registerAppleDevice({ teamId = '', deviceUDID, name, ...options }: RegisterAppleDeviceOptions) {
353
+ function normalizeAppleUDID(udid?: string) {
354
+ return (udid ?? '')
355
+ .replace(/-/g, '')
356
+ .replace(/[^a-fA-F0-9]/g, '')
357
+ .toUpperCase();
358
+ }
359
+
360
+ export async function registerAppleDevice({
361
+ teamId = '',
362
+ deviceUDID,
363
+ name,
364
+ ...options
365
+ }: RegisterAppleDeviceOptions) {
315
366
  return requestAppleProvisioning<Record<string, unknown> | undefined>({
316
367
  ...options,
317
368
  request: registerDeviceRequest({ deviceUDID, teamID: teamId, name }),
@@ -320,7 +371,12 @@ export async function registerAppleDevice({ teamId = '', deviceUDID, name, ...op
320
371
  });
321
372
  }
322
373
 
323
- export async function updateAppleDevice({ teamId = '', deviceId, name, ...options }: UpdateAppleDeviceOptions) {
374
+ export async function updateAppleDevice({
375
+ teamId = '',
376
+ deviceId,
377
+ name,
378
+ ...options
379
+ }: UpdateAppleDeviceOptions) {
324
380
  return requestAppleProvisioning<Record<string, unknown> | undefined>({
325
381
  ...options,
326
382
  request: {
@@ -355,9 +411,9 @@ export async function listAppleProfiles({
355
411
  return requestAppleProvisioning<Array<Record<string, unknown>>>({
356
412
  ...options,
357
413
  request:
358
- profileKind === 'adhoc'
359
- ? findAdHocProfilesRequest({ bundleID: bundleId, teamID: teamId })
360
- : findDevelopmentProfilesRequest({ bundleID: bundleId, teamID: teamId }),
414
+ profileKind === 'adhoc' ?
415
+ findAdHocProfilesRequest({ bundleID: bundleId, teamID: teamId })
416
+ : findDevelopmentProfilesRequest({ bundleID: bundleId, teamID: teamId }),
361
417
  label: 'Apple provisioning profile list',
362
418
  mapBody: (body) => body?.provisioningProfiles ?? [],
363
419
  });
@@ -380,29 +436,33 @@ export async function createAppleProfile({
380
436
  return requestAppleProvisioning<Record<string, unknown> | undefined>({
381
437
  ...options,
382
438
  request:
383
- profileKind === 'adhoc'
384
- ? createAdHocProfileRequest({
385
- bundleID: bundleId,
386
- teamID: teamId,
387
- appIDID: appIdId,
388
- certificateID: certificateId,
389
- deviceIDs: deviceIds,
390
- name,
391
- })
392
- : createDevelopmentProfileRequest({
393
- bundleID: bundleId,
394
- teamID: teamId,
395
- appIDID: appIdId,
396
- certificateID: certificateId,
397
- deviceIDs: deviceIds,
398
- name,
399
- }),
439
+ profileKind === 'adhoc' ?
440
+ createAdHocProfileRequest({
441
+ bundleID: bundleId,
442
+ teamID: teamId,
443
+ appIDID: appIdId,
444
+ certificateID: certificateId,
445
+ deviceIDs: deviceIds,
446
+ name,
447
+ })
448
+ : createDevelopmentProfileRequest({
449
+ bundleID: bundleId,
450
+ teamID: teamId,
451
+ appIDID: appIdId,
452
+ certificateID: certificateId,
453
+ deviceIDs: deviceIds,
454
+ name,
455
+ }),
400
456
  label: 'Apple provisioning profile creation',
401
457
  mapBody: (body) => body?.provisioningProfile,
402
458
  });
403
459
  }
404
460
 
405
- export async function downloadAppleProfile({ teamId = '', profileId, ...options }: DownloadAppleProfileOptions) {
461
+ export async function downloadAppleProfile({
462
+ teamId = '',
463
+ profileId,
464
+ ...options
465
+ }: DownloadAppleProfileOptions) {
406
466
  return requestAppleProvisioning<AppleRelayResponse>({
407
467
  ...options,
408
468
  request: downloadProfileRequest(profileId, teamId),
@@ -425,12 +485,17 @@ export async function deleteAppleProfile({ teamId = '', profileId, ...options }:
425
485
  });
426
486
  }
427
487
 
428
- export function assertAppleDeveloperPortalResponseOK(response: AppleDeveloperPortalResponse | undefined, label: string) {
488
+ export function assertAppleDeveloperPortalResponseOK(
489
+ response: AppleDeveloperPortalResponse | undefined,
490
+ label: string,
491
+ ) {
429
492
  if (!response) {
430
493
  throw new Error(`${label} returned an empty response.`);
431
494
  }
432
495
  if (response.resultCode !== undefined && response.resultCode !== 0) {
433
- throw new Error(`${label} failed: ${response.userString ?? response.resultString ?? response.resultCode}`);
496
+ throw new Error(
497
+ `${label} failed: ${response.userString ?? response.resultString ?? response.resultCode}`,
498
+ );
434
499
  }
435
500
  }
436
501
 
@@ -438,7 +503,9 @@ function uniqueAppleTeams(teams: AppleDeveloperPortalTeam[]) {
438
503
  const seen = new Set<string>();
439
504
  const result: AppleDeveloperPortalTeam[] = [];
440
505
  for (const team of teams) {
441
- const key = String(team.teamId ?? team.providerId ?? team.publicProviderId ?? team.name ?? JSON.stringify(team));
506
+ const key = String(
507
+ team.teamId ?? team.providerId ?? team.publicProviderId ?? team.name ?? JSON.stringify(team),
508
+ );
442
509
  if (seen.has(key)) continue;
443
510
  seen.add(key);
444
511
  result.push(team);
@@ -21,7 +21,9 @@ export type UseAppleIDLoginResult = {
21
21
  completeResponse?: AppleRelayResponse;
22
22
  twoFactorChallengeResponse?: AppleRelayResponse;
23
23
  error?: string;
24
- startLogin: (input: Pick<AppleIDLoginInput, 'accountName' | 'password'>) => Promise<AppleIDLoginResult | undefined>;
24
+ startLogin: (
25
+ input: Pick<AppleIDLoginInput, 'accountName' | 'password'>,
26
+ ) => Promise<AppleIDLoginResult | undefined>;
25
27
  submitTwoFactorCode: (code: string) => Promise<AppleRelayResponse | undefined>;
26
28
  finalize: () => Promise<AppleRelayResponse | undefined>;
27
29
  close: () => Promise<void>;
@@ -31,7 +33,9 @@ export function useAppleIDLogin({ limbuildApiUrl, token }: UseAppleIDLoginOption
31
33
  const [status, setStatus] = useState<AppleIDLoginStatus>('idle');
32
34
  const [session, setSession] = useState<AppleIDLoginResult | undefined>();
33
35
  const [completeResponse, setCompleteResponse] = useState<AppleRelayResponse | undefined>();
34
- const [twoFactorChallengeResponse, setTwoFactorChallengeResponse] = useState<AppleRelayResponse | undefined>();
36
+ const [twoFactorChallengeResponse, setTwoFactorChallengeResponse] = useState<
37
+ AppleRelayResponse | undefined
38
+ >();
35
39
  const [error, setError] = useState<string | undefined>();
36
40
  const sessionRef = useRef<AppleIDLoginResult | undefined>(undefined);
37
41
 
@@ -29,9 +29,7 @@ export type AppleIDLoginResult = {
29
29
  close: () => Promise<void>;
30
30
  };
31
31
 
32
- type TwoFactorMethod =
33
- | { type: 'trustedDevice' }
34
- | { type: 'phone'; phoneNumberId: number; mode: string };
32
+ type TwoFactorMethod = { type: 'trustedDevice' } | { type: 'phone'; phoneNumberId: number; mode: string };
35
33
 
36
34
  export async function startBrowserOwnedAppleIDLogin({
37
35
  limbuildApiUrl,
@@ -44,7 +42,9 @@ export async function startBrowserOwnedAppleIDLogin({
44
42
  const srp = new AppleGsaSrpClient(accountName);
45
43
  const initResponse = await proxySrpInit(limbuildApiUrl, appleSessionId, await srp.init(), token);
46
44
  if (initResponse.status < 200 || initResponse.status >= 300) {
47
- throw new Error(`Apple SRP init failed: HTTP ${initResponse.status} ${initResponse.rawBody ?? ''}`.trim());
45
+ throw new Error(
46
+ `Apple SRP init failed: HTTP ${initResponse.status} ${initResponse.rawBody ?? ''}`.trim(),
47
+ );
48
48
  }
49
49
  if (!initResponse.body) {
50
50
  throw new Error('Apple SRP init response did not include a body.');
@@ -66,17 +66,20 @@ export async function startBrowserOwnedAppleIDLogin({
66
66
  if (requiresTwoFactor) {
67
67
  twoFactorChallengeResponse = await triggerTrustedDeviceTwoFactor(limbuildApiUrl, appleSessionId, token);
68
68
  const phone = trustedPhoneNumberFromChallenge(twoFactorChallengeResponse.body);
69
- if (phone) {
69
+ // Only route codes through the phone/SMS endpoint when Apple actually
70
+ // falls back to phone verification (HTTP 412). The trusted-device
71
+ // challenge body frequently includes trusted-phone metadata even for a
72
+ // normal device push; routing those codes to the phone endpoint breaks
73
+ // trusted-device login.
74
+ if (twoFactorChallengeResponse.status === 412) {
75
+ if (!phone) {
76
+ throw new Error('Apple requested phone verification but did not include a trusted phone number.');
77
+ }
70
78
  twoFactorMethod = {
71
79
  type: 'phone',
72
80
  phoneNumberId: phone.id,
73
81
  mode: phone.pushMode ?? 'sms',
74
82
  };
75
- }
76
- if (twoFactorChallengeResponse.status === 412) {
77
- if (!phone) {
78
- throw new Error('Apple requested phone verification but did not include a trusted phone number.');
79
- }
80
83
  twoFactorChallengeResponse = await triggerPhoneTwoFactor(
81
84
  limbuildApiUrl,
82
85
  appleSessionId,
@@ -93,7 +96,9 @@ export async function startBrowserOwnedAppleIDLogin({
93
96
  );
94
97
  }
95
98
  } else if (completeResponse.status < 200 || completeResponse.status >= 300) {
96
- throw new Error(`Apple SRP complete failed: HTTP ${completeResponse.status} ${completeResponse.rawBody ?? ''}`.trim());
99
+ throw new Error(
100
+ `Apple SRP complete failed: HTTP ${completeResponse.status} ${completeResponse.rawBody ?? ''}`.trim(),
101
+ );
97
102
  }
98
103
  return {
99
104
  appleSessionId,
@@ -102,18 +107,20 @@ export async function startBrowserOwnedAppleIDLogin({
102
107
  requiresTwoFactor,
103
108
  finishTwoFactor: async (code) => {
104
109
  const response =
105
- twoFactorMethod.type === 'phone'
106
- ? await proxyPhoneTwoFactorCode(
107
- limbuildApiUrl,
108
- appleSessionId,
109
- twoFactorMethod.phoneNumberId,
110
- code,
111
- twoFactorMethod.mode,
112
- token,
113
- )
114
- : await proxyTwoFactorCode(limbuildApiUrl, appleSessionId, code, token);
110
+ twoFactorMethod.type === 'phone' ?
111
+ await proxyPhoneTwoFactorCode(
112
+ limbuildApiUrl,
113
+ appleSessionId,
114
+ twoFactorMethod.phoneNumberId,
115
+ code,
116
+ twoFactorMethod.mode,
117
+ token,
118
+ )
119
+ : await proxyTwoFactorCode(limbuildApiUrl, appleSessionId, code, token);
115
120
  if (response.status < 200 || response.status >= 300) {
116
- throw new Error(`Apple two-factor code failed: HTTP ${response.status} ${response.rawBody ?? ''}`.trim());
121
+ throw new Error(
122
+ `Apple two-factor code failed: HTTP ${response.status} ${response.rawBody ?? ''}`.trim(),
123
+ );
117
124
  }
118
125
  return response;
119
126
  },