@sitepong/fastnode 0.1.1 → 0.2.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 (76) hide show
  1. package/README.md +148 -0
  2. package/dist/actions/index.cjs +964 -137
  3. package/dist/actions/index.cjs.map +1 -1
  4. package/dist/actions/index.mjs +964 -137
  5. package/dist/actions/index.mjs.map +1 -1
  6. package/dist/cert/index.cjs +40 -0
  7. package/dist/cert/index.cjs.map +1 -1
  8. package/dist/cert/index.d.cts +3 -3
  9. package/dist/cert/index.d.ts +3 -3
  10. package/dist/cert/index.mjs +37 -1
  11. package/dist/cert/index.mjs.map +1 -1
  12. package/dist/{cert-BmJGtYtb.d.cts → cert-6m1R8UeW.d.cts} +1 -1
  13. package/dist/{cert-owkOFNW8.d.ts → cert-DtYDJBjG.d.ts} +1 -1
  14. package/dist/cli/index.cjs +964 -137
  15. package/dist/cli/index.cjs.map +1 -1
  16. package/dist/cli/index.d.cts +1 -1
  17. package/dist/cli/index.d.ts +1 -1
  18. package/dist/cli/index.mjs +964 -137
  19. package/dist/cli/index.mjs.map +1 -1
  20. package/dist/client/index.cjs +730 -109
  21. package/dist/client/index.cjs.map +1 -1
  22. package/dist/client/index.d.cts +1 -1
  23. package/dist/client/index.d.ts +1 -1
  24. package/dist/client/index.mjs +726 -110
  25. package/dist/client/index.mjs.map +1 -1
  26. package/dist/deliver/index.d.cts +2 -2
  27. package/dist/deliver/index.d.ts +2 -2
  28. package/dist/index-AD8QXY1T.d.cts +158 -0
  29. package/dist/{index-DfCUVJX1.d.cts → index-BW8wQUGC.d.cts} +1 -1
  30. package/dist/{index-EcVvViW8.d.cts → index-BWK-T2-S.d.cts} +1 -1
  31. package/dist/{index-B0yhu_LZ.d.ts → index-BpiEBPVd.d.ts} +1 -1
  32. package/dist/index-CCs5irG7.d.cts +61 -0
  33. package/dist/{index-B9yXicrQ.d.ts → index-CLsvyTkh.d.ts} +1 -1
  34. package/dist/{index-BLubSlgK.d.cts → index-CMH_GpN9.d.cts} +358 -2
  35. package/dist/index-CQKpbHZt.d.ts +61 -0
  36. package/dist/{index-BQJIJlK7.d.cts → index-CQd4bo3G.d.cts} +3 -3
  37. package/dist/{index-CWNLnSOL.d.cts → index-CdYdwxpQ.d.cts} +1 -1
  38. package/dist/{index-BFdNOmPJ.d.cts → index-Ce-WMV7c.d.cts} +1 -1
  39. package/dist/{index-TfXqwuac.d.ts → index-CojSGJ7m.d.ts} +1 -1
  40. package/dist/{index-1Vu2cRQ2.d.ts → index-CsFIp7OO.d.ts} +1 -1
  41. package/dist/{index-D1Izm7qt.d.ts → index-D2z7RiAM.d.ts} +358 -2
  42. package/dist/index-D82CmjMP.d.ts +158 -0
  43. package/dist/{index-BN0ubxjZ.d.ts → index-DZaPkUnC.d.ts} +3 -3
  44. package/dist/{index-D5eihzwM.d.cts → index-KCB2q1-8.d.cts} +1 -1
  45. package/dist/{index-A5MimeQk.d.ts → index-Kq3rbG0u.d.ts} +1 -1
  46. package/dist/index.cjs +1322 -138
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.cts +199 -10
  49. package/dist/index.d.ts +199 -10
  50. package/dist/index.mjs +1319 -139
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/lane/index.cjs +964 -137
  53. package/dist/lane/index.cjs.map +1 -1
  54. package/dist/lane/index.mjs +964 -137
  55. package/dist/lane/index.mjs.map +1 -1
  56. package/dist/match/index.d.cts +4 -4
  57. package/dist/match/index.d.ts +4 -4
  58. package/dist/pem/index.d.cts +2 -2
  59. package/dist/pem/index.d.ts +2 -2
  60. package/dist/pilot/index.cjs +190 -12
  61. package/dist/pilot/index.cjs.map +1 -1
  62. package/dist/pilot/index.d.cts +2 -2
  63. package/dist/pilot/index.d.ts +2 -2
  64. package/dist/pilot/index.mjs +186 -14
  65. package/dist/pilot/index.mjs.map +1 -1
  66. package/dist/precheck/index.d.cts +2 -2
  67. package/dist/precheck/index.d.ts +2 -2
  68. package/dist/produce/index.d.cts +2 -2
  69. package/dist/produce/index.d.ts +2 -2
  70. package/dist/sigh/index.d.cts +2 -2
  71. package/dist/sigh/index.d.ts +2 -2
  72. package/package.json +3 -1
  73. package/dist/index-DfirmFbC.d.ts +0 -32
  74. package/dist/index-DkvinY7_.d.ts +0 -54
  75. package/dist/index-HRiaaX-Q.d.cts +0 -54
  76. package/dist/index-lt819Jgj.d.cts +0 -32
package/README.md CHANGED
@@ -62,6 +62,154 @@ Each module can be imported separately:
62
62
  | `@sitepong/fastnode/precheck` | Static-analyze App Store metadata against review rules |
63
63
  | `@sitepong/fastnode/frameit` | Composite screenshots into device frames |
64
64
 
65
+ ## Apple sign-in & one-click setup
66
+
67
+ `fastnode` ships a complete "sign in with your Apple Developer Apple ID
68
+ and provision everything" flow built on top of the same SIRP+2FA login
69
+ fastlane's `spaceship` uses. Given an email + password and a 2FA code,
70
+ it produces a fully-populated credential set without ever touching the
71
+ Apple Developer Portal UI.
72
+
73
+ ### Resumable login (HTTP-friendly 2FA)
74
+
75
+ ```ts
76
+ import {
77
+ beginLogin,
78
+ submitTwoFactorCode,
79
+ requestSmsCode,
80
+ } from "@sitepong/fastnode/client";
81
+
82
+ // Step 1: SIRP. Returns either a logged-in session or a pending state
83
+ // describing the 2FA challenge.
84
+ const result = await beginLogin({ email, password });
85
+
86
+ if (result.kind === "logged_in") {
87
+ // No 2FA on this account — go straight to provisioning
88
+ console.log("Logged in:", result.session.userEmail);
89
+ } else {
90
+ // Prompt the user for the 6-digit code Apple pushed to their device
91
+ // (or call requestSmsCode() to fall back to SMS)
92
+ const session = await submitTwoFactorCode({
93
+ pending: result.pending,
94
+ code: "123456",
95
+ source: "device", // or "phone" if the user opted into SMS
96
+ });
97
+ }
98
+ ```
99
+
100
+ `beginLogin` and `submitTwoFactorCode` are split so the 2FA prompt can
101
+ be served by an HTTP request loop (e.g. a wizard in a web app), not
102
+ just an interactive CLI. The legacy `loginWithAppleId(...)` is still
103
+ available for one-shot CLI use.
104
+
105
+ ### One-click orchestration
106
+
107
+ ```ts
108
+ import {
109
+ oneClickListTeams,
110
+ oneClickInventory,
111
+ oneClickSetup,
112
+ } from "@sitepong/fastnode";
113
+
114
+ // 1. List developer-portal teams (the alphanumeric teamIds, e.g. "W44274RVJ2")
115
+ const teams = await oneClickListTeams(session);
116
+
117
+ // 2. Inspect the team's current state — bundle ids, dist cert capacity,
118
+ // existing push certs, profiles. Used to pre-populate UI / detect the
119
+ // 3-distribution-cert limit before trying to provision.
120
+ const inventory = await oneClickInventory(session, teams[0].teamId);
121
+
122
+ // 3. Provision everything in one call
123
+ const result = await oneClickSetup({
124
+ session,
125
+ teamId: teams[0].teamId,
126
+ bundleIdentifier: "com.example.myapp",
127
+ appName: "My App",
128
+ options: {
129
+ generateApnsAuthKey: true, // .p8 APNs auth key (modern push)
130
+ generateAscApiKey: true, // .p8 ASC API key (TestFlight uploads)
131
+ enableSignInWithApple: true, // bundle capability via REST API
132
+ enablePushNotifications: true,
133
+ revokeCertIds: [], // dist cert ids to revoke first
134
+ },
135
+ onProgress: (event) => {
136
+ // structured step events for live UI feedback
137
+ if (event.type === "step_done") console.log("✓", event.label);
138
+ },
139
+ });
140
+
141
+ console.log({
142
+ bundleId: result.bundle_identifier,
143
+ ascKeyId: result.asc_key_id, // e.g. "MRA2DX6Y6W"
144
+ ascIssuerId: result.asc_issuer_id, // e.g. "cfadaf94-44ca-4c2c-..."
145
+ ascP8: result.asc_api_key_base64, // base64 .p8 PEM
146
+ distP12: result.dist_cert_base64, // base64 PKCS#12
147
+ certPwd: result.cert_password,
148
+ profile: result.provisioning_profile_base64,
149
+ apnsKey: result.additional_certs.find(c => c.cert_type === "apns_key"),
150
+ });
151
+ ```
152
+
153
+ Under the hood `oneClickSetup` runs:
154
+
155
+ 1. Initialize the developer-portal CSRF context
156
+ 2. (Optional) revoke the user's existing distribution cert(s) — Apple
157
+ caps a team at 3 active distribution certs and 1 per user
158
+ 3. Create or reuse the bundle id
159
+ 4. Generate an RSA CSR + create a fresh distribution certificate, bundle
160
+ it with the private key into a PKCS#12 (.p12) using `node-forge`
161
+ 5. Create an App Store provisioning profile, picking a non-colliding
162
+ date-stamped name (e.g. `My App App Store 2026-04-14`, `… (1)`, `… (2)`
163
+ if the same day collides)
164
+ 6. (Optional) create an APNs Authentication Key (.p8) via the developer
165
+ portal's `account/auth/key/v2/create` endpoint and download the PEM
166
+ immediately via `account/auth/key/download`
167
+ 7. (Optional) create an App Store Connect API key (.p8) via the iris
168
+ admin API at `iris/v1/apiKeys`, then download the .p8 + issuer id via
169
+ the JSON-API sparse fieldset trick (`?fields[apiKeys]=privateKey&include=provider`)
170
+ reverse-engineered from `@expo/apple-utils`
171
+ 8. (Optional) flip Push Notifications and Sign in with Apple capabilities
172
+ on the bundle id via the JWT-authenticated REST API (the legacy portal
173
+ capability endpoint Apple removed)
174
+
175
+ Failures at any optional step are converted into structured warnings
176
+ rather than halting the orchestration, so the credential is always
177
+ saved with whatever could be obtained.
178
+
179
+ ### `PortalClient` and `IrisClient`
180
+
181
+ Two lower-level clients are exported alongside the high-level orchestrator
182
+ for direct use:
183
+
184
+ ```ts
185
+ import { PortalClient, IrisClient } from "@sitepong/fastnode/client";
186
+
187
+ // Apple Developer Portal — session-cookie auth, used by the legacy
188
+ // account/* endpoints (bundle ids, certs, profiles, devices, key
189
+ // management for service-tied auth keys like APNs / DeviceCheck).
190
+ const portal = new PortalClient(session, "W44274RVJ2");
191
+ await portal.init();
192
+ const apnsKey = await portal.createApnsAuthKey("My App APNs");
193
+ console.log(apnsKey.pem); // -----BEGIN PRIVATE KEY-----...
194
+
195
+ // App Store Connect iris admin API — same session cookies, used for
196
+ // the modern PUBLIC_API (App Store Connect) keys that show up under
197
+ // "Users and Access > Integrations > Keys".
198
+ const iris = new IrisClient(session);
199
+ const ascKey = await iris.createApiKey({
200
+ nickname: "My App SitePong",
201
+ roles: ["APP_MANAGER"],
202
+ allAppsVisible: true,
203
+ keyType: "PUBLIC_API",
204
+ });
205
+ const downloaded = await iris.fetchApiKeyPrivateKey(ascKey.id);
206
+ console.log(downloaded?.pem, downloaded?.issuerId);
207
+ ```
208
+
209
+ Both clients deliberately stay close to the wire format — they do not
210
+ hide the JSON-API envelope or Apple's per-section CSRF quirks. The
211
+ high-level `oneClickSetup` is where the ergonomics live.
212
+
65
213
  ## Environment
66
214
 
67
215
  The library pulls App Store Connect credentials from: