@shipeasy/devtools-core 1.0.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 (55) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +19 -0
  3. package/dist/api.d.mts +143 -0
  4. package/dist/api.d.ts +143 -0
  5. package/dist/api.js +10 -0
  6. package/dist/api.mjs +10 -0
  7. package/dist/auth.d.mts +57 -0
  8. package/dist/auth.d.ts +57 -0
  9. package/dist/auth.js +12 -0
  10. package/dist/auth.mjs +12 -0
  11. package/dist/chunk-2B75JLTX.js +25 -0
  12. package/dist/chunk-5VQLXGNR.mjs +101 -0
  13. package/dist/chunk-6SU5REDK.mjs +2848 -0
  14. package/dist/chunk-7NO55TFC.mjs +25 -0
  15. package/dist/chunk-GUTBZI2O.js +2848 -0
  16. package/dist/chunk-I2KLQ2HA.js +22 -0
  17. package/dist/chunk-KPO3R7ND.js +28 -0
  18. package/dist/chunk-LCRTLHGL.js +101 -0
  19. package/dist/chunk-LZDNBMH6.mjs +1394 -0
  20. package/dist/chunk-MK56AKZR.js +178 -0
  21. package/dist/chunk-RANNOC72.js +72 -0
  22. package/dist/chunk-RH6X24HG.mjs +28 -0
  23. package/dist/chunk-U5DDQYSF.mjs +22 -0
  24. package/dist/chunk-VHYOR3XA.mjs +178 -0
  25. package/dist/chunk-WP2TWHG3.mjs +72 -0
  26. package/dist/chunk-YQHCUZGP.js +1394 -0
  27. package/dist/feedback-state.d.mts +29 -0
  28. package/dist/feedback-state.d.ts +29 -0
  29. package/dist/feedback-state.js +9 -0
  30. package/dist/feedback-state.mjs +9 -0
  31. package/dist/forms.d.mts +95 -0
  32. package/dist/forms.d.ts +95 -0
  33. package/dist/forms.js +21 -0
  34. package/dist/forms.mjs +21 -0
  35. package/dist/gate-flow.d.mts +41 -0
  36. package/dist/gate-flow.d.ts +41 -0
  37. package/dist/gate-flow.js +17 -0
  38. package/dist/gate-flow.mjs +17 -0
  39. package/dist/index.d.mts +10 -0
  40. package/dist/index.d.ts +10 -0
  41. package/dist/index.js +86 -0
  42. package/dist/index.mjs +86 -0
  43. package/dist/public-report.d.mts +56 -0
  44. package/dist/public-report.d.ts +56 -0
  45. package/dist/public-report.js +12 -0
  46. package/dist/public-report.mjs +12 -0
  47. package/dist/self-report.d.mts +25 -0
  48. package/dist/self-report.d.ts +25 -0
  49. package/dist/self-report.js +73 -0
  50. package/dist/self-report.mjs +73 -0
  51. package/dist/types-D0sroUDD.d.mts +278 -0
  52. package/dist/types-D0sroUDD.d.ts +278 -0
  53. package/dist/zod.gen-DTKpgw-T.d.mts +64 -0
  54. package/dist/zod.gen-DTKpgw-T.d.ts +64 -0
  55. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,40 @@
1
+ Shipeasy Source-Available License (Shipeasy-SAL) 1.0
2
+
3
+ Copyright (c) 2026 Shipeasy, Inc. All rights reserved.
4
+
5
+ 1. License Grant.
6
+ Subject to the terms of this License, Shipeasy, Inc. ("Shipeasy") grants
7
+ you a non-exclusive, non-transferable, revocable, worldwide license to:
8
+
9
+ (a) Use, copy, and modify the Software solely as a client integration for
10
+ interacting with Shipeasy's hosted services (the "Service");
11
+ (b) Distribute the Software as part of an application that calls the
12
+ Service, in object form, provided the recipient also agrees to this
13
+ License.
14
+
15
+ 2. Restrictions.
16
+ You may not:
17
+
18
+ (a) Use the Software, in whole or in part, to build, host, or operate any
19
+ service that competes with the Service or that provides feature-flag,
20
+ experimentation, configuration, internationalization, or related
21
+ functionality to third parties on a commercial basis;
22
+ (b) Sublicense, sell, rent, or lease the Software;
23
+ (c) Remove or alter copyright notices, license terms, or attribution.
24
+
25
+ 3. Contributions.
26
+ Any pull request you submit is licensed back to Shipeasy under this
27
+ License plus a perpetual, irrevocable right for Shipeasy to relicense.
28
+
29
+ 4. Trademarks.
30
+ This License does not grant rights in the names "Shipeasy", related
31
+ marks, or logos.
32
+
33
+ 5. No Warranty / Limitation of Liability.
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. IN NO
35
+ EVENT SHALL SHIPEASY BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
36
+ LIABILITY ARISING FROM USE OF THE SOFTWARE.
37
+
38
+ 6. Termination.
39
+ This License terminates automatically if you breach it. Sections 2-5
40
+ survive termination.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @shipeasy/devtools-core
2
+
3
+ Headless core shared by the [Shipeasy](https://shipeasy.ai) devtools overlays —
4
+ the admin API client, PKCE device auth, public bug intake, and the generated
5
+ form schemas.
6
+
7
+ You normally do not install this directly: it comes with
8
+ [`@shipeasy/react-native-devtools`](https://www.npmjs.com/package/@shipeasy/react-native-devtools).
9
+ Install it on its own only to build a custom devtools surface.
10
+
11
+ ```bash
12
+ npm install @shipeasy/devtools-core
13
+ ```
14
+
15
+ It reads the app's live SDK state over the `globalThis` bridge the client Engine
16
+ publishes (`@shipeasy/sdk/devtools-contract`) rather than importing the client,
17
+ so it can never inline a second Engine. `@shipeasy/sdk` is therefore a peer.
18
+
19
+ Full docs: <https://shipeasy-ai.github.io/sdk-ts/>
package/dist/api.d.mts ADDED
@@ -0,0 +1,143 @@
1
+ import { o as ProjectRecord, G as GateRecord, C as ConfigRecord, E as ExperimentRecord, U as UniverseRecord, m as ProfileRecord, g as DraftRecord, K as KeyRecord, c as BugRecord, B as BugDetail, O as OpsItemDetail, h as FeatureRequestRecord, F as FeatureRequestDetail, a as AttachmentUploadResult } from './types-D0sroUDD.mjs';
2
+ import { C as CreateBugRequestInput, a as CreateFeatureRequestRequestInput } from './zod.gen-DTKpgw-T.mjs';
3
+ import 'zod';
4
+
5
+ /** Thrown when an admin request returns 401. Distinct from generic `Error`
6
+ * so callers can branch on it without string-matching. */
7
+ declare class AuthError extends Error {
8
+ readonly status = 401;
9
+ constructor(message: string);
10
+ }
11
+ interface DevtoolsClientOptions {
12
+ /** Raw admin SDK key from device auth (`sdk_admin_*`). */
13
+ token: string;
14
+ projectId: string;
15
+ /** Admin API origin. Defaults to production (`https://shipeasy.ai`). */
16
+ adminBaseUrl?: string;
17
+ /** Called once per 401 so the host can clear its stored session and
18
+ * re-prompt login. The request still rejects with `AuthError`. */
19
+ onUnauthed?: () => void;
20
+ /** Injectable fetch for tests / exotic runtimes. Defaults to global fetch.
21
+ * Note the generated client invokes it with a single `Request` argument. */
22
+ fetch?: typeof fetch;
23
+ }
24
+ declare class DevtoolsClient {
25
+ private cache;
26
+ private readonly adminBaseUrl;
27
+ /** Raw admin bearer key for this session. Public so overlays can compare it
28
+ * against their stored session and rebuild the client on session swap. */
29
+ readonly token: string;
30
+ readonly projectId: string;
31
+ private readonly onUnauthed?;
32
+ private readonly fetchImpl;
33
+ /** Session-configured generated client — every spec-covered call goes
34
+ * through it (bearer auth, base URL, injectable fetch). */
35
+ private readonly api;
36
+ constructor(opts: DevtoolsClientOptions);
37
+ /** Admin origin this client talks to. Overlays build dashboard deep links
38
+ * ("Open in dashboard ↗") off it. */
39
+ get adminUrl(): string;
40
+ /** Run `fn` once per `key` and cache the resulting promise. Rejections are
41
+ * evicted so the next call retries instead of replaying the failure. */
42
+ private memo;
43
+ /** Drop all cached responses (pull-to-refresh). */
44
+ invalidate(): void;
45
+ /** Build + throw the error for a failed request. 401 produces an `AuthError`
46
+ * and fires `onUnauthed` so the host can drop the stale session instead of
47
+ * leaving the user staring at a failed-load message. */
48
+ private fail;
49
+ /** Resolve a generated op result to its body, or throw the normalized
50
+ * error. Write ops with empty bodies pass on any 2xx. */
51
+ private unwrap;
52
+ /** Some list endpoints return a bare array, others a `{ data }` envelope
53
+ * (the spec is the contract, but be tolerant like the overlays always
54
+ * were). */
55
+ private asList;
56
+ /**
57
+ * Drain a paginated `{ data, next_cursor }` list op by walking cursors.
58
+ * Devtools shows the entire list at once; without this, large projects would
59
+ * silently truncate at the first page (limit=100 default).
60
+ */
61
+ private drain;
62
+ private rawInit;
63
+ private rawGet;
64
+ private rawSend;
65
+ /** Read the response error body for diagnostics. Best-effort; never throws. */
66
+ private readErrorDetail;
67
+ project(): Promise<ProjectRecord>;
68
+ gates(): Promise<GateRecord[]>;
69
+ configs(): Promise<ConfigRecord[]>;
70
+ /** Experiments list. The endpoint returns the non-archived set (running /
71
+ * draft / stopped) by default; pass `{ archived: true }` for the archive tab
72
+ * (a separate memo bucket so the overlay's Archived section loads on demand). */
73
+ experiments(opts?: {
74
+ archived?: boolean;
75
+ }): Promise<ExperimentRecord[]>;
76
+ universes(): Promise<UniverseRecord[]>;
77
+ profiles(): Promise<ProfileRecord[]>;
78
+ drafts(): Promise<DraftRecord[]>;
79
+ keys(profileId?: string): Promise<KeyRecord[]>;
80
+ createDraft(input: {
81
+ profileId: string;
82
+ name: string;
83
+ }): Promise<DraftRecord>;
84
+ upsertDraftKey(draftId: string, key: string, value: string): Promise<void>;
85
+ updateKeyById(id: string, value: string): Promise<void>;
86
+ upsertKeys(profileId: string, keys: Array<{
87
+ key: string;
88
+ value: string;
89
+ }>, chunk?: string): Promise<void>;
90
+ /** Drop every cached `keys(profileId?)` response. We don't track which
91
+ * profile a single key write affects, so be safe and clear them all. */
92
+ private invalidateKeysCache;
93
+ bugs(): Promise<BugRecord[]>;
94
+ bug(id: string): Promise<BugDetail>;
95
+ /** Auto-filed error tickets. Same list endpoint, `type=error`. */
96
+ errors(): Promise<BugRecord[]>;
97
+ /** Auto-filed alert tickets. Same list endpoint, `type=alert`. */
98
+ alerts(): Promise<BugRecord[]>;
99
+ /** Generic ops-item detail — works for any type (bug/feature/error/alert). */
100
+ opsItem(id: string): Promise<OpsItemDetail>;
101
+ /** Triage-only update (status / priority) that works for ANY item type —
102
+ * the only editable shape for auto-filed error/alert tickets. Scrubs every
103
+ * list bucket + the item's detail so the panels refetch. */
104
+ updateOps(id: string, patch: {
105
+ status?: BugRecord["status"];
106
+ priority?: BugRecord["priority"];
107
+ }): Promise<void>;
108
+ createBug(input: Omit<CreateBugRequestInput, "type">): Promise<{
109
+ id: string;
110
+ }>;
111
+ updateBug(id: string, patch: {
112
+ title?: string;
113
+ stepsToReproduce?: string;
114
+ actualResult?: string;
115
+ expectedResult?: string;
116
+ status?: BugRecord["status"];
117
+ priority?: BugRecord["priority"];
118
+ }): Promise<void>;
119
+ featureRequests(): Promise<FeatureRequestRecord[]>;
120
+ featureRequest(id: string): Promise<FeatureRequestDetail>;
121
+ createFeatureRequest(input: Omit<CreateFeatureRequestRequestInput, "type">): Promise<{
122
+ id: string;
123
+ }>;
124
+ updateFeatureRequest(id: string, patch: {
125
+ title?: string;
126
+ description?: string;
127
+ useCase?: string;
128
+ status?: FeatureRequestRecord["status"];
129
+ priority?: FeatureRequestRecord["priority"];
130
+ }): Promise<void>;
131
+ /** Fetch an attachment file as a Blob via the authenticated stream route
132
+ * (an <Image>/<img> can't send the Authorization header itself). */
133
+ attachmentBlob(id: string): Promise<Blob>;
134
+ uploadAttachment(args: {
135
+ reportKind: "bug" | "feature_request";
136
+ reportId: string;
137
+ kind: "screenshot" | "recording" | "file";
138
+ filename: string;
139
+ blob: Blob;
140
+ }): Promise<AttachmentUploadResult>;
141
+ }
142
+
143
+ export { AuthError, DevtoolsClient, type DevtoolsClientOptions };
package/dist/api.d.ts ADDED
@@ -0,0 +1,143 @@
1
+ import { o as ProjectRecord, G as GateRecord, C as ConfigRecord, E as ExperimentRecord, U as UniverseRecord, m as ProfileRecord, g as DraftRecord, K as KeyRecord, c as BugRecord, B as BugDetail, O as OpsItemDetail, h as FeatureRequestRecord, F as FeatureRequestDetail, a as AttachmentUploadResult } from './types-D0sroUDD.js';
2
+ import { C as CreateBugRequestInput, a as CreateFeatureRequestRequestInput } from './zod.gen-DTKpgw-T.js';
3
+ import 'zod';
4
+
5
+ /** Thrown when an admin request returns 401. Distinct from generic `Error`
6
+ * so callers can branch on it without string-matching. */
7
+ declare class AuthError extends Error {
8
+ readonly status = 401;
9
+ constructor(message: string);
10
+ }
11
+ interface DevtoolsClientOptions {
12
+ /** Raw admin SDK key from device auth (`sdk_admin_*`). */
13
+ token: string;
14
+ projectId: string;
15
+ /** Admin API origin. Defaults to production (`https://shipeasy.ai`). */
16
+ adminBaseUrl?: string;
17
+ /** Called once per 401 so the host can clear its stored session and
18
+ * re-prompt login. The request still rejects with `AuthError`. */
19
+ onUnauthed?: () => void;
20
+ /** Injectable fetch for tests / exotic runtimes. Defaults to global fetch.
21
+ * Note the generated client invokes it with a single `Request` argument. */
22
+ fetch?: typeof fetch;
23
+ }
24
+ declare class DevtoolsClient {
25
+ private cache;
26
+ private readonly adminBaseUrl;
27
+ /** Raw admin bearer key for this session. Public so overlays can compare it
28
+ * against their stored session and rebuild the client on session swap. */
29
+ readonly token: string;
30
+ readonly projectId: string;
31
+ private readonly onUnauthed?;
32
+ private readonly fetchImpl;
33
+ /** Session-configured generated client — every spec-covered call goes
34
+ * through it (bearer auth, base URL, injectable fetch). */
35
+ private readonly api;
36
+ constructor(opts: DevtoolsClientOptions);
37
+ /** Admin origin this client talks to. Overlays build dashboard deep links
38
+ * ("Open in dashboard ↗") off it. */
39
+ get adminUrl(): string;
40
+ /** Run `fn` once per `key` and cache the resulting promise. Rejections are
41
+ * evicted so the next call retries instead of replaying the failure. */
42
+ private memo;
43
+ /** Drop all cached responses (pull-to-refresh). */
44
+ invalidate(): void;
45
+ /** Build + throw the error for a failed request. 401 produces an `AuthError`
46
+ * and fires `onUnauthed` so the host can drop the stale session instead of
47
+ * leaving the user staring at a failed-load message. */
48
+ private fail;
49
+ /** Resolve a generated op result to its body, or throw the normalized
50
+ * error. Write ops with empty bodies pass on any 2xx. */
51
+ private unwrap;
52
+ /** Some list endpoints return a bare array, others a `{ data }` envelope
53
+ * (the spec is the contract, but be tolerant like the overlays always
54
+ * were). */
55
+ private asList;
56
+ /**
57
+ * Drain a paginated `{ data, next_cursor }` list op by walking cursors.
58
+ * Devtools shows the entire list at once; without this, large projects would
59
+ * silently truncate at the first page (limit=100 default).
60
+ */
61
+ private drain;
62
+ private rawInit;
63
+ private rawGet;
64
+ private rawSend;
65
+ /** Read the response error body for diagnostics. Best-effort; never throws. */
66
+ private readErrorDetail;
67
+ project(): Promise<ProjectRecord>;
68
+ gates(): Promise<GateRecord[]>;
69
+ configs(): Promise<ConfigRecord[]>;
70
+ /** Experiments list. The endpoint returns the non-archived set (running /
71
+ * draft / stopped) by default; pass `{ archived: true }` for the archive tab
72
+ * (a separate memo bucket so the overlay's Archived section loads on demand). */
73
+ experiments(opts?: {
74
+ archived?: boolean;
75
+ }): Promise<ExperimentRecord[]>;
76
+ universes(): Promise<UniverseRecord[]>;
77
+ profiles(): Promise<ProfileRecord[]>;
78
+ drafts(): Promise<DraftRecord[]>;
79
+ keys(profileId?: string): Promise<KeyRecord[]>;
80
+ createDraft(input: {
81
+ profileId: string;
82
+ name: string;
83
+ }): Promise<DraftRecord>;
84
+ upsertDraftKey(draftId: string, key: string, value: string): Promise<void>;
85
+ updateKeyById(id: string, value: string): Promise<void>;
86
+ upsertKeys(profileId: string, keys: Array<{
87
+ key: string;
88
+ value: string;
89
+ }>, chunk?: string): Promise<void>;
90
+ /** Drop every cached `keys(profileId?)` response. We don't track which
91
+ * profile a single key write affects, so be safe and clear them all. */
92
+ private invalidateKeysCache;
93
+ bugs(): Promise<BugRecord[]>;
94
+ bug(id: string): Promise<BugDetail>;
95
+ /** Auto-filed error tickets. Same list endpoint, `type=error`. */
96
+ errors(): Promise<BugRecord[]>;
97
+ /** Auto-filed alert tickets. Same list endpoint, `type=alert`. */
98
+ alerts(): Promise<BugRecord[]>;
99
+ /** Generic ops-item detail — works for any type (bug/feature/error/alert). */
100
+ opsItem(id: string): Promise<OpsItemDetail>;
101
+ /** Triage-only update (status / priority) that works for ANY item type —
102
+ * the only editable shape for auto-filed error/alert tickets. Scrubs every
103
+ * list bucket + the item's detail so the panels refetch. */
104
+ updateOps(id: string, patch: {
105
+ status?: BugRecord["status"];
106
+ priority?: BugRecord["priority"];
107
+ }): Promise<void>;
108
+ createBug(input: Omit<CreateBugRequestInput, "type">): Promise<{
109
+ id: string;
110
+ }>;
111
+ updateBug(id: string, patch: {
112
+ title?: string;
113
+ stepsToReproduce?: string;
114
+ actualResult?: string;
115
+ expectedResult?: string;
116
+ status?: BugRecord["status"];
117
+ priority?: BugRecord["priority"];
118
+ }): Promise<void>;
119
+ featureRequests(): Promise<FeatureRequestRecord[]>;
120
+ featureRequest(id: string): Promise<FeatureRequestDetail>;
121
+ createFeatureRequest(input: Omit<CreateFeatureRequestRequestInput, "type">): Promise<{
122
+ id: string;
123
+ }>;
124
+ updateFeatureRequest(id: string, patch: {
125
+ title?: string;
126
+ description?: string;
127
+ useCase?: string;
128
+ status?: FeatureRequestRecord["status"];
129
+ priority?: FeatureRequestRecord["priority"];
130
+ }): Promise<void>;
131
+ /** Fetch an attachment file as a Blob via the authenticated stream route
132
+ * (an <Image>/<img> can't send the Authorization header itself). */
133
+ attachmentBlob(id: string): Promise<Blob>;
134
+ uploadAttachment(args: {
135
+ reportKind: "bug" | "feature_request";
136
+ reportId: string;
137
+ kind: "screenshot" | "recording" | "file";
138
+ filename: string;
139
+ blob: Blob;
140
+ }): Promise<AttachmentUploadResult>;
141
+ }
142
+
143
+ export { AuthError, DevtoolsClient, type DevtoolsClientOptions };
package/dist/api.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+ var _chunkYQHCUZGPjs = require('./chunk-YQHCUZGP.js');
5
+ require('./chunk-KPO3R7ND.js');
6
+ require('./chunk-I2KLQ2HA.js');
7
+
8
+
9
+
10
+ exports.AuthError = _chunkYQHCUZGPjs.AuthError; exports.DevtoolsClient = _chunkYQHCUZGPjs.DevtoolsClient;
package/dist/api.mjs ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ AuthError,
3
+ DevtoolsClient
4
+ } from "./chunk-LZDNBMH6.mjs";
5
+ import "./chunk-RH6X24HG.mjs";
6
+ import "./chunk-U5DDQYSF.mjs";
7
+ export {
8
+ AuthError,
9
+ DevtoolsClient
10
+ };
@@ -0,0 +1,57 @@
1
+ import { f as DevtoolsSession } from './types-D0sroUDD.mjs';
2
+
3
+ /** Thrown when the user dismisses the auth browser without completing login. */
4
+ declare class LoginCancelled extends Error {
5
+ constructor();
6
+ }
7
+ /** Result contract of an auth-session opener (mirrors expo-web-browser's
8
+ * `openAuthSessionAsync`): resolves when the browser deep-links back to
9
+ * `redirectUri` (type "success", `url` = the full deep link) or the user
10
+ * dismisses it. */
11
+ interface AuthSessionResult {
12
+ type: "success" | "cancel" | "dismiss" | string;
13
+ url?: string;
14
+ }
15
+ interface DeviceAuthAdapters {
16
+ /** Open `url` in an auth browser session and resolve when it redirects to
17
+ * `redirectUri` (or the user cancels). RN: `WebBrowser.openAuthSessionAsync`. */
18
+ openAuthSession(url: string, redirectUri: string): Promise<AuthSessionResult>;
19
+ /** base64url(SHA-256(input)) — the PKCE S256 transform. Defaults to
20
+ * `crypto.subtle` where available; RN injects expo-crypto. */
21
+ sha256Base64Url?(input: string): Promise<string>;
22
+ /** UUID source for the verifier. Defaults to `crypto.randomUUID`. */
23
+ randomUUID?(): string;
24
+ /** Injectable fetch for tests. Defaults to global fetch. */
25
+ fetch?: typeof fetch;
26
+ }
27
+ interface DeviceAuthOptions {
28
+ /** The HOST APP's deep-link redirect URI (its own scheme), e.g.
29
+ * `acme://se-auth`. Must match a scheme the app registered with the OS. */
30
+ redirectUri: string;
31
+ /** Edge worker origin (`/auth/device/*`). Defaults to production. */
32
+ edgeBaseUrl?: string;
33
+ /** Admin web origin (the `/cli-auth` page). Defaults to production. */
34
+ adminBaseUrl?: string;
35
+ /** Pre-select a project on the /cli-auth page (skips the picker when the
36
+ * user has access). */
37
+ projectId?: string;
38
+ /** The app's public client key (`sdk_client_*`). The /cli-auth page resolves
39
+ * it to the project it belongs to and locks the flow there — the key IS the
40
+ * project identity, so the user never sees a project picker (same contract
41
+ * as the browser overlay's `sdkKey` param). An explicit `projectId` wins. */
42
+ clientKey?: string;
43
+ }
44
+ /** Parse a deep link's query params without relying on the host's URL
45
+ * implementation (React Native's URL polyfill is historically incomplete). */
46
+ declare function parseDeepLinkQuery(url: string): Record<string, string>;
47
+ /**
48
+ * Run the full device-auth flow and return the session. Throws
49
+ * {@link LoginCancelled} if the user dismisses the browser, or `Error` on a
50
+ * protocol failure (state mismatch, exchange rejection).
51
+ *
52
+ * The caller persists the returned session (RN: SecureStore/Keychain) — this
53
+ * core deliberately does no storage of its own.
54
+ */
55
+ declare function startDeviceAuth(opts: DeviceAuthOptions, adapters: DeviceAuthAdapters): Promise<DevtoolsSession>;
56
+
57
+ export { type AuthSessionResult, type DeviceAuthAdapters, type DeviceAuthOptions, LoginCancelled, parseDeepLinkQuery, startDeviceAuth };
package/dist/auth.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { f as DevtoolsSession } from './types-D0sroUDD.js';
2
+
3
+ /** Thrown when the user dismisses the auth browser without completing login. */
4
+ declare class LoginCancelled extends Error {
5
+ constructor();
6
+ }
7
+ /** Result contract of an auth-session opener (mirrors expo-web-browser's
8
+ * `openAuthSessionAsync`): resolves when the browser deep-links back to
9
+ * `redirectUri` (type "success", `url` = the full deep link) or the user
10
+ * dismisses it. */
11
+ interface AuthSessionResult {
12
+ type: "success" | "cancel" | "dismiss" | string;
13
+ url?: string;
14
+ }
15
+ interface DeviceAuthAdapters {
16
+ /** Open `url` in an auth browser session and resolve when it redirects to
17
+ * `redirectUri` (or the user cancels). RN: `WebBrowser.openAuthSessionAsync`. */
18
+ openAuthSession(url: string, redirectUri: string): Promise<AuthSessionResult>;
19
+ /** base64url(SHA-256(input)) — the PKCE S256 transform. Defaults to
20
+ * `crypto.subtle` where available; RN injects expo-crypto. */
21
+ sha256Base64Url?(input: string): Promise<string>;
22
+ /** UUID source for the verifier. Defaults to `crypto.randomUUID`. */
23
+ randomUUID?(): string;
24
+ /** Injectable fetch for tests. Defaults to global fetch. */
25
+ fetch?: typeof fetch;
26
+ }
27
+ interface DeviceAuthOptions {
28
+ /** The HOST APP's deep-link redirect URI (its own scheme), e.g.
29
+ * `acme://se-auth`. Must match a scheme the app registered with the OS. */
30
+ redirectUri: string;
31
+ /** Edge worker origin (`/auth/device/*`). Defaults to production. */
32
+ edgeBaseUrl?: string;
33
+ /** Admin web origin (the `/cli-auth` page). Defaults to production. */
34
+ adminBaseUrl?: string;
35
+ /** Pre-select a project on the /cli-auth page (skips the picker when the
36
+ * user has access). */
37
+ projectId?: string;
38
+ /** The app's public client key (`sdk_client_*`). The /cli-auth page resolves
39
+ * it to the project it belongs to and locks the flow there — the key IS the
40
+ * project identity, so the user never sees a project picker (same contract
41
+ * as the browser overlay's `sdkKey` param). An explicit `projectId` wins. */
42
+ clientKey?: string;
43
+ }
44
+ /** Parse a deep link's query params without relying on the host's URL
45
+ * implementation (React Native's URL polyfill is historically incomplete). */
46
+ declare function parseDeepLinkQuery(url: string): Record<string, string>;
47
+ /**
48
+ * Run the full device-auth flow and return the session. Throws
49
+ * {@link LoginCancelled} if the user dismisses the browser, or `Error` on a
50
+ * protocol failure (state mismatch, exchange rejection).
51
+ *
52
+ * The caller persists the returned session (RN: SecureStore/Keychain) — this
53
+ * core deliberately does no storage of its own.
54
+ */
55
+ declare function startDeviceAuth(opts: DeviceAuthOptions, adapters: DeviceAuthAdapters): Promise<DevtoolsSession>;
56
+
57
+ export { type AuthSessionResult, type DeviceAuthAdapters, type DeviceAuthOptions, LoginCancelled, parseDeepLinkQuery, startDeviceAuth };
package/dist/auth.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+ var _chunkLCRTLHGLjs = require('./chunk-LCRTLHGL.js');
6
+ require('./chunk-KPO3R7ND.js');
7
+ require('./chunk-I2KLQ2HA.js');
8
+
9
+
10
+
11
+
12
+ exports.LoginCancelled = _chunkLCRTLHGLjs.LoginCancelled; exports.parseDeepLinkQuery = _chunkLCRTLHGLjs.parseDeepLinkQuery; exports.startDeviceAuth = _chunkLCRTLHGLjs.startDeviceAuth;
package/dist/auth.mjs ADDED
@@ -0,0 +1,12 @@
1
+ import {
2
+ LoginCancelled,
3
+ parseDeepLinkQuery,
4
+ startDeviceAuth
5
+ } from "./chunk-5VQLXGNR.mjs";
6
+ import "./chunk-RH6X24HG.mjs";
7
+ import "./chunk-U5DDQYSF.mjs";
8
+ export {
9
+ LoginCancelled,
10
+ parseDeepLinkQuery,
11
+ startDeviceAuth
12
+ };
@@ -0,0 +1,25 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/feedback-state.ts
2
+ function prMerged(pr) {
3
+ return pr.merged === true || pr.state === "merged" || typeof pr.mergedAt === "string";
4
+ }
5
+ function agentAwaitingReply(cd) {
6
+ const a = _optionalChain([cd, 'optionalAccess', _ => _.agent]);
7
+ return _optionalChain([a, 'optionalAccess', _2 => _2.awaitingReply]) === true || _optionalChain([a, 'optionalAccess', _3 => _3.state]) === "awaiting_reply";
8
+ }
9
+ function feedbackAgentInfo(item) {
10
+ const pr = _nullishCoalesce(_optionalChain([item, 'access', _4 => _4.connectorData, 'optionalAccess', _5 => _5.github, 'optionalAccess', _6 => _6.pr]), () => ( null));
11
+ if (pr && prMerged(pr)) return { state: "pr_landed", pr: { number: pr.number, url: pr.url } };
12
+ if (agentAwaitingReply(item.connectorData)) return { state: "question" };
13
+ if (pr) return { state: "pr_ready", pr: { number: pr.number, url: pr.url } };
14
+ return null;
15
+ }
16
+ var FEEDBACK_AGENT_LABEL = {
17
+ pr_landed: "merged",
18
+ question: "needs reply",
19
+ pr_ready: "PR ready"
20
+ };
21
+
22
+
23
+
24
+
25
+ exports.feedbackAgentInfo = feedbackAgentInfo; exports.FEEDBACK_AGENT_LABEL = FEEDBACK_AGENT_LABEL;
@@ -0,0 +1,101 @@
1
+ import {
2
+ DEFAULT_ADMIN_BASE_URL,
3
+ DEFAULT_EDGE_BASE_URL,
4
+ types_exports
5
+ } from "./chunk-RH6X24HG.mjs";
6
+
7
+ // src/auth.ts
8
+ var LoginCancelled = class extends Error {
9
+ constructor() {
10
+ super("Login cancelled");
11
+ this.name = "LoginCancelled";
12
+ }
13
+ };
14
+ function defaultRandomUUID() {
15
+ const c = globalThis.crypto;
16
+ if (c?.randomUUID) return c.randomUUID();
17
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
18
+ const r = Math.random() * 16 | 0;
19
+ return (ch === "x" ? r : r & 3 | 8).toString(16);
20
+ });
21
+ }
22
+ async function defaultSha256Base64Url(input) {
23
+ const subtle = globalThis.crypto?.subtle;
24
+ if (!subtle) {
25
+ throw new Error(
26
+ "No crypto.subtle available \u2014 pass adapters.sha256Base64Url (e.g. expo-crypto's digestStringAsync)."
27
+ );
28
+ }
29
+ const hash = await subtle.digest("SHA-256", new TextEncoder().encode(input));
30
+ const bytes = new Uint8Array(hash);
31
+ let binary = "";
32
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
33
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
34
+ }
35
+ function makeVerifier(randomUUID) {
36
+ return (randomUUID() + randomUUID()).replace(/-/g, "");
37
+ }
38
+ function parseDeepLinkQuery(url) {
39
+ const q = url.indexOf("?");
40
+ if (q < 0) return {};
41
+ const out = {};
42
+ const query = url.slice(q + 1).split("#")[0];
43
+ for (const pair of query.split("&")) {
44
+ if (!pair) continue;
45
+ const eq = pair.indexOf("=");
46
+ const k = eq < 0 ? pair : pair.slice(0, eq);
47
+ const v = eq < 0 ? "" : pair.slice(eq + 1);
48
+ try {
49
+ out[decodeURIComponent(k)] = decodeURIComponent(v.replace(/\+/g, " "));
50
+ } catch {
51
+ out[k] = v;
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ async function startDeviceAuth(opts, adapters) {
57
+ const edge = (opts.edgeBaseUrl ?? DEFAULT_EDGE_BASE_URL).replace(/\/$/, "");
58
+ const admin = (opts.adminBaseUrl ?? DEFAULT_ADMIN_BASE_URL).replace(/\/$/, "");
59
+ const fetchImpl = adapters.fetch ?? ((...args) => fetch(...args));
60
+ const randomUUID = adapters.randomUUID ?? defaultRandomUUID;
61
+ const digest = adapters.sha256Base64Url ?? defaultSha256Base64Url;
62
+ const verifier = makeVerifier(randomUUID);
63
+ const challenge = await digest(verifier);
64
+ const startRes = await fetchImpl(`${edge}/auth/device/start`, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify({ code_challenge: challenge })
68
+ });
69
+ if (!startRes.ok) {
70
+ throw new Error(`Login could not start (HTTP ${startRes.status}). Please try again.`);
71
+ }
72
+ const { state } = await startRes.json();
73
+ if (!state) throw new Error("Login could not start (no state). Please try again.");
74
+ const authUrl = `${admin}/cli-auth?state=${encodeURIComponent(state)}&code_challenge=${encodeURIComponent(challenge)}&redirect_uri=${encodeURIComponent(opts.redirectUri)}` + (opts.projectId ? `&project_id=${encodeURIComponent(opts.projectId)}` : "") + (!opts.projectId && opts.clientKey ? `&client_key=${encodeURIComponent(opts.clientKey)}` : "");
75
+ const result = await adapters.openAuthSession(authUrl, opts.redirectUri);
76
+ if (result.type !== "success" || !result.url) throw new LoginCancelled();
77
+ const params = parseDeepLinkQuery(result.url);
78
+ if (!params.state || params.state !== state) throw new Error("Auth state mismatch");
79
+ const tokenRes = await fetchImpl(`${edge}/auth/device/token`, {
80
+ method: "POST",
81
+ headers: { "Content-Type": "application/json" },
82
+ body: JSON.stringify({ state, code_verifier: verifier })
83
+ });
84
+ if (!tokenRes.ok) {
85
+ throw new Error(`Login did not complete (HTTP ${tokenRes.status}). Please try again.`);
86
+ }
87
+ const data = await tokenRes.json();
88
+ if (!data.token || !data.project_id) throw new Error("Login did not return a token");
89
+ return {
90
+ token: data.token,
91
+ projectId: data.project_id,
92
+ userEmail: data.user_email ?? null,
93
+ projectName: data.project_name ?? null
94
+ };
95
+ }
96
+
97
+ export {
98
+ LoginCancelled,
99
+ parseDeepLinkQuery,
100
+ startDeviceAuth
101
+ };