@tapthrough/expo 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +105 -2
- package/app.plugin.d.ts +37 -0
- package/app.plugin.js +79 -0
- package/dist/Annotator.d.ts +14 -0
- package/dist/Annotator.d.ts.map +1 -0
- package/dist/Annotator.js +160 -0
- package/dist/Annotator.js.map +1 -0
- package/dist/Bubble.d.ts +27 -0
- package/dist/Bubble.d.ts.map +1 -0
- package/dist/Bubble.js +234 -0
- package/dist/Bubble.js.map +1 -0
- package/dist/Feedback.d.ts +14 -0
- package/dist/Feedback.d.ts.map +1 -0
- package/dist/Feedback.js +977 -0
- package/dist/Feedback.js.map +1 -0
- package/dist/Overlay.d.ts +13 -0
- package/dist/Overlay.d.ts.map +1 -0
- package/dist/Overlay.js +44 -0
- package/dist/Overlay.js.map +1 -0
- package/dist/Popover.d.ts +98 -0
- package/dist/Popover.d.ts.map +1 -0
- package/dist/Popover.js +531 -0
- package/dist/Popover.js.map +1 -0
- package/dist/Tapthrough.d.ts +27 -0
- package/dist/Tapthrough.d.ts.map +1 -0
- package/dist/Tapthrough.js +111 -0
- package/dist/Tapthrough.js.map +1 -0
- package/dist/core/annotations.d.ts +63 -0
- package/dist/core/annotations.d.ts.map +1 -0
- package/dist/core/annotations.js +76 -0
- package/dist/core/annotations.js.map +1 -0
- package/dist/core/bubble.d.ts +21 -0
- package/dist/core/bubble.d.ts.map +1 -0
- package/dist/core/bubble.js +25 -0
- package/dist/core/bubble.js.map +1 -0
- package/dist/core/controller.d.ts +46 -0
- package/dist/core/controller.d.ts.map +1 -0
- package/dist/core/controller.js +103 -0
- package/dist/core/controller.js.map +1 -0
- package/dist/core/diagnostics.d.ts +47 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +101 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/feedback.d.ts +302 -0
- package/dist/core/feedback.d.ts.map +1 -0
- package/dist/core/feedback.js +345 -0
- package/dist/core/feedback.js.map +1 -0
- package/dist/core/flags.d.ts +13 -0
- package/dist/core/flags.d.ts.map +1 -0
- package/dist/core/flags.js +25 -0
- package/dist/core/flags.js.map +1 -0
- package/dist/core/ideas.d.ts +166 -0
- package/dist/core/ideas.d.ts.map +1 -0
- package/dist/core/ideas.js +159 -0
- package/dist/core/ideas.js.map +1 -0
- package/dist/core/launch.d.ts +43 -0
- package/dist/core/launch.d.ts.map +1 -0
- package/dist/core/launch.js +81 -0
- package/dist/core/launch.js.map +1 -0
- package/dist/core/reset.d.ts +17 -0
- package/dist/core/reset.d.ts.map +1 -0
- package/dist/core/reset.js +19 -0
- package/dist/core/reset.js.map +1 -0
- package/dist/core/review.d.ts +49 -0
- package/dist/core/review.d.ts.map +1 -0
- package/dist/core/review.js +92 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/url.d.ts +9 -0
- package/dist/core/url.d.ts.map +1 -0
- package/dist/core/url.js +23 -0
- package/dist/core/url.js.map +1 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +13 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/native-intent.d.ts +18 -0
- package/dist/native-intent.d.ts.map +1 -0
- package/dist/native-intent.js +32 -0
- package/dist/native-intent.js.map +1 -0
- package/dist/runtime.d.ts +24 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +17 -0
- package/dist/runtime.js.map +1 -0
- package/dist/screenshot.d.ts +7 -0
- package/dist/screenshot.d.ts.map +1 -0
- package/dist/screenshot.js +48 -0
- package/dist/screenshot.js.map +1 -0
- package/dist/supabase.d.ts +20 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +18 -0
- package/dist/supabase.js.map +1 -0
- package/dist/ui/native.d.ts +34 -0
- package/dist/ui/native.d.ts.map +1 -0
- package/dist/ui/native.js +76 -0
- package/dist/ui/native.js.map +1 -0
- package/dist/ui/parts.d.ts +62 -0
- package/dist/ui/parts.d.ts.map +1 -0
- package/dist/ui/parts.js +110 -0
- package/dist/ui/parts.js.map +1 -0
- package/dist/ui/theme.d.ts +90 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +61 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +82 -3
- package/src/Annotator.tsx +279 -0
- package/src/Bubble.tsx +331 -0
- package/src/Feedback.tsx +1739 -0
- package/src/Overlay.tsx +61 -0
- package/src/Popover.tsx +1393 -0
- package/src/Tapthrough.tsx +171 -0
- package/src/core/annotations.ts +116 -0
- package/src/core/bubble.ts +45 -0
- package/src/core/controller.ts +119 -0
- package/src/core/diagnostics.ts +128 -0
- package/src/core/feedback.ts +589 -0
- package/src/core/flags.ts +31 -0
- package/src/core/ideas.ts +337 -0
- package/src/core/launch.ts +119 -0
- package/src/core/reset.ts +28 -0
- package/src/core/review.ts +148 -0
- package/src/core/url.ts +21 -0
- package/src/hooks.ts +15 -0
- package/src/index.ts +8 -0
- package/src/native-intent.ts +36 -0
- package/src/runtime.ts +30 -0
- package/src/screenshot.ts +64 -0
- package/src/supabase.ts +27 -0
- package/src/ui/native.ts +116 -0
- package/src/ui/parts.tsx +249 -0
- package/src/ui/theme.ts +75 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const OFFLINE = "Couldn't reach Tapthrough. Check your connection and try again.";
|
|
2
|
+
function credential(caller, platform) {
|
|
3
|
+
return caller.kind === 'build'
|
|
4
|
+
? {
|
|
5
|
+
apiVersion: 1,
|
|
6
|
+
updateGroupId: caller.preview.updateGroupId,
|
|
7
|
+
projectId: caller.preview.projectId,
|
|
8
|
+
platform,
|
|
9
|
+
}
|
|
10
|
+
: { apiVersion: 1, appKey: caller.appKey, platform };
|
|
11
|
+
}
|
|
12
|
+
function query(caller, platform) {
|
|
13
|
+
return new URLSearchParams(caller.kind === 'build'
|
|
14
|
+
? {
|
|
15
|
+
updateGroupId: caller.preview.updateGroupId,
|
|
16
|
+
projectId: caller.preview.projectId,
|
|
17
|
+
platform,
|
|
18
|
+
}
|
|
19
|
+
: { appKey: caller.appKey, platform });
|
|
20
|
+
}
|
|
21
|
+
async function call(options, path, init, pick) {
|
|
22
|
+
const doFetch = options.fetch ?? fetch;
|
|
23
|
+
let response;
|
|
24
|
+
try {
|
|
25
|
+
response = await doFetch(`${options.apiUrl.replace(/\/$/, '')}${path}`, {
|
|
26
|
+
method: init.method,
|
|
27
|
+
...(init.method === 'POST'
|
|
28
|
+
? { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(init.body) }
|
|
29
|
+
: {}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { ok: false, message: OFFLINE };
|
|
34
|
+
}
|
|
35
|
+
const body = (await response.json().catch(() => undefined));
|
|
36
|
+
const value = response.ok && body ? pick(body) : undefined;
|
|
37
|
+
if (value !== undefined)
|
|
38
|
+
return { ok: true, ...value };
|
|
39
|
+
// A server from before ideas answers 404 without an error code.
|
|
40
|
+
if (response.status === 404 && !body?.error) {
|
|
41
|
+
return { ok: false, message: 'This version of Tapthrough doesn’t take ideas yet.' };
|
|
42
|
+
}
|
|
43
|
+
return { ok: false, message: body?.error?.message ?? `Tapthrough returned ${response.status}.` };
|
|
44
|
+
}
|
|
45
|
+
const pickIdea = (body) => body.idea && typeof body.idea === 'object' ? { idea: body.idea } : undefined;
|
|
46
|
+
/** Sends an idea; Claude starts writing it up in the repo's fix workflow. Never throws. */
|
|
47
|
+
export function submitIdea(input, options) {
|
|
48
|
+
const { draft } = input;
|
|
49
|
+
return call(options, '/api/v1/ideas', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: {
|
|
52
|
+
...credential(input.caller, input.platform),
|
|
53
|
+
body: draft.body,
|
|
54
|
+
...(draft.authorName ? { authorName: draft.authorName } : {}),
|
|
55
|
+
...(draft.authorEmail ? { authorEmail: draft.authorEmail } : {}),
|
|
56
|
+
...(draft.route ? { route: draft.route } : {}),
|
|
57
|
+
...(draft.device ? { device: draft.device } : {}),
|
|
58
|
+
...(draft.screenshot ? { screenshot: draft.screenshot } : {}),
|
|
59
|
+
},
|
|
60
|
+
}, pickIdea);
|
|
61
|
+
}
|
|
62
|
+
/** The app's recent ideas, newest first; an empty list when Tapthrough can't say. */
|
|
63
|
+
export async function fetchIdeas(caller, platform, options) {
|
|
64
|
+
const path = `/api/v1/ideas?${query(caller, platform)}`;
|
|
65
|
+
const result = await call(options, path, { method: 'GET' }, (body) => Array.isArray(body.ideas) ? { ideas: body.ideas } : undefined);
|
|
66
|
+
return result.ok ? result.ideas : [];
|
|
67
|
+
}
|
|
68
|
+
/** The repo's open PRs with previews; null when Tapthrough can't say. */
|
|
69
|
+
export async function fetchOpenPrs(preview, platform, options) {
|
|
70
|
+
const path = `/api/v1/pulls?${query({ kind: 'build', preview }, platform)}`;
|
|
71
|
+
const result = await call(options, path, { method: 'GET' }, (body) => Array.isArray(body.pulls) ? { pulls: body.pulls } : undefined);
|
|
72
|
+
return result.ok ? result.pulls : null;
|
|
73
|
+
}
|
|
74
|
+
/** Answers Claude's questions; it writes the idea up again with them. Never throws. */
|
|
75
|
+
export function answerIdea(input, options) {
|
|
76
|
+
return call(options, `/api/v1/ideas/${encodeURIComponent(input.id)}/answers`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: {
|
|
79
|
+
...credential(input.caller, input.platform),
|
|
80
|
+
answers: input.answers,
|
|
81
|
+
...(input.by ? { by: input.by } : {}),
|
|
82
|
+
},
|
|
83
|
+
}, pickIdea);
|
|
84
|
+
}
|
|
85
|
+
/** Build It, Retry or Approve on one idea. Never throws. */
|
|
86
|
+
export function ideaAction(input, options) {
|
|
87
|
+
return call(options, `/api/v1/ideas/${encodeURIComponent(input.id)}/${input.action}`, {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
body: { ...credential(input.caller, input.platform), ...(input.by ? { by: input.by } : {}) },
|
|
90
|
+
}, (body) => {
|
|
91
|
+
const picked = pickIdea(body);
|
|
92
|
+
if (!picked)
|
|
93
|
+
return undefined;
|
|
94
|
+
const result = body.result;
|
|
95
|
+
return {
|
|
96
|
+
...picked,
|
|
97
|
+
approve: result === 'review' || result === 'auto_merge' || result === 'merged'
|
|
98
|
+
? result
|
|
99
|
+
: null,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Ideas waiting on a person: questions to answer, or a build to test. */
|
|
104
|
+
export function ideasNeedingYou(ideas) {
|
|
105
|
+
return ideas.filter((idea) => idea.status === 'needs_answers' || idea.status === 'ready_to_test')
|
|
106
|
+
.length;
|
|
107
|
+
}
|
|
108
|
+
/** What Approve did, in plain words. */
|
|
109
|
+
export function approveMessage(outcome) {
|
|
110
|
+
if (outcome === 'merged')
|
|
111
|
+
return 'Approved and merged.';
|
|
112
|
+
if (outcome === 'auto_merge')
|
|
113
|
+
return 'Approved. It merges once its checks pass.';
|
|
114
|
+
return 'Approved. An engineer reviews and merges it.';
|
|
115
|
+
}
|
|
116
|
+
/** Grooming, building or waiting on a preview: worth refreshing often while someone watches. */
|
|
117
|
+
export function isMoving(idea) {
|
|
118
|
+
return idea.status === 'grooming' || idea.status === 'building' || idea.status === 'built';
|
|
119
|
+
}
|
|
120
|
+
/** "2:41 PM" today, "Nov 10" before, for a timeline step. */
|
|
121
|
+
export function stepTime(iso, now = new Date()) {
|
|
122
|
+
if (!iso)
|
|
123
|
+
return null;
|
|
124
|
+
const date = new Date(iso);
|
|
125
|
+
if (Number.isNaN(date.getTime()))
|
|
126
|
+
return null;
|
|
127
|
+
const sameDay = date.toDateString() === now.toDateString();
|
|
128
|
+
return sameDay
|
|
129
|
+
? date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' })
|
|
130
|
+
: date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Files a report from a staging or TestFlight build. There's no PR to comment
|
|
134
|
+
* on, so Tapthrough opens a GitHub issue. Never throws.
|
|
135
|
+
*/
|
|
136
|
+
export function sendStaffReport(input, options) {
|
|
137
|
+
const { report } = input;
|
|
138
|
+
return call(options, '/api/v1/reports', {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
body: {
|
|
141
|
+
apiVersion: 1,
|
|
142
|
+
appKey: input.appKey,
|
|
143
|
+
platform: input.platform,
|
|
144
|
+
body: report.body,
|
|
145
|
+
...(report.reporterName ? { reporterName: report.reporterName } : {}),
|
|
146
|
+
...(report.route ? { route: report.route } : {}),
|
|
147
|
+
...(report.device ? { device: report.device } : {}),
|
|
148
|
+
...(report.app ? { app: report.app } : {}),
|
|
149
|
+
...(report.screenshot ? { screenshot: report.screenshot } : {}),
|
|
150
|
+
...(report.screenshot && report.annotations?.length
|
|
151
|
+
? { annotations: report.annotations }
|
|
152
|
+
: {}),
|
|
153
|
+
...(report.diagnostics ? { diagnostics: report.diagnostics } : {}),
|
|
154
|
+
},
|
|
155
|
+
}, (body) => typeof body.issueNumber === 'number' && typeof body.issueUrl === 'string'
|
|
156
|
+
? { issueNumber: body.issueNumber, issueUrl: body.issueUrl }
|
|
157
|
+
: undefined);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=ideas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ideas.js","sourceRoot":"","sources":["../../src/core/ideas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Flags } from '@tapthrough/protocol';
|
|
2
|
+
/** Logs the app in with the session the seed hook created. */
|
|
3
|
+
export interface AuthAdapter {
|
|
4
|
+
signIn(session: Record<string, unknown>): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/** Clears one piece of local state, e.g. a query cache or a persisted store. */
|
|
7
|
+
export type ResetTarget = () => void | Promise<void>;
|
|
8
|
+
export interface LaunchDeps {
|
|
9
|
+
seedHookUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* The app's own IDs (iOS bundle ID, Android package). A token for any other
|
|
12
|
+
* app is refused before anything changes, so a stray link can't swap accounts.
|
|
13
|
+
*/
|
|
14
|
+
expectedAudiences?: string[];
|
|
15
|
+
auth: AuthAdapter;
|
|
16
|
+
reset?: ResetTarget[];
|
|
17
|
+
applyFlags?: (flags: Flags) => void;
|
|
18
|
+
navigate: (path: string) => void | Promise<void>;
|
|
19
|
+
fetch?: typeof fetch;
|
|
20
|
+
/** Called as each part of the launch starts, so a UI can show progress. */
|
|
21
|
+
onStage?: (stage: LaunchStage) => void;
|
|
22
|
+
}
|
|
23
|
+
/** The parts of a launch, in order. */
|
|
24
|
+
export type LaunchStage = 'seeding' | 'resetting' | 'signing_in' | 'opening';
|
|
25
|
+
export interface LaunchError {
|
|
26
|
+
code: string;
|
|
27
|
+
/** Shown to the reviewer as is. */
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
export type LaunchResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
path: string;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
error: LaunchError;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The launch sequence for one link token: ask the seed hook for a session,
|
|
39
|
+
* clear local state, log in, apply flags, then open the scenario's screen.
|
|
40
|
+
* Never throws; every failure comes back as a reviewer-facing message.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runLaunch(token: string, deps: LaunchDeps): Promise<LaunchResult>;
|
|
43
|
+
//# sourceMappingURL=launch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/core/launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,KAAK,EAAkC,MAAM,sBAAsB,CAAA;AAGhG,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACxD;AAED,gFAAgF;AAChF,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAEpD,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACnC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;CACvC;AAED,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAA;AAE5E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAA;AAOzF;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAkEtF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ErrorResponse, resolveRoute, SeedHookResponse } from '@tapthrough/protocol';
|
|
2
|
+
import { decodeTokenClaims } from './feedback.js';
|
|
3
|
+
const fail = (code, message) => ({
|
|
4
|
+
ok: false,
|
|
5
|
+
error: { code, message },
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* The launch sequence for one link token: ask the seed hook for a session,
|
|
9
|
+
* clear local state, log in, apply flags, then open the scenario's screen.
|
|
10
|
+
* Never throws; every failure comes back as a reviewer-facing message.
|
|
11
|
+
*/
|
|
12
|
+
export async function runLaunch(token, deps) {
|
|
13
|
+
const doFetch = deps.fetch ?? fetch;
|
|
14
|
+
if (deps.expectedAudiences?.length) {
|
|
15
|
+
const aud = decodeTokenClaims(token)?.aud;
|
|
16
|
+
const audiences = Array.isArray(aud) ? aud : [aud];
|
|
17
|
+
if (!audiences.some((a) => typeof a === 'string' && deps.expectedAudiences?.includes(a))) {
|
|
18
|
+
return fail('wrong_app', 'This link is for a different app.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
deps.onStage?.('seeding');
|
|
22
|
+
let response;
|
|
23
|
+
try {
|
|
24
|
+
response = await doFetch(deps.seedHookUrl, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({ token }),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return fail('network', "Couldn't reach the seed hook. Check your connection and try again.");
|
|
32
|
+
}
|
|
33
|
+
const body = await response.json().catch(() => undefined);
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const error = ErrorResponse.safeParse(body);
|
|
36
|
+
if (error.success)
|
|
37
|
+
return { ok: false, error: error.data.error };
|
|
38
|
+
return fail('seed_hook_failed', `The seed hook returned ${response.status}.`);
|
|
39
|
+
}
|
|
40
|
+
const seeded = SeedHookResponse.safeParse(body);
|
|
41
|
+
if (!seeded.success) {
|
|
42
|
+
return fail('bad_response', 'The seed hook returned a response this app version cannot read.');
|
|
43
|
+
}
|
|
44
|
+
// Resolve before changing anything, so a bad route leaves the app untouched.
|
|
45
|
+
let path;
|
|
46
|
+
try {
|
|
47
|
+
path = resolveRoute(seeded.data.route, seeded.data.refs);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return fail('bad_route', error instanceof Error ? error.message : 'Could not resolve the route');
|
|
51
|
+
}
|
|
52
|
+
// Reset before signing in, so clearing persisted keys can't remove the new session.
|
|
53
|
+
deps.onStage?.('resetting');
|
|
54
|
+
try {
|
|
55
|
+
for (const target of deps.reset ?? [])
|
|
56
|
+
await target();
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return fail('reset_failed', `Couldn't clear local data: ${messageOf(error)}`);
|
|
60
|
+
}
|
|
61
|
+
deps.onStage?.('signing_in');
|
|
62
|
+
try {
|
|
63
|
+
await deps.auth.signIn(seeded.data.session);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return fail('sign_in_failed', `Couldn't sign in as the test user: ${messageOf(error)}`);
|
|
67
|
+
}
|
|
68
|
+
deps.onStage?.('opening');
|
|
69
|
+
deps.applyFlags?.(seeded.data.flags);
|
|
70
|
+
try {
|
|
71
|
+
await deps.navigate(path);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return fail('navigation_failed', `Couldn't open ${path}: ${messageOf(error)}`);
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, path };
|
|
77
|
+
}
|
|
78
|
+
function messageOf(error) {
|
|
79
|
+
return error instanceof Error ? error.message : String(error);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/core/launch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResetTarget } from './launch.js';
|
|
2
|
+
/** Clears a TanStack Query cache so the test user never sees the previous user's data. */
|
|
3
|
+
export declare function resetQueryClient(queryClient: {
|
|
4
|
+
clear(): void;
|
|
5
|
+
}): ResetTarget;
|
|
6
|
+
/** Removes persisted keys from AsyncStorage, MMKV or anything with `removeItem`. */
|
|
7
|
+
export declare function resetStorage(storage: {
|
|
8
|
+
removeItem(key: string): void | Promise<void>;
|
|
9
|
+
}, keys: string[]): ResetTarget;
|
|
10
|
+
interface ResettableStore {
|
|
11
|
+
getInitialState(): unknown;
|
|
12
|
+
setState(state: never, replace: true): void;
|
|
13
|
+
}
|
|
14
|
+
/** Puts Zustand stores back to their initial state. */
|
|
15
|
+
export declare function resetStores(stores: ResettableStore[]): ResetTarget;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=reset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../src/core/reset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,WAAW,EAAE;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,GAAG,WAAW,CAE5E;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAC1B,OAAO,EAAE;IAAE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,EAC1D,IAAI,EAAE,MAAM,EAAE,GACb,WAAW,CAIb;AAED,UAAU,eAAe;IACvB,eAAe,IAAI,OAAO,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,IAAI,CAAA;CAC5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,WAAW,CAIlE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Clears a TanStack Query cache so the test user never sees the previous user's data. */
|
|
2
|
+
export function resetQueryClient(queryClient) {
|
|
3
|
+
return () => queryClient.clear();
|
|
4
|
+
}
|
|
5
|
+
/** Removes persisted keys from AsyncStorage, MMKV or anything with `removeItem`. */
|
|
6
|
+
export function resetStorage(storage, keys) {
|
|
7
|
+
return async () => {
|
|
8
|
+
for (const key of keys)
|
|
9
|
+
await storage.removeItem(key);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Puts Zustand stores back to their initial state. */
|
|
13
|
+
export function resetStores(stores) {
|
|
14
|
+
return () => {
|
|
15
|
+
for (const store of stores)
|
|
16
|
+
store.setState(store.getInitialState(), true);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=reset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.js","sourceRoot":"","sources":["../../src/core/reset.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,WAA8B;IAC7D,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;AAClC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAC1B,OAA0D,EAC1D,IAAc;IAEd,OAAO,KAAK,IAAI,EAAE;QAChB,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IACvD,CAAC,CAAA;AACH,CAAC;AAOD,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,MAAyB;IACnD,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAW,EAAE,IAAI,CAAC,CAAA;IACpF,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Bounds, type Point } from './bubble.js';
|
|
2
|
+
import type { Checklist, ChecklistScenario, LibraryEntry, SetupLine, StepResultValue } from './feedback.js';
|
|
3
|
+
import type { LaunchStage } from './launch.js';
|
|
4
|
+
/** Step results keyed `scenario:index`, with the reviewer's own taps applied on top. */
|
|
5
|
+
export type Results = Record<string, StepResultValue | undefined>;
|
|
6
|
+
export declare const stepId: (scenarioKey: string, index: number) => string;
|
|
7
|
+
/** The results the server already has for each step. */
|
|
8
|
+
export declare function resultsFrom(checklist: Checklist | null): Results;
|
|
9
|
+
export interface ScenarioProgress {
|
|
10
|
+
total: number;
|
|
11
|
+
/** Steps with any result, skips included. */
|
|
12
|
+
checked: number;
|
|
13
|
+
passed: number;
|
|
14
|
+
failed: number;
|
|
15
|
+
state: 'new' | 'partial' | 'passed' | 'failed';
|
|
16
|
+
}
|
|
17
|
+
export declare function scenarioProgress(scenario: ChecklistScenario, results: Results): ScenarioProgress;
|
|
18
|
+
/**
|
|
19
|
+
* Position in `scenario.steps` of the first step without a result, starting at
|
|
20
|
+
* `from` and wrapping round. -1 when every step has one.
|
|
21
|
+
*/
|
|
22
|
+
export declare function nextOpenStep(scenario: ChecklistScenario, results: Results, from?: number): number;
|
|
23
|
+
/** The next scenario with a step left, after `afterKey` and wrapping round. */
|
|
24
|
+
export declare function nextOpenScenario(checklist: Checklist, results: Results, afterKey?: string): ChecklistScenario | undefined;
|
|
25
|
+
/** Scenarios in the run that aren't in the PR's plan, for "Other states". */
|
|
26
|
+
export declare function otherStates(checklist: Checklist): LibraryEntry[];
|
|
27
|
+
export interface Placement {
|
|
28
|
+
side: 'left' | 'right';
|
|
29
|
+
/** Distance from the screen's `side` edge. */
|
|
30
|
+
inset: number;
|
|
31
|
+
width: number;
|
|
32
|
+
vertical: 'above' | 'below';
|
|
33
|
+
/** Distance from the screen's bottom (above the button) or top (below it). */
|
|
34
|
+
offset: number;
|
|
35
|
+
maxHeight: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Where the popover goes for a button resting at `anchor`: on the button's
|
|
39
|
+
* side of the screen, and above or below it, whichever has more room.
|
|
40
|
+
*/
|
|
41
|
+
export declare function popoverPlacement(anchor: Point, bounds: Bounds, preferred?: {
|
|
42
|
+
width: number;
|
|
43
|
+
maxHeight: number;
|
|
44
|
+
}, size?: number, gap?: number): Placement;
|
|
45
|
+
export type SetupProgress = 'requesting' | LaunchStage | 'done';
|
|
46
|
+
export type LineState = 'waiting' | 'active' | 'done';
|
|
47
|
+
/** How far along each setup line is, for ticking them off while a launch runs. */
|
|
48
|
+
export declare function setupLineStates(lines: SetupLine[], progress: SetupProgress): LineState[];
|
|
49
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAA4B,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAC/E,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,eAAe,EAChB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,wFAAwF;AACxF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC,CAAA;AAEjE,eAAO,MAAM,MAAM,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,WAA8B,CAAA;AAEvF,wDAAwD;AACxD,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAOhE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;CAC/C;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAShG;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,SAAI,GAAG,MAAM,CAQ5F;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,iBAAiB,GAAG,SAAS,CAQ/B;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,EAAE,CAGhE;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;IAC3B,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,EACb,MAAM,EAAE,MAAM,EACd,SAAS,GAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAmC,EAChF,IAAI,SAAc,EAClB,GAAG,SAAK,GACP,SAAS,CAeX;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,MAAM,CAAA;AAC/D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAerD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,CAYxF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BUBBLE_SIZE, EDGE_MARGIN } from './bubble.js';
|
|
2
|
+
export const stepId = (scenarioKey, index) => `${scenarioKey}:${index}`;
|
|
3
|
+
/** The results the server already has for each step. */
|
|
4
|
+
export function resultsFrom(checklist) {
|
|
5
|
+
if (!checklist)
|
|
6
|
+
return {};
|
|
7
|
+
return Object.fromEntries(checklist.scenarios.flatMap((scenario) => scenario.steps.map((step) => [stepId(scenario.key, step.index), step.result?.result])));
|
|
8
|
+
}
|
|
9
|
+
export function scenarioProgress(scenario, results) {
|
|
10
|
+
const values = scenario.steps.map((step) => results[stepId(scenario.key, step.index)]);
|
|
11
|
+
const checked = values.filter(Boolean).length;
|
|
12
|
+
const passed = values.filter((v) => v === 'pass').length;
|
|
13
|
+
const failed = values.filter((v) => v === 'fail').length;
|
|
14
|
+
const total = values.length;
|
|
15
|
+
const state = checked === 0 ? 'new' : checked < total ? 'partial' : failed > 0 ? 'failed' : 'passed';
|
|
16
|
+
return { total, checked, passed, failed, state };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Position in `scenario.steps` of the first step without a result, starting at
|
|
20
|
+
* `from` and wrapping round. -1 when every step has one.
|
|
21
|
+
*/
|
|
22
|
+
export function nextOpenStep(scenario, results, from = 0) {
|
|
23
|
+
const n = scenario.steps.length;
|
|
24
|
+
for (let k = 0; k < n; k++) {
|
|
25
|
+
const i = (((from + k) % n) + n) % n;
|
|
26
|
+
const step = scenario.steps[i];
|
|
27
|
+
if (step && !results[stepId(scenario.key, step.index)])
|
|
28
|
+
return i;
|
|
29
|
+
}
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
/** The next scenario with a step left, after `afterKey` and wrapping round. */
|
|
33
|
+
export function nextOpenScenario(checklist, results, afterKey) {
|
|
34
|
+
const list = checklist.scenarios;
|
|
35
|
+
const start = afterKey ? list.findIndex((s) => s.key === afterKey) + 1 : 0;
|
|
36
|
+
for (let k = 0; k < list.length; k++) {
|
|
37
|
+
const scenario = list[(start + k) % list.length];
|
|
38
|
+
if (scenario && nextOpenStep(scenario, results) >= 0)
|
|
39
|
+
return scenario;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
/** Scenarios in the run that aren't in the PR's plan, for "Other states". */
|
|
44
|
+
export function otherStates(checklist) {
|
|
45
|
+
const planned = new Set(checklist.scenarios.map((s) => s.key));
|
|
46
|
+
return (checklist.library ?? []).filter((entry) => !entry.picked && !planned.has(entry.key));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Where the popover goes for a button resting at `anchor`: on the button's
|
|
50
|
+
* side of the screen, and above or below it, whichever has more room.
|
|
51
|
+
*/
|
|
52
|
+
export function popoverPlacement(anchor, bounds, preferred = { width: 320, maxHeight: 560 }, size = BUBBLE_SIZE, gap = 10) {
|
|
53
|
+
const side = anchor.x + size / 2 < bounds.width / 2 ? 'left' : 'right';
|
|
54
|
+
const width = Math.min(preferred.width, bounds.width - EDGE_MARGIN * 2);
|
|
55
|
+
const above = anchor.y - gap - bounds.insetTop;
|
|
56
|
+
const below = bounds.height - bounds.insetBottom - (anchor.y + size + gap);
|
|
57
|
+
const vertical = above >= below ? 'above' : 'below';
|
|
58
|
+
const room = Math.max(vertical === 'above' ? above : below, 0);
|
|
59
|
+
return {
|
|
60
|
+
side,
|
|
61
|
+
inset: EDGE_MARGIN,
|
|
62
|
+
width,
|
|
63
|
+
vertical,
|
|
64
|
+
offset: vertical === 'above' ? bounds.height - anchor.y + gap : anchor.y + size + gap,
|
|
65
|
+
maxHeight: Math.min(preferred.maxHeight, room),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const RANK = {
|
|
69
|
+
requesting: 0,
|
|
70
|
+
seeding: 1,
|
|
71
|
+
resetting: 2,
|
|
72
|
+
signing_in: 3,
|
|
73
|
+
opening: 4,
|
|
74
|
+
done: 5,
|
|
75
|
+
};
|
|
76
|
+
// The seed hook makes the account and data; sign-in uses the account; flags and
|
|
77
|
+
// navigation come last. Each line is done once the launch is past its part.
|
|
78
|
+
const ACTIVE_AT = { data: 1, account: 3, flag: 4, screen: 4 };
|
|
79
|
+
const DONE_AT = { data: 2, account: 4, flag: 4, screen: 5 };
|
|
80
|
+
/** How far along each setup line is, for ticking them off while a launch runs. */
|
|
81
|
+
export function setupLineStates(lines, progress) {
|
|
82
|
+
const rank = RANK[progress];
|
|
83
|
+
const states = lines.map((line) => rank >= DONE_AT[line.kind] ? 'done' : rank >= ACTIVE_AT[line.kind] ? 'active' : 'waiting');
|
|
84
|
+
// Something is always visibly happening until the end.
|
|
85
|
+
if (!states.includes('active')) {
|
|
86
|
+
const first = states.indexOf('waiting');
|
|
87
|
+
if (first >= 0)
|
|
88
|
+
states[first] = 'active';
|
|
89
|
+
}
|
|
90
|
+
return states;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Path the preview link opens: `<scheme>://tapthrough/launch?t=<token>`, or the same path on a universal link. */
|
|
2
|
+
export declare const LAUNCH_PATH = "tapthrough/launch";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the link token from a launch URL or path, or null for any other URL.
|
|
5
|
+
* Accepts `myapp://tapthrough/launch?t=…`, `https://host/tapthrough/launch?t=…`
|
|
6
|
+
* and the bare path Expo Router passes to `+native-intent`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseLaunchUrl(url: string | null | undefined): string | null;
|
|
9
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/core/url.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,eAAO,MAAM,WAAW,sBAAsB,CAAA;AAK9C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAS5E"}
|
package/dist/core/url.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Path the preview link opens: `<scheme>://tapthrough/launch?t=<token>`, or the same path on a universal link. */
|
|
2
|
+
export const LAUNCH_PATH = 'tapthrough/launch';
|
|
3
|
+
// Parsed by hand: React Native's URL polyfill doesn't handle custom schemes everywhere.
|
|
4
|
+
const LAUNCH_URL = /(?:^|\/)tapthrough\/launch\/?\?(?:[^#]*&)?t=([^&#]+)/;
|
|
5
|
+
/**
|
|
6
|
+
* Returns the link token from a launch URL or path, or null for any other URL.
|
|
7
|
+
* Accepts `myapp://tapthrough/launch?t=…`, `https://host/tapthrough/launch?t=…`
|
|
8
|
+
* and the bare path Expo Router passes to `+native-intent`.
|
|
9
|
+
*/
|
|
10
|
+
export function parseLaunchUrl(url) {
|
|
11
|
+
if (!url)
|
|
12
|
+
return null;
|
|
13
|
+
const match = LAUNCH_URL.exec(url);
|
|
14
|
+
if (!match?.[1])
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
return decodeURIComponent(match[1]);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/core/url.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAA;AAE9C,wFAAwF;AACxF,MAAM,UAAU,GAAG,sDAAsD,CAAA;AAEzE;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAA8B;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5B,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LaunchState } from './core/controller.js';
|
|
2
|
+
/** The scenario's value for a flag, or undefined when the scenario doesn't set it. */
|
|
3
|
+
export declare function useFlagOverride(name: string): boolean | string | undefined;
|
|
4
|
+
/** Where the current launch is: idle, launching, done or error. */
|
|
5
|
+
export declare function useLaunchState(): LaunchState;
|
|
6
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAIvD,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED,mEAAmE;AACnE,wBAAgB,cAAc,IAAI,WAAW,CAE5C"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
2
|
+
import { getFlagOverride, subscribeToFlagOverrides } from './core/flags.js';
|
|
3
|
+
import { controller } from './runtime.js';
|
|
4
|
+
/** The scenario's value for a flag, or undefined when the scenario doesn't set it. */
|
|
5
|
+
export function useFlagOverride(name) {
|
|
6
|
+
const get = useCallback(() => getFlagOverride(name), [name]);
|
|
7
|
+
return useSyncExternalStore(subscribeToFlagOverrides, get, get);
|
|
8
|
+
}
|
|
9
|
+
/** Where the current launch is: idle, launching, done or error. */
|
|
10
|
+
export function useLaunchState() {
|
|
11
|
+
return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEzD,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5D,OAAO,oBAAoB,CAAC,wBAAwB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;AACjE,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc;IAC5B,OAAO,oBAAoB,CAAC,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;AAC7F,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { LaunchState } from './core/controller.js';
|
|
2
|
+
export { getFlagOverride, getFlagOverrides } from './core/flags.js';
|
|
3
|
+
export type { AuthAdapter, LaunchError, ResetTarget } from './core/launch.js';
|
|
4
|
+
export { resetQueryClient, resetStorage, resetStores } from './core/reset.js';
|
|
5
|
+
export { LAUNCH_PATH, parseLaunchUrl } from './core/url.js';
|
|
6
|
+
export { useFlagOverride, useLaunchState } from './hooks.js';
|
|
7
|
+
export { handleLaunchPath, redirectSystemPath } from './native-intent.js';
|
|
8
|
+
export { Tapthrough, type TapthroughProps } from './Tapthrough.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACnE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getFlagOverride, getFlagOverrides } from './core/flags.js';
|
|
2
|
+
export { resetQueryClient, resetStorage, resetStores } from './core/reset.js';
|
|
3
|
+
export { LAUNCH_PATH, parseLaunchUrl } from './core/url.js';
|
|
4
|
+
export { useFlagOverride, useLaunchState } from './hooks.js';
|
|
5
|
+
export { handleLaunchPath, redirectSystemPath } from './native-intent.js';
|
|
6
|
+
export { Tapthrough } from './Tapthrough.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAA"}
|