@fixback/expo 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 +118 -0
- package/dist/FeedbackModal.d.ts +23 -0
- package/dist/FeedbackModal.js +201 -0
- package/dist/FixbackProvider.d.ts +35 -0
- package/dist/FixbackProvider.js +102 -0
- package/dist/adapters.d.ts +26 -0
- package/dist/adapters.js +143 -0
- package/dist/auto-report-backoff.d.ts +42 -0
- package/dist/auto-report-backoff.js +67 -0
- package/dist/boot.d.ts +65 -0
- package/dist/boot.js +60 -0
- package/dist/breadcrumbs.d.ts +276 -0
- package/dist/breadcrumbs.js +731 -0
- package/dist/client.d.ts +174 -0
- package/dist/client.js +351 -0
- package/dist/error-capture.d.ts +168 -0
- package/dist/error-capture.js +395 -0
- package/dist/http.d.ts +40 -0
- package/dist/http.js +20 -0
- package/dist/identity.d.ts +25 -0
- package/dist/identity.js +46 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +10 -0
- package/dist/report.d.ts +93 -0
- package/dist/report.js +67 -0
- package/dist/scrub.d.ts +55 -0
- package/dist/scrub.js +184 -0
- package/dist/shake.d.ts +51 -0
- package/dist/shake.js +79 -0
- package/dist/submit.d.ts +98 -0
- package/dist/submit.js +154 -0
- package/dist/tokens.d.ts +41 -0
- package/dist/tokens.js +46 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.js +13 -0
- package/package.json +63 -0
package/dist/submit.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The multipart feedback submission — the transport half of the capture loop,
|
|
3
|
+
* kept file-parallel with `packages/sdk/src/submit.ts` (ADR-0021).
|
|
4
|
+
*
|
|
5
|
+
* `POST /api/ingest/feedback` is a multipart request: a single text part
|
|
6
|
+
* `payload` carrying the JSON (publishable key + identity evidence + content)
|
|
7
|
+
* and an optional binary part `screenshot`. Two mobile differences:
|
|
8
|
+
*
|
|
9
|
+
* - a native HTTP stack attaches no `Origin` header, so the configured origin
|
|
10
|
+
* is sent explicitly (the server exact-matches it against the allowlist);
|
|
11
|
+
* - the screenshot is a `{ uri, name, type }` file reference (React Native's
|
|
12
|
+
* `FormData` file-part shape), not a `Blob`.
|
|
13
|
+
*
|
|
14
|
+
* The content-type header is deliberately not set, so the runtime writes the
|
|
15
|
+
* `multipart/form-data` boundary itself. Like `requestBoot`, this never throws:
|
|
16
|
+
* an unreachable Fixback or a refusal is a quiet result, so a Fixback problem
|
|
17
|
+
* never breaks the host app. Automatic reports (`source: auto`) honour ingest's
|
|
18
|
+
* `429` + `Retry-After` backpressure; manual reports are never gated.
|
|
19
|
+
*/
|
|
20
|
+
import { AutoReportBackoff } from "./auto-report-backoff";
|
|
21
|
+
import type { IdentityInputs, ReporterTier } from "./boot";
|
|
22
|
+
import { type FetchLike, type FormDataFactory } from "./http";
|
|
23
|
+
import type { ReportContent } from "./report";
|
|
24
|
+
/** What ingest returns for an accepted submission (vendored server shape). */
|
|
25
|
+
export interface RecordedFeedback {
|
|
26
|
+
readonly feedbackId: string;
|
|
27
|
+
readonly issueId: string;
|
|
28
|
+
readonly reporterId: string;
|
|
29
|
+
readonly tier: ReporterTier;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The captured screenshot to attach, if any — a React Native file reference.
|
|
33
|
+
* The server accepts png/jpeg/webp/gif up to 10 MiB; `react-native-view-shot`
|
|
34
|
+
* produces a png tmpfile by default.
|
|
35
|
+
*/
|
|
36
|
+
export interface ScreenshotFile {
|
|
37
|
+
readonly uri: string;
|
|
38
|
+
readonly name?: string;
|
|
39
|
+
readonly type?: string;
|
|
40
|
+
}
|
|
41
|
+
/** Everything a single feedback submission carries. */
|
|
42
|
+
export interface SubmitInput {
|
|
43
|
+
readonly key: string;
|
|
44
|
+
/** The configured origin, sent as the `Origin` header (spec 0004 §A). */
|
|
45
|
+
readonly origin: string;
|
|
46
|
+
readonly identity?: IdentityInputs;
|
|
47
|
+
readonly content: ReportContent;
|
|
48
|
+
readonly screenshot?: ScreenshotFile | null;
|
|
49
|
+
}
|
|
50
|
+
/** The outcome of a submission — never an exception. */
|
|
51
|
+
export type SubmitResult = {
|
|
52
|
+
readonly ok: true;
|
|
53
|
+
readonly feedback: RecordedFeedback | null;
|
|
54
|
+
} | {
|
|
55
|
+
readonly ok: false;
|
|
56
|
+
readonly reason: "unreachable" | "refused";
|
|
57
|
+
readonly status?: number;
|
|
58
|
+
} | {
|
|
59
|
+
/**
|
|
60
|
+
* A `source: auto` report shed under ingest backpressure — either held
|
|
61
|
+
* locally because the window is still open, or answered `429` by ingest,
|
|
62
|
+
* which opened/extended the window. Manual reports never produce this.
|
|
63
|
+
*/
|
|
64
|
+
readonly ok: false;
|
|
65
|
+
readonly reason: "backpressure";
|
|
66
|
+
readonly retryAfterSeconds: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* How long a submission may stay in flight before it is reported as
|
|
70
|
+
* `unreachable`. React Native's Android networking applies no default request
|
|
71
|
+
* timeout, so without this a stalled connection would hang a report forever.
|
|
72
|
+
* The abandoned request keeps running in the background harmlessly.
|
|
73
|
+
*/
|
|
74
|
+
export declare const DEFAULT_SUBMIT_TIMEOUT_MS = 30000;
|
|
75
|
+
/** Injectable transport collaborators, defaulted to the real implementations. */
|
|
76
|
+
export interface SubmitDeps {
|
|
77
|
+
readonly fetchImpl?: FetchLike;
|
|
78
|
+
readonly formData?: FormDataFactory;
|
|
79
|
+
readonly backoff?: AutoReportBackoff;
|
|
80
|
+
/** Overall request timeout in ms; `0` disables. Defaults to {@link DEFAULT_SUBMIT_TIMEOUT_MS}. */
|
|
81
|
+
readonly timeoutMs?: number;
|
|
82
|
+
}
|
|
83
|
+
/** Join an API base URL with the feedback path, tolerating a trailing slash. */
|
|
84
|
+
export declare function feedbackEndpoint(apiUrl: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Submit a report to ingest. Assembles the `payload` JSON (key + identity +
|
|
87
|
+
* content, with an explicit `source` stamped) and the optional screenshot file
|
|
88
|
+
* reference into a `FormData`, posts it with the configured `Origin` header,
|
|
89
|
+
* and resolves to the recorded Feedback on success or a named failure
|
|
90
|
+
* otherwise.
|
|
91
|
+
*
|
|
92
|
+
* A `source: auto` report first consults the shared backpressure window: while
|
|
93
|
+
* it is open the report is dropped without a request. Ingest's `429` +
|
|
94
|
+
* `Retry-After` opens/extends that window. Manual reports (`source: reporter`,
|
|
95
|
+
* the default) never consult the window and a non-2xx for them stays a plain
|
|
96
|
+
* refusal. The `deps` seams exist purely so the call is testable.
|
|
97
|
+
*/
|
|
98
|
+
export declare function submitReport(apiUrl: string, input: SubmitInput, deps?: SubmitDeps): Promise<SubmitResult>;
|
package/dist/submit.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The multipart feedback submission — the transport half of the capture loop,
|
|
3
|
+
* kept file-parallel with `packages/sdk/src/submit.ts` (ADR-0021).
|
|
4
|
+
*
|
|
5
|
+
* `POST /api/ingest/feedback` is a multipart request: a single text part
|
|
6
|
+
* `payload` carrying the JSON (publishable key + identity evidence + content)
|
|
7
|
+
* and an optional binary part `screenshot`. Two mobile differences:
|
|
8
|
+
*
|
|
9
|
+
* - a native HTTP stack attaches no `Origin` header, so the configured origin
|
|
10
|
+
* is sent explicitly (the server exact-matches it against the allowlist);
|
|
11
|
+
* - the screenshot is a `{ uri, name, type }` file reference (React Native's
|
|
12
|
+
* `FormData` file-part shape), not a `Blob`.
|
|
13
|
+
*
|
|
14
|
+
* The content-type header is deliberately not set, so the runtime writes the
|
|
15
|
+
* `multipart/form-data` boundary itself. Like `requestBoot`, this never throws:
|
|
16
|
+
* an unreachable Fixback or a refusal is a quiet result, so a Fixback problem
|
|
17
|
+
* never breaks the host app. Automatic reports (`source: auto`) honour ingest's
|
|
18
|
+
* `429` + `Retry-After` backpressure; manual reports are never gated.
|
|
19
|
+
*/
|
|
20
|
+
import { AutoReportBackoff } from "./auto-report-backoff";
|
|
21
|
+
import { globalFetch, globalFormData, } from "./http";
|
|
22
|
+
/** The multipart field the screenshot binary is sent under (mirrors the server). */
|
|
23
|
+
const SCREENSHOT_FIELD = "screenshot";
|
|
24
|
+
/**
|
|
25
|
+
* How long a submission may stay in flight before it is reported as
|
|
26
|
+
* `unreachable`. React Native's Android networking applies no default request
|
|
27
|
+
* timeout, so without this a stalled connection would hang a report forever.
|
|
28
|
+
* The abandoned request keeps running in the background harmlessly.
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_SUBMIT_TIMEOUT_MS = 30_000;
|
|
31
|
+
/**
|
|
32
|
+
* The shared `source: auto` backpressure window for the app. One instance so a
|
|
33
|
+
* `429` from an auto-report holds the auto-reports that follow it.
|
|
34
|
+
*/
|
|
35
|
+
const defaultBackoff = new AutoReportBackoff();
|
|
36
|
+
/** Join an API base URL with the feedback path, tolerating a trailing slash. */
|
|
37
|
+
export function feedbackEndpoint(apiUrl) {
|
|
38
|
+
return `${apiUrl.replace(/\/+$/, "")}/api/ingest/feedback`;
|
|
39
|
+
}
|
|
40
|
+
/** Drop `undefined` identity fields so the payload carries only what was given. */
|
|
41
|
+
function compactIdentity(identity) {
|
|
42
|
+
if (!identity)
|
|
43
|
+
return {};
|
|
44
|
+
const out = {};
|
|
45
|
+
if (identity.signedIdentity)
|
|
46
|
+
out.signedIdentity = identity.signedIdentity;
|
|
47
|
+
if (identity.reporterId)
|
|
48
|
+
out.reporterId = identity.reporterId;
|
|
49
|
+
if (identity.anonymousId)
|
|
50
|
+
out.anonymousId = identity.anonymousId;
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/** Read one response header defensively — `null` when unavailable or on any throw. */
|
|
54
|
+
function safeHeader(response, name) {
|
|
55
|
+
try {
|
|
56
|
+
return response.headers?.get?.(name) ?? null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Submit a report to ingest. Assembles the `payload` JSON (key + identity +
|
|
64
|
+
* content, with an explicit `source` stamped) and the optional screenshot file
|
|
65
|
+
* reference into a `FormData`, posts it with the configured `Origin` header,
|
|
66
|
+
* and resolves to the recorded Feedback on success or a named failure
|
|
67
|
+
* otherwise.
|
|
68
|
+
*
|
|
69
|
+
* A `source: auto` report first consults the shared backpressure window: while
|
|
70
|
+
* it is open the report is dropped without a request. Ingest's `429` +
|
|
71
|
+
* `Retry-After` opens/extends that window. Manual reports (`source: reporter`,
|
|
72
|
+
* the default) never consult the window and a non-2xx for them stays a plain
|
|
73
|
+
* refusal. The `deps` seams exist purely so the call is testable.
|
|
74
|
+
*/
|
|
75
|
+
export async function submitReport(apiUrl, input, deps = {}) {
|
|
76
|
+
const source = input.content.source ?? "reporter";
|
|
77
|
+
const isAuto = source === "auto";
|
|
78
|
+
const backoff = deps.backoff ?? defaultBackoff;
|
|
79
|
+
// Client-side backpressure: shed auto-reports while the hold window is open,
|
|
80
|
+
// without touching the network. Manual reports are never gated.
|
|
81
|
+
if (isAuto && backoff.isPaused()) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: "backpressure",
|
|
85
|
+
retryAfterSeconds: backoff.retryAfterSeconds(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const doFetch = deps.fetchImpl ?? globalFetch();
|
|
89
|
+
const makeForm = deps.formData ?? globalFormData();
|
|
90
|
+
if (!doFetch || !makeForm)
|
|
91
|
+
return { ok: false, reason: "unreachable" };
|
|
92
|
+
const payload = {
|
|
93
|
+
key: input.key,
|
|
94
|
+
...compactIdentity(input.identity),
|
|
95
|
+
...input.content,
|
|
96
|
+
source,
|
|
97
|
+
};
|
|
98
|
+
const form = makeForm();
|
|
99
|
+
form.append("payload", JSON.stringify(payload));
|
|
100
|
+
if (input.screenshot?.uri) {
|
|
101
|
+
// React Native's FormData accepts a { uri, name, type } object as a file
|
|
102
|
+
// part; the explicit type matters — the server answers 415 without a
|
|
103
|
+
// recognised image content type.
|
|
104
|
+
form.append(SCREENSHOT_FIELD, {
|
|
105
|
+
uri: input.screenshot.uri,
|
|
106
|
+
name: input.screenshot.name ?? "screenshot.png",
|
|
107
|
+
type: input.screenshot.type ?? "image/png",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_SUBMIT_TIMEOUT_MS;
|
|
111
|
+
let timer;
|
|
112
|
+
let response;
|
|
113
|
+
try {
|
|
114
|
+
const request = doFetch(feedbackEndpoint(apiUrl), {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { origin: input.origin },
|
|
117
|
+
body: form,
|
|
118
|
+
});
|
|
119
|
+
response =
|
|
120
|
+
timeoutMs > 0
|
|
121
|
+
? await Promise.race([
|
|
122
|
+
request,
|
|
123
|
+
new Promise((_, reject) => {
|
|
124
|
+
timer = setTimeout(() => reject(new Error("fixback: submission timed out")), timeoutMs);
|
|
125
|
+
}),
|
|
126
|
+
])
|
|
127
|
+
: await request;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return { ok: false, reason: "unreachable" };
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
if (timer !== undefined)
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
}
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
// A `429` for an auto-report is backpressure, not a refusal: open/extend
|
|
138
|
+
// the hold window from `Retry-After` and report the wait. Manual reports
|
|
139
|
+
// never 429 here, so any non-2xx for them stays a plain refusal.
|
|
140
|
+
if (isAuto && response.status === 429) {
|
|
141
|
+
const retryAfterSeconds = backoff.hold(safeHeader(response, "Retry-After"));
|
|
142
|
+
return { ok: false, reason: "backpressure", retryAfterSeconds };
|
|
143
|
+
}
|
|
144
|
+
return { ok: false, reason: "refused", status: response.status };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const feedback = (await response.json());
|
|
148
|
+
return { ok: true, feedback };
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Accepted, but the body wasn't the expected JSON — still a success.
|
|
152
|
+
return { ok: true, feedback: null };
|
|
153
|
+
}
|
|
154
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A vendored slice of the **Signal** design tokens for the mobile composer.
|
|
3
|
+
*
|
|
4
|
+
* React Native cannot consume CSS custom properties, so the semantic roles the
|
|
5
|
+
* composer needs are mirrored here as constants. Every value traces to
|
|
6
|
+
* `packages/ui/src/tokens.css` (the design source of truth) — change it there
|
|
7
|
+
* first, then keep this slice in step (ADR-0021 records the gap).
|
|
8
|
+
*
|
|
9
|
+
* IBM Plex is not loadable without the host app shipping fonts, so type falls
|
|
10
|
+
* back to the system stack; the mono accents use the platform's monospace face
|
|
11
|
+
* (the composer passes `Platform.select` for that — this module stays free of
|
|
12
|
+
* React Native imports so pure code can use it too).
|
|
13
|
+
*/
|
|
14
|
+
export declare const signal: {
|
|
15
|
+
readonly accent: "#2f6fed";
|
|
16
|
+
readonly accentTint: "#eaf1fe";
|
|
17
|
+
readonly accentBorder: "#cfe0fd";
|
|
18
|
+
readonly text: "#0f1720";
|
|
19
|
+
readonly textPanel: "#1a2530";
|
|
20
|
+
readonly textMuted: "#5a6875";
|
|
21
|
+
readonly textFaint: "#8a97a3";
|
|
22
|
+
readonly textSubtle: "#9aa7b2";
|
|
23
|
+
readonly textGhost: "#b3bdc7";
|
|
24
|
+
readonly surface: "#ffffff";
|
|
25
|
+
readonly canvas: "#f4f6f9";
|
|
26
|
+
readonly sunken: "#eef1f5";
|
|
27
|
+
readonly border: "#e6ebf0";
|
|
28
|
+
readonly borderControl: "#e0e6ec";
|
|
29
|
+
readonly borderStrong: "#dce3ea";
|
|
30
|
+
readonly danger: "#e5484d";
|
|
31
|
+
readonly dangerSurface: "#fdecec";
|
|
32
|
+
readonly dangerBorder: "#f7cfcf";
|
|
33
|
+
readonly success: "#2f9e5b";
|
|
34
|
+
readonly successSurface: "#e5f5eb";
|
|
35
|
+
readonly successBorder: "#bfe6cd";
|
|
36
|
+
readonly idea: "#8b5cf6";
|
|
37
|
+
readonly ideaSurface: "#f1ecfe";
|
|
38
|
+
readonly ideaBorder: "#e0d4fb";
|
|
39
|
+
readonly onEmphasis: "#ffffff";
|
|
40
|
+
readonly scrim: "rgba(15, 23, 32, 0.42)";
|
|
41
|
+
};
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A vendored slice of the **Signal** design tokens for the mobile composer.
|
|
3
|
+
*
|
|
4
|
+
* React Native cannot consume CSS custom properties, so the semantic roles the
|
|
5
|
+
* composer needs are mirrored here as constants. Every value traces to
|
|
6
|
+
* `packages/ui/src/tokens.css` (the design source of truth) — change it there
|
|
7
|
+
* first, then keep this slice in step (ADR-0021 records the gap).
|
|
8
|
+
*
|
|
9
|
+
* IBM Plex is not loadable without the host app shipping fonts, so type falls
|
|
10
|
+
* back to the system stack; the mono accents use the platform's monospace face
|
|
11
|
+
* (the composer passes `Platform.select` for that — this module stays free of
|
|
12
|
+
* React Native imports so pure code can use it too).
|
|
13
|
+
*/
|
|
14
|
+
export const signal = {
|
|
15
|
+
/* --fb-color-accent / tints */
|
|
16
|
+
accent: "#2f6fed",
|
|
17
|
+
accentTint: "#eaf1fe",
|
|
18
|
+
accentBorder: "#cfe0fd",
|
|
19
|
+
/* ink + muted text ramp */
|
|
20
|
+
text: "#0f1720",
|
|
21
|
+
textPanel: "#1a2530",
|
|
22
|
+
textMuted: "#5a6875",
|
|
23
|
+
textFaint: "#8a97a3",
|
|
24
|
+
textSubtle: "#9aa7b2",
|
|
25
|
+
textGhost: "#b3bdc7",
|
|
26
|
+
/* surfaces + borders */
|
|
27
|
+
surface: "#ffffff",
|
|
28
|
+
canvas: "#f4f6f9",
|
|
29
|
+
sunken: "#eef1f5",
|
|
30
|
+
border: "#e6ebf0",
|
|
31
|
+
borderControl: "#e0e6ec",
|
|
32
|
+
borderStrong: "#dce3ea",
|
|
33
|
+
/* status colors (danger doubles as the error tone; violet kept in the Signal palette) */
|
|
34
|
+
danger: "#e5484d",
|
|
35
|
+
dangerSurface: "#fdecec",
|
|
36
|
+
dangerBorder: "#f7cfcf",
|
|
37
|
+
success: "#2f9e5b",
|
|
38
|
+
successSurface: "#e5f5eb",
|
|
39
|
+
successBorder: "#bfe6cd",
|
|
40
|
+
idea: "#8b5cf6",
|
|
41
|
+
ideaSurface: "#f1ecfe",
|
|
42
|
+
ideaBorder: "#e0d4fb",
|
|
43
|
+
/* emphasis */
|
|
44
|
+
onEmphasis: "#ffffff",
|
|
45
|
+
scrim: "rgba(15, 23, 32, 0.42)",
|
|
46
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Expo SDK's own version string, reported to ingest as
|
|
3
|
+
* `environment.sdkVersion`. Kept as an inlined constant rather than a runtime
|
|
4
|
+
* `package.json` import, so the published build stays self-contained (the same
|
|
5
|
+
* posture as `packages/sdk/src/version.ts`).
|
|
6
|
+
*
|
|
7
|
+
* It must equal `package.json`'s `version`. Two things keep it there: the
|
|
8
|
+
* Changesets `version` step syncs it automatically (`scripts/sync-sdk-version.mjs`,
|
|
9
|
+
* wired into `version-packages`), and `version.test.ts` fails the build if the two
|
|
10
|
+
* ever drift. Do not hand-edit this line to a value other than `package.json`'s
|
|
11
|
+
* version.
|
|
12
|
+
*/
|
|
13
|
+
export declare const EXPO_SDK_VERSION = "0.1.0";
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Expo SDK's own version string, reported to ingest as
|
|
3
|
+
* `environment.sdkVersion`. Kept as an inlined constant rather than a runtime
|
|
4
|
+
* `package.json` import, so the published build stays self-contained (the same
|
|
5
|
+
* posture as `packages/sdk/src/version.ts`).
|
|
6
|
+
*
|
|
7
|
+
* It must equal `package.json`'s `version`. Two things keep it there: the
|
|
8
|
+
* Changesets `version` step syncs it automatically (`scripts/sync-sdk-version.mjs`,
|
|
9
|
+
* wired into `version-packages`), and `version.test.ts` fails the build if the two
|
|
10
|
+
* ever drift. Do not hand-edit this line to a value other than `package.json`'s
|
|
11
|
+
* version.
|
|
12
|
+
*/
|
|
13
|
+
export const EXPO_SDK_VERSION = "0.1.0";
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fixback/expo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Fixback capture SDK for Expo / React Native apps — shake the device to report feedback with trace Evidence.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"fixback",
|
|
26
|
+
"feedback",
|
|
27
|
+
"bug-report",
|
|
28
|
+
"expo",
|
|
29
|
+
"react-native",
|
|
30
|
+
"shake",
|
|
31
|
+
"sdk"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/wemuda/fixback.git",
|
|
36
|
+
"directory": "packages/expo"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/wemuda/fixback/tree/master/packages/expo#readme",
|
|
39
|
+
"bugs": "https://github.com/wemuda/fixback/issues",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@react-native-async-storage/async-storage": ">=1.23.0",
|
|
45
|
+
"expo-sensors": ">=13.0.0",
|
|
46
|
+
"react": ">=18.0.0",
|
|
47
|
+
"react-native": ">=0.74.0",
|
|
48
|
+
"react-native-view-shot": ">=3.8.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/react": "^19.0.0",
|
|
52
|
+
"react": "19.2.8",
|
|
53
|
+
"react-native": "^0.81.0",
|
|
54
|
+
"typescript": "5.9.3",
|
|
55
|
+
"vitest": "^4.1.10"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc -p tsconfig.build.json",
|
|
59
|
+
"lint": "eslint .",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"test": "vitest run"
|
|
62
|
+
}
|
|
63
|
+
}
|