@limrun/ui 0.14.0-rc.2 → 0.14.0-rc.4
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.
- package/dist/app-store-relay/index.cjs +3 -3
- package/dist/app-store-relay/index.js +5 -5
- package/dist/app-store-relay/react.cjs +1 -1
- package/dist/app-store-relay/react.d.ts +1 -1
- package/dist/app-store-relay/react.js +34 -31
- package/dist/apple-credentials/index.cjs +1 -0
- package/dist/apple-credentials/index.d.ts +59 -0
- package/dist/apple-credentials/index.js +227 -0
- package/dist/browser-storage-BnO-Ma-e.mjs +219 -0
- package/dist/browser-storage-DLxIqZk5.js +1 -0
- package/dist/client-D1drDXxE.js +1 -0
- package/dist/{client-CBX-hLnL.mjs → client-DSuNtWDv.mjs} +2 -7
- package/dist/core/device-install/operations/operations.d.ts +1 -1
- package/dist/core/device-install/storage/browser-storage.d.ts +8 -0
- package/dist/core/device-install/storage/index.d.ts +1 -0
- package/dist/core/device-install/storage/secret-store.d.ts +101 -0
- package/dist/core/device-install/types.d.ts +6 -0
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/play-publish/google.d.ts +13 -0
- package/dist/core/play-publish/publish.d.ts +34 -0
- package/dist/device-build/index.cjs +1 -1
- package/dist/device-build/index.js +25 -24
- package/dist/device-build/react.cjs +1 -1
- package/dist/device-build/react.js +42 -43
- package/dist/device-install/index.cjs +1 -1
- package/dist/device-install/index.js +164 -160
- package/dist/device-install/react.cjs +1 -1
- package/dist/device-install/react.js +61 -60
- package/dist/errors-BSkqS7U-.js +1 -0
- package/dist/errors-gy8yQJos.mjs +6 -0
- package/dist/gsa-srp-Bme3JLEi.mjs +266 -0
- package/dist/gsa-srp-CCIfUz7Y.js +1 -0
- package/dist/index-ChhtuQ7e.js +22 -0
- package/dist/index-YDu8K9a4.mjs +11279 -0
- package/dist/play-publish/index.cjs +1 -0
- package/dist/play-publish/index.d.ts +2 -0
- package/dist/play-publish/index.js +109 -0
- package/dist/play-publish/react.cjs +1 -0
- package/dist/play-publish/react.d.ts +31 -0
- package/dist/play-publish/react.js +91 -0
- package/dist/{provisioning-DKbkBO-p.mjs → provisioning-B_DefBEp.mjs} +9 -9
- package/dist/{provisioning-Va0PH4Kk.js → provisioning-BlQMccX_.js} +1 -1
- package/package.json +18 -2
- package/src/app-store-relay/index.test.ts +6 -6
- package/src/app-store-relay/react.ts +6 -5
- package/src/apple-credentials/index.test.ts +158 -0
- package/src/apple-credentials/index.ts +241 -0
- package/src/core/device-install/apple/client.ts +1 -6
- package/src/core/device-install/apple/relay.ts +1 -4
- package/src/core/device-install/operations/operations.ts +8 -5
- package/src/core/device-install/storage/browser-storage.test.ts +65 -0
- package/src/core/device-install/storage/browser-storage.ts +33 -0
- package/src/core/device-install/storage/index.ts +1 -0
- package/src/core/device-install/storage/secret-store.ts +298 -0
- package/src/core/device-install/types.ts +6 -0
- package/src/core/errors.ts +3 -0
- package/src/core/play-publish/google.ts +113 -0
- package/src/core/play-publish/publish.test.ts +68 -0
- package/src/core/play-publish/publish.ts +89 -0
- package/src/device-build/react.ts +1 -4
- package/src/device-install/demo/demo.tsx +2 -8
- package/src/device-install/react.ts +31 -31
- package/src/play-publish/README.md +66 -0
- package/src/play-publish/index.ts +12 -0
- package/src/play-publish/react.ts +208 -0
- package/vite.config.ts +3 -0
- package/dist/browser-storage-Cx9eW-bm.mjs +0 -205
- package/dist/browser-storage-jJaIht4K.js +0 -1
- package/dist/client-BtC4n_qf.js +0 -1
- package/dist/index-CMeQfhYy.js +0 -22
- package/dist/index-DFdCvi2_.mjs +0 -11543
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type RelayClient,
|
|
13
13
|
type StoredPairRecord,
|
|
14
14
|
} from './index';
|
|
15
|
+
import { errorMessage } from '../core/errors';
|
|
15
16
|
|
|
16
17
|
export type DeviceInstallRelayBusyAction = 'usb' | 'pair' | 'install';
|
|
17
18
|
|
|
@@ -136,33 +137,36 @@ export function useDeviceInstallRelay({
|
|
|
136
137
|
}
|
|
137
138
|
}, [apiUrl, cleanupDeviceAccess, device, organizationId, token]);
|
|
138
139
|
|
|
139
|
-
const startInstallation = useCallback(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
140
|
+
const startInstallation = useCallback(
|
|
141
|
+
async (installSource: InstallSource) => {
|
|
142
|
+
if (!apiUrl || !device || !pairRecord) {
|
|
143
|
+
throw new Error('Select and pair a USB device before starting installation.');
|
|
144
|
+
}
|
|
145
|
+
setBusyAction('install');
|
|
146
|
+
setError(undefined);
|
|
147
|
+
try {
|
|
148
|
+
await cleanupDeviceAccess();
|
|
149
|
+
relayRef.current = await startDeviceInstall({
|
|
150
|
+
registryApiUrl: apiUrl,
|
|
151
|
+
token,
|
|
152
|
+
organizationId,
|
|
153
|
+
log: logRef.current,
|
|
154
|
+
target: device,
|
|
155
|
+
pairRecord,
|
|
156
|
+
installSource,
|
|
157
|
+
});
|
|
158
|
+
logRef.current('Device install started', 'Installation will continue through the connected iPhone.');
|
|
159
|
+
return relayRef.current;
|
|
160
|
+
} catch (caught) {
|
|
161
|
+
await closeDeviceRelayTarget(device, logRef.current);
|
|
162
|
+
setError(errorMessage(caught));
|
|
163
|
+
return undefined;
|
|
164
|
+
} finally {
|
|
165
|
+
setBusyAction(undefined);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
[apiUrl, cleanupDeviceAccess, device, organizationId, pairRecord, token],
|
|
169
|
+
);
|
|
166
170
|
|
|
167
171
|
const stopRelay = useCallback(() => {
|
|
168
172
|
void cleanupDeviceAccess();
|
|
@@ -189,7 +193,3 @@ export function useDeviceInstallRelay({
|
|
|
189
193
|
function noopLog() {
|
|
190
194
|
// Intentionally empty. Consumers can pass a logger for progress messages.
|
|
191
195
|
}
|
|
192
|
-
|
|
193
|
-
function errorMessage(error: unknown) {
|
|
194
|
-
return error instanceof Error ? error.message : String(error);
|
|
195
|
-
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @limrun/ui/play-publish
|
|
2
|
+
|
|
3
|
+
Headless building blocks for publishing an AAB asset from the Limrun
|
|
4
|
+
registry to Google Play, with a browser-owned Google sign-in. No UI ships
|
|
5
|
+
here; embedders render their own buttons and dialogs around the hook,
|
|
6
|
+
same as `device-install`.
|
|
7
|
+
|
|
8
|
+
The Google access token is minted in the browser via Google Identity
|
|
9
|
+
Services (token model, no client secret) and sent to the registry once
|
|
10
|
+
per publish. Limrun never stores it.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- A Google OAuth **Web application** client ID whose authorized
|
|
15
|
+
JavaScript origins include your app's origin.
|
|
16
|
+
- The signed-in Google account needs release permission for the target
|
|
17
|
+
app in Play Console, and the app listing must already exist.
|
|
18
|
+
- The asset must be an AAB signed with the app's Play upload key.
|
|
19
|
+
|
|
20
|
+
## React
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { usePlaystorePublish } from '@limrun/ui/play-publish/react';
|
|
24
|
+
|
|
25
|
+
const play = usePlaystorePublish({
|
|
26
|
+
registryApiUrl: 'https://registry.limrun.com',
|
|
27
|
+
token: limrunToken,
|
|
28
|
+
organizationId: organizationTid,
|
|
29
|
+
googleClientId: GOOGLE_OAUTH_CLIENT_ID,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// On dialog open, warm the sign-in script so the click stays popup-safe.
|
|
33
|
+
// Optionally await the returned promise (true = ready) to gate the button:
|
|
34
|
+
play.preloadGoogle();
|
|
35
|
+
|
|
36
|
+
// Button handlers:
|
|
37
|
+
await play.signInWithGoogle();
|
|
38
|
+
await play.publish({ assetName: 'app-release.aab', packageName: 'com.example.app' });
|
|
39
|
+
|
|
40
|
+
// Render from state: play.status, play.versionCode, play.error, play.errorCode
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`errorCode` carries the registry's machine-readable error code; the
|
|
44
|
+
canonical value list lives on `PlaystorePublishError`'s doc comment and
|
|
45
|
+
grows additively.
|
|
46
|
+
|
|
47
|
+
Google access tokens expire after about an hour. A `permissionDenied`
|
|
48
|
+
error long after sign-in usually means the token expired; offer "Sign in
|
|
49
|
+
with Google" again rather than pointing users at Play Console
|
|
50
|
+
permissions.
|
|
51
|
+
|
|
52
|
+
## Without React
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { requestGoogleAccessToken, publishToPlaystore } from '@limrun/ui/play-publish';
|
|
56
|
+
|
|
57
|
+
const accessToken = await requestGoogleAccessToken({ clientId: GOOGLE_OAUTH_CLIENT_ID });
|
|
58
|
+
const { versionCode } = await publishToPlaystore({
|
|
59
|
+
registryApiUrl,
|
|
60
|
+
token,
|
|
61
|
+
organizationId,
|
|
62
|
+
accessToken,
|
|
63
|
+
assetName: 'app-release.aab',
|
|
64
|
+
packageName: 'com.example.app',
|
|
65
|
+
});
|
|
66
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ANDROID_PUBLISHER_SCOPE,
|
|
3
|
+
loadGoogleIdentityServices,
|
|
4
|
+
requestGoogleAccessToken,
|
|
5
|
+
type RequestGoogleAccessTokenInput,
|
|
6
|
+
} from '../core/play-publish/google';
|
|
7
|
+
export {
|
|
8
|
+
publishToPlaystore,
|
|
9
|
+
PlaystorePublishError,
|
|
10
|
+
type PlaystorePublishInput,
|
|
11
|
+
type PlaystorePublishResult,
|
|
12
|
+
} from '../core/play-publish/publish';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { errorMessage } from '../core/errors';
|
|
3
|
+
import {
|
|
4
|
+
loadGoogleIdentityServices,
|
|
5
|
+
PlaystorePublishError,
|
|
6
|
+
publishToPlaystore,
|
|
7
|
+
requestGoogleAccessToken,
|
|
8
|
+
type PlaystorePublishInput,
|
|
9
|
+
type PlaystorePublishResult,
|
|
10
|
+
} from './index';
|
|
11
|
+
|
|
12
|
+
export type UsePlaystorePublishOptions = Partial<
|
|
13
|
+
Pick<PlaystorePublishInput, 'registryApiUrl' | 'token' | 'organizationId'>
|
|
14
|
+
> & {
|
|
15
|
+
/** Google OAuth Web application client ID used for sign-in. */
|
|
16
|
+
googleClientId?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type PlaystorePublishStatus = 'idle' | 'signing-in' | 'ready' | 'publishing' | 'published' | 'error';
|
|
20
|
+
|
|
21
|
+
export type PlaystorePublishRequest = Omit<
|
|
22
|
+
PlaystorePublishInput,
|
|
23
|
+
'registryApiUrl' | 'token' | 'organizationId' | 'accessToken'
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
export type UsePlaystorePublishResult = {
|
|
27
|
+
status: PlaystorePublishStatus;
|
|
28
|
+
isSignedIn: boolean;
|
|
29
|
+
versionCode?: number;
|
|
30
|
+
error?: string;
|
|
31
|
+
/** Registry error code driving per-cause UX, e.g. versionCodeExists. */
|
|
32
|
+
errorCode?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Warm up the Google sign-in script (e.g. on dialog open) so
|
|
35
|
+
* signInWithGoogle stays synchronous with the click. Resolves true once
|
|
36
|
+
* the script is ready; await it to gate the sign-in button, or ignore
|
|
37
|
+
* the result and rely on the blocked-popup error plus retry.
|
|
38
|
+
*/
|
|
39
|
+
preloadGoogle: () => Promise<boolean>;
|
|
40
|
+
signInWithGoogle: () => Promise<boolean>;
|
|
41
|
+
publish: (request: PlaystorePublishRequest) => Promise<PlaystorePublishResult | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Clears the outcome state (keeps the Google session). Does not cancel an
|
|
44
|
+
* in-flight sign-in or publish: their completion is ignored status-wise
|
|
45
|
+
* and new calls stay refused until the in-flight one settles.
|
|
46
|
+
*/
|
|
47
|
+
reset: () => void;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function usePlaystorePublish({
|
|
51
|
+
registryApiUrl,
|
|
52
|
+
token,
|
|
53
|
+
organizationId,
|
|
54
|
+
googleClientId,
|
|
55
|
+
}: UsePlaystorePublishOptions): UsePlaystorePublishResult {
|
|
56
|
+
const [status, setStatus] = useState<PlaystorePublishStatus>('idle');
|
|
57
|
+
// isSignedIn derives from token possession so the two can never diverge,
|
|
58
|
+
// even when a reset() during the popup makes the flow's status stale.
|
|
59
|
+
const [accessToken, setAccessToken] = useState<string | undefined>();
|
|
60
|
+
const [versionCode, setVersionCode] = useState<number | undefined>();
|
|
61
|
+
const [error, setError] = useState<string | undefined>();
|
|
62
|
+
const [errorCode, setErrorCode] = useState<string | undefined>();
|
|
63
|
+
// The token lives in a ref so publish() right after signInWithGoogle()
|
|
64
|
+
// in one handler sees it before React re-renders.
|
|
65
|
+
const accessTokenRef = useRef<string | undefined>(undefined);
|
|
66
|
+
// reset() bumps the generation so an abandoned in-flight call cannot
|
|
67
|
+
// overwrite the state it settles into.
|
|
68
|
+
const generationRef = useRef(0);
|
|
69
|
+
const busyRef = useRef(false);
|
|
70
|
+
|
|
71
|
+
const preloadGoogle = useCallback(
|
|
72
|
+
() =>
|
|
73
|
+
loadGoogleIdentityServices().then(
|
|
74
|
+
() => true,
|
|
75
|
+
() => false,
|
|
76
|
+
),
|
|
77
|
+
[],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Guard failures replace the whole outcome, like the main paths do: a
|
|
81
|
+
// stale errorCode or versionCode must not render beside the new error.
|
|
82
|
+
const failGuard = useCallback((message: string) => {
|
|
83
|
+
setError(message);
|
|
84
|
+
setErrorCode(undefined);
|
|
85
|
+
setVersionCode(undefined);
|
|
86
|
+
setStatus('error');
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
const signInWithGoogle = useCallback(async () => {
|
|
90
|
+
if (busyRef.current) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (!googleClientId) {
|
|
94
|
+
failGuard('Google OAuth client ID is not configured.');
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
busyRef.current = true;
|
|
98
|
+
const generation = generationRef.current;
|
|
99
|
+
setStatus('signing-in');
|
|
100
|
+
setError(undefined);
|
|
101
|
+
setErrorCode(undefined);
|
|
102
|
+
try {
|
|
103
|
+
const nextToken = await requestGoogleAccessToken({ clientId: googleClientId });
|
|
104
|
+
accessTokenRef.current = nextToken;
|
|
105
|
+
setAccessToken(nextToken);
|
|
106
|
+
if (generation === generationRef.current) {
|
|
107
|
+
setStatus('ready');
|
|
108
|
+
} else {
|
|
109
|
+
// A reset() during the popup ran before the token landed and left
|
|
110
|
+
// idle; upgrade it so status agrees with isSignedIn. Anything but
|
|
111
|
+
// idle belongs to a newer flow and must not be stomped.
|
|
112
|
+
setStatus((current) => (current === 'idle' ? 'ready' : current));
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
} catch (caught) {
|
|
116
|
+
if (generation === generationRef.current) {
|
|
117
|
+
setError(errorMessage(caught));
|
|
118
|
+
setStatus('error');
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
} finally {
|
|
122
|
+
busyRef.current = false;
|
|
123
|
+
}
|
|
124
|
+
}, [googleClientId]);
|
|
125
|
+
|
|
126
|
+
const publish = useCallback(
|
|
127
|
+
async (request: PlaystorePublishRequest) => {
|
|
128
|
+
if (busyRef.current) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
if (!registryApiUrl) {
|
|
132
|
+
failGuard('Registry URL is not configured.');
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
const googleToken = accessTokenRef.current;
|
|
136
|
+
if (!googleToken) {
|
|
137
|
+
failGuard('Sign in with Google before publishing.');
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
busyRef.current = true;
|
|
141
|
+
const generation = generationRef.current;
|
|
142
|
+
setStatus('publishing');
|
|
143
|
+
setError(undefined);
|
|
144
|
+
setErrorCode(undefined);
|
|
145
|
+
// A stale code from an earlier publish must not render as this
|
|
146
|
+
// attempt's success next to an error state.
|
|
147
|
+
setVersionCode(undefined);
|
|
148
|
+
try {
|
|
149
|
+
// Fields picked explicitly: spreading a wider-typed request object
|
|
150
|
+
// could smuggle same-named credential fields or excess properties
|
|
151
|
+
// into the request body.
|
|
152
|
+
const result = await publishToPlaystore({
|
|
153
|
+
registryApiUrl,
|
|
154
|
+
token,
|
|
155
|
+
organizationId,
|
|
156
|
+
accessToken: googleToken,
|
|
157
|
+
packageName: request.packageName,
|
|
158
|
+
assetId: request.assetId,
|
|
159
|
+
assetName: request.assetName,
|
|
160
|
+
track: request.track,
|
|
161
|
+
releaseStatus: request.releaseStatus,
|
|
162
|
+
});
|
|
163
|
+
if (generation === generationRef.current) {
|
|
164
|
+
setVersionCode(result.versionCode);
|
|
165
|
+
setStatus('published');
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
} catch (caught) {
|
|
169
|
+
if (generation === generationRef.current) {
|
|
170
|
+
setError(errorMessage(caught));
|
|
171
|
+
if (caught instanceof PlaystorePublishError) {
|
|
172
|
+
setErrorCode(caught.code);
|
|
173
|
+
}
|
|
174
|
+
setStatus('error');
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
} finally {
|
|
178
|
+
busyRef.current = false;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
[registryApiUrl, token, organizationId],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const reset = useCallback(() => {
|
|
185
|
+
generationRef.current += 1;
|
|
186
|
+
setVersionCode(undefined);
|
|
187
|
+
setError(undefined);
|
|
188
|
+
setErrorCode(undefined);
|
|
189
|
+
setStatus(accessTokenRef.current ? 'ready' : 'idle');
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
// Memoized so the result works as a prop or dependency without
|
|
193
|
+
// re-rendering consumers on unrelated parent renders.
|
|
194
|
+
return useMemo(
|
|
195
|
+
() => ({
|
|
196
|
+
status,
|
|
197
|
+
isSignedIn: accessToken !== undefined,
|
|
198
|
+
versionCode,
|
|
199
|
+
error,
|
|
200
|
+
errorCode,
|
|
201
|
+
preloadGoogle,
|
|
202
|
+
signInWithGoogle,
|
|
203
|
+
publish,
|
|
204
|
+
reset,
|
|
205
|
+
}),
|
|
206
|
+
[status, accessToken, versionCode, error, errorCode, preloadGoogle, signInWithGoogle, publish, reset],
|
|
207
|
+
);
|
|
208
|
+
}
|
package/vite.config.ts
CHANGED
|
@@ -13,10 +13,13 @@ export default defineConfig({
|
|
|
13
13
|
index: resolve(__dirname, 'src/index.ts'),
|
|
14
14
|
'app-store-relay/index': resolve(__dirname, 'src/app-store-relay/index.ts'),
|
|
15
15
|
'app-store-relay/react': resolve(__dirname, 'src/app-store-relay/react.ts'),
|
|
16
|
+
'apple-credentials/index': resolve(__dirname, 'src/apple-credentials/index.ts'),
|
|
16
17
|
'device-build/index': resolve(__dirname, 'src/device-build/index.ts'),
|
|
17
18
|
'device-build/react': resolve(__dirname, 'src/device-build/react.ts'),
|
|
18
19
|
'device-install/index': resolve(__dirname, 'src/device-install/index.ts'),
|
|
19
20
|
'device-install/react': resolve(__dirname, 'src/device-install/react.ts'),
|
|
21
|
+
'play-publish/index': resolve(__dirname, 'src/play-publish/index.ts'),
|
|
22
|
+
'play-publish/react': resolve(__dirname, 'src/play-publish/react.ts'),
|
|
20
23
|
},
|
|
21
24
|
name: 'LimrunUI',
|
|
22
25
|
formats: ['es', 'cjs'],
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
const v = "limbuild-device-pairing";
|
|
2
|
-
const d = "pairRecords", w = "limbuild-device-signing";
|
|
3
|
-
const c = "signingAssets";
|
|
4
|
-
function l(e) {
|
|
5
|
-
return (e ?? "").replace(/-/g, "").replace(/[^a-fA-F0-9]/g, "");
|
|
6
|
-
}
|
|
7
|
-
function f(e) {
|
|
8
|
-
return (e ?? "").trim();
|
|
9
|
-
}
|
|
10
|
-
async function R(e) {
|
|
11
|
-
const n = l(e);
|
|
12
|
-
if (!n) return;
|
|
13
|
-
const r = await p(v, 1, d, "udid");
|
|
14
|
-
return g(
|
|
15
|
-
r.transaction(d, "readonly").objectStore(d).get(n)
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
async function G(e, n = {}) {
|
|
19
|
-
const r = l(e.udid);
|
|
20
|
-
if (!r) throw new Error("Cannot store pair record without a UDID.");
|
|
21
|
-
const t = {
|
|
22
|
-
...e,
|
|
23
|
-
udid: r,
|
|
24
|
-
productName: n.productName,
|
|
25
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26
|
-
}, i = await p(v, 1, d, "udid");
|
|
27
|
-
return await g(
|
|
28
|
-
i.transaction(d, "readwrite").objectStore(d).put(t)
|
|
29
|
-
), t;
|
|
30
|
-
}
|
|
31
|
-
async function O({
|
|
32
|
-
deviceUDID: e,
|
|
33
|
-
bundleID: n,
|
|
34
|
-
signingMode: r = "development"
|
|
35
|
-
}) {
|
|
36
|
-
const t = f(n);
|
|
37
|
-
if (!t) return;
|
|
38
|
-
const i = l(e), s = await S(D("bundle", t, r));
|
|
39
|
-
if (s) return s;
|
|
40
|
-
if (i) {
|
|
41
|
-
const o = await S(D(i, t, r));
|
|
42
|
-
if (o) return o;
|
|
43
|
-
}
|
|
44
|
-
return (await h(t, r))[0];
|
|
45
|
-
}
|
|
46
|
-
async function T(e = "development") {
|
|
47
|
-
return (await m()).filter((r) => (r.signingMode ?? "development") === e).sort((r, t) => new Date(t.updatedAt).getTime() - new Date(r.updatedAt).getTime())[0];
|
|
48
|
-
}
|
|
49
|
-
async function V(e, n = "development") {
|
|
50
|
-
return (await m()).filter((t) => !t.certificateID || !t.certificateP12Base64 || (t.signingMode ?? "development") !== n ? !1 : !e || !t.teamID || t.teamID === e).sort((t, i) => new Date(i.updatedAt).getTime() - new Date(t.updatedAt).getTime())[0];
|
|
51
|
-
}
|
|
52
|
-
async function z(e) {
|
|
53
|
-
const n = f(e.bundleID);
|
|
54
|
-
if (!n)
|
|
55
|
-
throw new Error("Cannot store signing assets without a bundle ID.");
|
|
56
|
-
const r = l(e.deviceUDID), t = e.signingMode ?? "development", i = D("bundle", n, t), s = {
|
|
57
|
-
...e,
|
|
58
|
-
id: i,
|
|
59
|
-
deviceUDID: r || void 0,
|
|
60
|
-
signingMode: t,
|
|
61
|
-
bundleID: n,
|
|
62
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
63
|
-
}, a = await p(w, 1, c, "id");
|
|
64
|
-
return await g(
|
|
65
|
-
a.transaction(c, "readwrite").objectStore(c).put(s)
|
|
66
|
-
), s;
|
|
67
|
-
}
|
|
68
|
-
async function h(e, n = "development") {
|
|
69
|
-
const r = f(e);
|
|
70
|
-
return r ? (await m()).filter(
|
|
71
|
-
(i) => i.bundleID === r && (i.signingMode ?? "development") === n
|
|
72
|
-
) : [];
|
|
73
|
-
}
|
|
74
|
-
function U(e, n) {
|
|
75
|
-
const r = l(n);
|
|
76
|
-
return !!r && e.provisionedDevices.some((t) => l(t) === r);
|
|
77
|
-
}
|
|
78
|
-
function j(e, n) {
|
|
79
|
-
const r = f(n), t = f(e.bundleID);
|
|
80
|
-
if (!r || !t) return !1;
|
|
81
|
-
if (t === r || t === "*") return !0;
|
|
82
|
-
if (!t.endsWith(".*")) return !1;
|
|
83
|
-
const i = t.slice(0, -1);
|
|
84
|
-
return r.startsWith(i);
|
|
85
|
-
}
|
|
86
|
-
async function M(e) {
|
|
87
|
-
return E(new Uint8Array(await e.arrayBuffer()));
|
|
88
|
-
}
|
|
89
|
-
function C(e) {
|
|
90
|
-
const n = atob(e), r = new Uint8Array(n.length);
|
|
91
|
-
for (let t = 0; t < n.length; t += 1)
|
|
92
|
-
r[t] = n.charCodeAt(t);
|
|
93
|
-
return E(r);
|
|
94
|
-
}
|
|
95
|
-
function E(e) {
|
|
96
|
-
const n = new TextDecoder("latin1").decode(e), r = n.indexOf("<?xml"), t = n.indexOf("</plist>");
|
|
97
|
-
if (r < 0 || t < r)
|
|
98
|
-
throw new Error("Provisioning profile plist not found.");
|
|
99
|
-
const i = n.slice(r, t + 8);
|
|
100
|
-
if (/<!ENTITY/i.test(i))
|
|
101
|
-
throw new Error("Provisioning profile plist contains a disallowed ENTITY declaration.");
|
|
102
|
-
const s = new DOMParser().parseFromString(i, "application/xml");
|
|
103
|
-
if (s.querySelector("parsererror"))
|
|
104
|
-
throw new Error("Provisioning profile plist could not be parsed.");
|
|
105
|
-
const a = s.querySelector("plist > dict");
|
|
106
|
-
if (!a)
|
|
107
|
-
throw new Error("Provisioning profile plist dictionary not found.");
|
|
108
|
-
const o = A(a);
|
|
109
|
-
if (!b(o))
|
|
110
|
-
throw new Error("Provisioning profile plist has an unexpected shape.");
|
|
111
|
-
const I = b(o.Entitlements) ? o.Entitlements : {}, y = u(I["application-identifier"]), P = B(y);
|
|
112
|
-
return {
|
|
113
|
-
name: u(o.Name),
|
|
114
|
-
uuid: u(o.UUID),
|
|
115
|
-
teamID: u(I["com.apple.developer.team-identifier"]) ?? N(o.TeamIdentifier)[0],
|
|
116
|
-
applicationIdentifier: y,
|
|
117
|
-
bundleID: P,
|
|
118
|
-
provisionedDevices: N(o.ProvisionedDevices),
|
|
119
|
-
getTaskAllow: _(I["get-task-allow"]),
|
|
120
|
-
expirationDate: u(o.ExpirationDate)
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
async function S(e) {
|
|
124
|
-
if (!e) return;
|
|
125
|
-
const n = await p(w, 1, c, "id");
|
|
126
|
-
return g(
|
|
127
|
-
n.transaction(c, "readonly").objectStore(c).get(e)
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
async function m() {
|
|
131
|
-
const e = await p(w, 1, c, "id");
|
|
132
|
-
return g(
|
|
133
|
-
e.transaction(c, "readonly").objectStore(c).getAll()
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
function D(e, n, r) {
|
|
137
|
-
return `${r}:${e}:${n}`;
|
|
138
|
-
}
|
|
139
|
-
function B(e) {
|
|
140
|
-
if (!e) return;
|
|
141
|
-
const n = e.indexOf(".");
|
|
142
|
-
return n >= 0 ? e.slice(n + 1) : void 0;
|
|
143
|
-
}
|
|
144
|
-
function A(e) {
|
|
145
|
-
switch (e.tagName) {
|
|
146
|
-
case "dict":
|
|
147
|
-
return x(e);
|
|
148
|
-
case "array":
|
|
149
|
-
return Array.from(e.children).map(A);
|
|
150
|
-
case "string":
|
|
151
|
-
case "date":
|
|
152
|
-
return e.textContent ?? "";
|
|
153
|
-
default:
|
|
154
|
-
return e.textContent ?? "";
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function x(e) {
|
|
158
|
-
const n = {}, r = Array.from(e.children);
|
|
159
|
-
for (let t = 0; t < r.length; t += 2) {
|
|
160
|
-
const i = r[t], s = r[t + 1];
|
|
161
|
-
!i || i.tagName !== "key" || !s || (n[i.textContent ?? ""] = A(s));
|
|
162
|
-
}
|
|
163
|
-
return n;
|
|
164
|
-
}
|
|
165
|
-
function u(e) {
|
|
166
|
-
return typeof e == "string" && e ? e : void 0;
|
|
167
|
-
}
|
|
168
|
-
function N(e) {
|
|
169
|
-
return Array.isArray(e) ? e.filter((n) => typeof n == "string") : [];
|
|
170
|
-
}
|
|
171
|
-
function _(e) {
|
|
172
|
-
return typeof e == "boolean" ? e : void 0;
|
|
173
|
-
}
|
|
174
|
-
function b(e) {
|
|
175
|
-
return typeof e == "object" && e !== null && !Array.isArray(e);
|
|
176
|
-
}
|
|
177
|
-
function p(e, n, r, t) {
|
|
178
|
-
return new Promise((i, s) => {
|
|
179
|
-
const a = indexedDB.open(e, n);
|
|
180
|
-
a.onupgradeneeded = () => {
|
|
181
|
-
const o = a.result;
|
|
182
|
-
o.objectStoreNames.contains(r) || o.createObjectStore(r, { keyPath: t });
|
|
183
|
-
}, a.onsuccess = () => i(a.result), a.onerror = () => s(a.error ?? new Error("Open IndexedDB failed"));
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
function g(e) {
|
|
187
|
-
return new Promise((n, r) => {
|
|
188
|
-
e.onsuccess = () => n(e.result), e.onerror = () => r(e.error ?? new Error("IndexedDB request failed"));
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
export {
|
|
192
|
-
z as a,
|
|
193
|
-
j as b,
|
|
194
|
-
U as c,
|
|
195
|
-
V as d,
|
|
196
|
-
O as e,
|
|
197
|
-
C as f,
|
|
198
|
-
T as g,
|
|
199
|
-
E as h,
|
|
200
|
-
f as i,
|
|
201
|
-
R as j,
|
|
202
|
-
G as k,
|
|
203
|
-
l as n,
|
|
204
|
-
M as p
|
|
205
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";const b="limbuild-device-pairing";const l="pairRecords",w="limbuild-device-signing";const c="signingAssets";function d(e){return(e??"").replace(/-/g,"").replace(/[^a-fA-F0-9]/g,"")}function u(e){return(e??"").trim()}async function E(e){const n=d(e);if(!n)return;const r=await g(b,1,l,"udid");return p(r.transaction(l,"readonly").objectStore(l).get(n))}async function h(e,n={}){const r=d(e.udid);if(!r)throw new Error("Cannot store pair record without a UDID.");const t={...e,udid:r,productName:n.productName,updatedAt:new Date().toISOString()},i=await g(b,1,l,"udid");return await p(i.transaction(l,"readwrite").objectStore(l).put(t)),t}async function x({deviceUDID:e,bundleID:n,signingMode:r="development"}){const t=u(n);if(!t)return;const i=d(e),s=await v(I("bundle",t,r));if(s)return s;if(i){const o=await v(I(i,t,r));if(o)return o}return(await O(t,r))[0]}async function _(e="development"){return(await A()).filter(r=>(r.signingMode??"development")===e).sort((r,t)=>new Date(t.updatedAt).getTime()-new Date(r.updatedAt).getTime())[0]}async function R(e,n="development"){return(await A()).filter(t=>!t.certificateID||!t.certificateP12Base64||(t.signingMode??"development")!==n?!1:!e||!t.teamID||t.teamID===e).sort((t,i)=>new Date(i.updatedAt).getTime()-new Date(t.updatedAt).getTime())[0]}async function G(e){const n=u(e.bundleID);if(!n)throw new Error("Cannot store signing assets without a bundle ID.");const r=d(e.deviceUDID),t=e.signingMode??"development",i=I("bundle",n,t),s={...e,id:i,deviceUDID:r||void 0,signingMode:t,bundleID:n,updatedAt:new Date().toISOString()},a=await g(w,1,c,"id");return await p(a.transaction(c,"readwrite").objectStore(c).put(s)),s}async function O(e,n="development"){const r=u(e);return r?(await A()).filter(i=>i.bundleID===r&&(i.signingMode??"development")===n):[]}function T(e,n){const r=d(n);return!!r&&e.provisionedDevices.some(t=>d(t)===r)}function z(e,n){const r=u(n),t=u(e.bundleID);if(!r||!t)return!1;if(t===r||t==="*")return!0;if(!t.endsWith(".*"))return!1;const i=t.slice(0,-1);return r.startsWith(i)}async function U(e){return m(new Uint8Array(await e.arrayBuffer()))}function V(e){const n=atob(e),r=new Uint8Array(n.length);for(let t=0;t<n.length;t+=1)r[t]=n.charCodeAt(t);return m(r)}function m(e){const n=new TextDecoder("latin1").decode(e),r=n.indexOf("<?xml"),t=n.indexOf("</plist>");if(r<0||t<r)throw new Error("Provisioning profile plist not found.");const i=n.slice(r,t+8);if(/<!ENTITY/i.test(i))throw new Error("Provisioning profile plist contains a disallowed ENTITY declaration.");const s=new DOMParser().parseFromString(i,"application/xml");if(s.querySelector("parsererror"))throw new Error("Provisioning profile plist could not be parsed.");const a=s.querySelector("plist > dict");if(!a)throw new Error("Provisioning profile plist dictionary not found.");const o=S(a);if(!P(o))throw new Error("Provisioning profile plist has an unexpected shape.");const D=P(o.Entitlements)?o.Entitlements:{},y=f(D["application-identifier"]),B=C(y);return{name:f(o.Name),uuid:f(o.UUID),teamID:f(D["com.apple.developer.team-identifier"])??N(o.TeamIdentifier)[0],applicationIdentifier:y,bundleID:B,provisionedDevices:N(o.ProvisionedDevices),getTaskAllow:j(D["get-task-allow"]),expirationDate:f(o.ExpirationDate)}}async function v(e){if(!e)return;const n=await g(w,1,c,"id");return p(n.transaction(c,"readonly").objectStore(c).get(e))}async function A(){const e=await g(w,1,c,"id");return p(e.transaction(c,"readonly").objectStore(c).getAll())}function I(e,n,r){return`${r}:${e}:${n}`}function C(e){if(!e)return;const n=e.indexOf(".");return n>=0?e.slice(n+1):void 0}function S(e){switch(e.tagName){case"dict":return M(e);case"array":return Array.from(e.children).map(S);case"string":case"date":return e.textContent??"";default:return e.textContent??""}}function M(e){const n={},r=Array.from(e.children);for(let t=0;t<r.length;t+=2){const i=r[t],s=r[t+1];!i||i.tagName!=="key"||!s||(n[i.textContent??""]=S(s))}return n}function f(e){return typeof e=="string"&&e?e:void 0}function N(e){return Array.isArray(e)?e.filter(n=>typeof n=="string"):[]}function j(e){return typeof e=="boolean"?e:void 0}function P(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function g(e,n,r,t){return new Promise((i,s)=>{const a=indexedDB.open(e,n);a.onupgradeneeded=()=>{const o=a.result;o.objectStoreNames.contains(r)||o.createObjectStore(r,{keyPath:t})},a.onsuccess=()=>i(a.result),a.onerror=()=>s(a.error??new Error("Open IndexedDB failed"))})}function p(e){return new Promise((n,r)=>{e.onsuccess=()=>n(e.result),e.onerror=()=>r(e.error??new Error("IndexedDB request failed"))})}exports.getLatestSigningAssets=_;exports.getLatestSigningAssetsWithCertificate=R;exports.getPairRecord=E;exports.getSigningAssets=x;exports.normalizeBundleID=u;exports.normalizeUDID=d;exports.parseProvisioningProfile=U;exports.parseProvisioningProfileBase64=V;exports.parseProvisioningProfileBytes=m;exports.profileContainsDevice=T;exports.profileMatchesBundleID=z;exports.putPairRecord=h;exports.putSigningAssets=G;
|
package/dist/client-BtC4n_qf.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";const F=require("./index-CMeQfhYy.js");class m{constructor(t,r,o){this.apiUrl=t,this.token=r,this.organizationId=o}socket;nextId=1;pending=new Map;async connect(){if(this.socket&&this.socket.readyState===WebSocket.OPEN)return;const t=new WebSocket(E(this.apiUrl,this.token,this.organizationId));this.socket=t,t.onmessage=r=>this.handleMessage(r),t.onclose=()=>this.rejectPending(new Error("Apple relay WebSocket closed")),t.onerror=()=>this.rejectPending(new Error("Apple relay WebSocket failed")),await new Promise((r,o)=>{t.onopen=()=>r(),t.onerror=()=>o(new Error("Apple relay WebSocket connection failed"))})}request(t,r){const o=this.socket;if(!o||o.readyState!==WebSocket.OPEN)throw new Error("Apple relay WebSocket is not connected.");const a=String(this.nextId++),n=new Promise((p,i)=>{this.pending.set(a,{resolve:h=>p(q(h)),reject:i})});return o.send(JSON.stringify({id:a,type:t,...r===void 0?{}:{payload:r}})),n}close(){this.rejectPending(new Error("Apple relay WebSocket closed")),this.socket?.close(),this.socket=void 0}handleMessage(t){const r=JSON.parse(String(t.data)),o=this.pending.get(r.id);if(o){if(this.pending.delete(r.id),!r.ok){o.reject(new Error(r.error||"Apple relay request failed"));return}if(!r.response){o.reject(new Error("Apple relay response was empty."));return}o.resolve(r.response)}}rejectPending(t){for(const r of this.pending.values())r.reject(t);this.pending.clear()}}async function g(e,t,r){const o=new m(e,t,r);return await o.connect(),o}async function A(e,t){return e.request("srpInit",t)}async function S(e,t){return e.request("srpComplete",t)}async function P(e){return e.request("triggerTrustedDevice2FA")}async function b(e,t,r="sms"){return e.request("triggerPhone2FA",{phoneNumberId:t,mode:r})}async function k(e,t){return e.request("submitTrustedDevice2FA",{code:t})}async function T(e,t,r,o="sms"){return e.request("submitPhone2FA",{phoneNumberId:t,mode:o,code:r})}async function v(e){return e.request("finalize")}async function R(e,t){return e.request("provisioning",t)}function q(e){if(e.body!==void 0||!e.rawBody)return e;try{return{...e,body:JSON.parse(e.rawBody)}}catch{return e}}function E(e,t,r){const o=new URL(e);return o.protocol=o.protocol==="https:"?"wss:":"ws:",o.pathname=`${o.pathname.replace(/\/$/,"")}/ios/appstoreconnect/ws`,t&&o.searchParams.set("token",t),r&&o.searchParams.set("organization",r),o.toString()}async function x({registryApiUrl:e,accountName:t,password:r,token:o,organizationId:a}){const n=await g(e,o,a);try{const p=new F.AppleGsaSrpClient(t),i=await A(n,await p.init());if(i.status<200||i.status>=300)throw new Error(`Apple SRP init failed: HTTP ${i.status} ${i.rawBody??""}`.trim());if(!i.body)throw new Error("Apple SRP init response did not include a body.");const h=await p.complete(r,i.body),u=await S(n,{...h,rememberMe:!1,trustTokens:[]}),y=u.status===409;let s,d={type:"trustedDevice"};if(y){s=await P(n);const c=N(s.body);if(s.status===412){if(!c)throw new Error("Apple requested phone verification but did not include a trusted phone number.");d={type:"phone",phoneNumberId:c.id,mode:c.pushMode??"sms"},s=await b(n,c.id,c.pushMode??"sms")}if(s.status<200||s.status>=300)throw new Error(`Apple two-factor challenge failed: HTTP ${s.status} ${s.rawBody??""}`.trim())}else if(u.status<200||u.status>=300)throw new Error(`Apple SRP complete failed: HTTP ${u.status} ${u.rawBody??""}`.trim());return{relay:n,completeResponse:u,twoFactorChallengeResponse:s,requiresTwoFactor:y,finishTwoFactor:async c=>{const l=d.type==="phone"?await T(n,d.phoneNumberId,c,d.mode):await k(n,c);if(l.status<200||l.status>=300)throw new Error(`Apple two-factor code failed: HTTP ${l.status} ${l.rawBody??""}`.trim());return l},finalize:async()=>v(n),close:async()=>n.close()}}catch(p){throw n.close(),p}}function N(e){if(!f(e))return;const t=f(e.phoneNumberVerification)?e.phoneNumberVerification:void 0,r=w(t?.trustedPhoneNumber)??w(e.trustedPhoneNumber)??w(e.phoneNumber);if(!r)return;const o=r.id;if(typeof o!="number")return;const a=typeof r.pushMode=="string"?r.pushMode:typeof e.mode=="string"?e.mode:void 0;return{id:o,pushMode:a}}function w(e){return f(e)?e:void 0}function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}exports.AppleRelayWebSocketClient=m;exports.fetchAppleAccountSession=v;exports.openAppleRelayWebSocket=g;exports.proxyPhoneTwoFactorCode=T;exports.proxyProvisioningRequest=R;exports.proxySrpComplete=S;exports.proxySrpInit=A;exports.proxyTwoFactorCode=k;exports.startBrowserOwnedAppleIDLogin=x;exports.triggerPhoneTwoFactor=b;exports.triggerTrustedDeviceTwoFactor=P;
|