cursedbelt 5.99.1 → 5.99.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/engagement/beaconClient.d.ts.map +1 -1
- package/dist/react/engagement/beaconClient.js +24 -3
- package/dist/react/engagement/beaconClient.js.map +1 -1
- package/package.json +2 -2
- package/src/react/engagement/beaconClient.spec.ts +25 -14
- package/src/react/engagement/beaconClient.ts +25 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beaconClient.d.ts","sourceRoot":"","sources":["../../../src/react/engagement/beaconClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAaD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAYD,8DAA8D;AAC9D,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;
|
|
1
|
+
{"version":3,"file":"beaconClient.d.ts","sourceRoot":"","sources":["../../../src/react/engagement/beaconClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAaD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAYD,8DAA8D;AAC9D,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AA6CD;;;;;GAKG;AACH,wBAAsB,WAAW,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,OAAO,CAAC,CAgClB;AAED,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC9D;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBtG;AAED,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC;AAIpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAehF"}
|
|
@@ -97,6 +97,23 @@ export function resetEngagementBeacon() {
|
|
|
97
97
|
lastReported = null;
|
|
98
98
|
sessionAnswer = null;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Read an answer to its end, and drop it.
|
|
102
|
+
*
|
|
103
|
+
* 🔴 Chromium reports a fetch whose body nobody reads as STILL LOADING, so Playwright's
|
|
104
|
+
* `networkidle` never comes on a page that fired the beacon. flix's desktop design audit waited
|
|
105
|
+
* out its 15 s cap at every place it visited — 60 s of a 98 s gate (forge task 3884); the resume
|
|
106
|
+
* client learned the same thing in task 2574 (`deep-link/resume.ts`). Every answer this file
|
|
107
|
+
* receives — the probe's refusal, the recorder's reply — is read through here.
|
|
108
|
+
*/
|
|
109
|
+
async function drain(res) {
|
|
110
|
+
try {
|
|
111
|
+
await res.text();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// The request was made; an answer that cannot be read changes nothing it reports.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
100
117
|
/**
|
|
101
118
|
* Is there a session? Silent, cached, and false on any doubt.
|
|
102
119
|
*
|
|
@@ -113,8 +130,10 @@ async function hasSession(options) {
|
|
|
113
130
|
const res = await doFetch(options.sessionPath ?? SESSION_PATH, {
|
|
114
131
|
credentials: "same-origin",
|
|
115
132
|
});
|
|
116
|
-
if (!res.ok)
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
await drain(res);
|
|
117
135
|
return false;
|
|
136
|
+
}
|
|
118
137
|
const body = (await res.json());
|
|
119
138
|
return body?.authenticated === true;
|
|
120
139
|
}
|
|
@@ -147,7 +166,7 @@ export async function reportPlace(place, options = {}) {
|
|
|
147
166
|
return false;
|
|
148
167
|
lastReported = place;
|
|
149
168
|
try {
|
|
150
|
-
await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/view`, {
|
|
169
|
+
const res = await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/view`, {
|
|
151
170
|
method: "POST",
|
|
152
171
|
headers: { "content-type": "application/json" },
|
|
153
172
|
// Same-origin credentials: the session cookie is the only identity
|
|
@@ -156,6 +175,7 @@ export async function reportPlace(place, options = {}) {
|
|
|
156
175
|
body: JSON.stringify({ place }),
|
|
157
176
|
keepalive: true,
|
|
158
177
|
});
|
|
178
|
+
await drain(res);
|
|
159
179
|
return true;
|
|
160
180
|
}
|
|
161
181
|
catch {
|
|
@@ -195,13 +215,14 @@ export async function reportAction(action, options = {}) {
|
|
|
195
215
|
}
|
|
196
216
|
const place = options.place ?? (typeof window === "undefined" ? "/" : currentLocationPlace());
|
|
197
217
|
try {
|
|
198
|
-
await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/action`, {
|
|
218
|
+
const res = await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/action`, {
|
|
199
219
|
method: "POST",
|
|
200
220
|
headers: { "content-type": "application/json" },
|
|
201
221
|
credentials: "same-origin",
|
|
202
222
|
body: JSON.stringify({ place, action }),
|
|
203
223
|
keepalive: true,
|
|
204
224
|
});
|
|
225
|
+
await drain(res);
|
|
205
226
|
return true;
|
|
206
227
|
}
|
|
207
228
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beaconClient.js","sourceRoot":"","sources":["../../../src/react/engagement/beaconClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAmBhD;;;;;;;;GAQG;AACH,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IACnC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,IAAI,aAAa,GAA4B,IAAI,CAAC;AAElD,8DAA8D;AAC9D,MAAM,UAAU,qBAAqB;IACpC,YAAY,GAAG,IAAI,CAAC;IACpB,aAAa,GAAG,IAAI,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,UAAU,CAAC,OAA2B;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACtD,IAAI,OAAO,OAAO,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,WAAW,IAAI,YAAY,EAAE;YAC9D,WAAW,EAAE,aAAa;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"beaconClient.js","sourceRoot":"","sources":["../../../src/react/engagement/beaconClient.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "5.99.
|
|
3
|
+
"version": "5.99.2",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
|
|
@@ -965,7 +965,7 @@
|
|
|
965
965
|
"@vidstack/react": "1.15.6",
|
|
966
966
|
"@vitejs/plugin-react": "^5.2.0",
|
|
967
967
|
"cursedbelt-server": "^4.43.1",
|
|
968
|
-
"cursedops": "^0.
|
|
968
|
+
"cursedops": "^0.22.0",
|
|
969
969
|
"diff": "^9.0.0",
|
|
970
970
|
"emoji-mart": "^5.6.0",
|
|
971
971
|
"hls.js": "^1.5.0",
|
|
@@ -15,21 +15,19 @@
|
|
|
15
15
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
16
16
|
import { reportAction, reportPlace, resetEngagementBeacon, SESSION_PATH } from "./beaconClient.js";
|
|
17
17
|
|
|
18
|
-
/** A `fetch` that records every call and answers from a script. */
|
|
18
|
+
/** A `fetch` that records every call, and every answer it handed out, and answers from a script. */
|
|
19
19
|
function recordingFetch(answers: Record<string, { status?: number; body?: unknown }>) {
|
|
20
20
|
const calls: string[] = [];
|
|
21
|
+
const responses: Response[] = [];
|
|
21
22
|
const impl = (async (input: string) => {
|
|
22
23
|
const url = String(input);
|
|
23
24
|
calls.push(url);
|
|
24
25
|
const answer = answers[url] ?? { status: 404 };
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
status,
|
|
29
|
-
json: async () => answer.body ?? {},
|
|
30
|
-
};
|
|
26
|
+
const response = new Response(JSON.stringify(answer.body ?? {}), { status: answer.status ?? 200 });
|
|
27
|
+
responses.push(response);
|
|
28
|
+
return response;
|
|
31
29
|
}) as unknown as typeof fetch;
|
|
32
|
-
return { impl, calls };
|
|
30
|
+
return { impl, calls, responses };
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
const SIGNED_IN = { body: { authenticated: true } };
|
|
@@ -116,18 +114,31 @@ describe("the probe fails CLOSED", () => {
|
|
|
116
114
|
const impl = (async (input: string) => {
|
|
117
115
|
if (answer === null) throw new Error("network down");
|
|
118
116
|
const status = (answer as { status?: number }).status ?? 200;
|
|
119
|
-
return {
|
|
120
|
-
ok: status >= 200 && status < 300,
|
|
121
|
-
status,
|
|
122
|
-
json: async () => (answer as { body?: unknown }).body ?? {},
|
|
123
|
-
_url: input,
|
|
124
|
-
};
|
|
117
|
+
return new Response(JSON.stringify((answer as { body?: unknown }).body ?? {}), { status, headers: { "x-url": input } });
|
|
125
118
|
}) as unknown as typeof fetch;
|
|
126
119
|
|
|
127
120
|
expect(await reportPlace("/#/hub", { fetchImpl: impl })).toBe(false);
|
|
128
121
|
});
|
|
129
122
|
});
|
|
130
123
|
|
|
124
|
+
describe("every answer is read to its end (forge task 3884)", () => {
|
|
125
|
+
// Chromium reports a fetch whose body nobody reads as still loading, so Playwright's
|
|
126
|
+
// `networkidle` never came on a page that fired the beacon: flix's desktop audit waited out a
|
|
127
|
+
// 15 s cap at every place — 60 s of its 98 s gate.
|
|
128
|
+
test.each([
|
|
129
|
+
["a signed-in view", { [SESSION_PATH]: SIGNED_IN, "/api/engagement/view": { status: 204 } }],
|
|
130
|
+
["the recorder refuses", { [SESSION_PATH]: SIGNED_IN, "/api/engagement/view": { status: 404 } }],
|
|
131
|
+
["the probe refuses", { [SESSION_PATH]: { status: 500 } }],
|
|
132
|
+
["signed out", { [SESSION_PATH]: SIGNED_OUT }],
|
|
133
|
+
])("%s — no response is left unread", async (_name, answers) => {
|
|
134
|
+
const { impl, responses } = recordingFetch(answers);
|
|
135
|
+
await reportPlace("/#/hub", { fetchImpl: impl });
|
|
136
|
+
await reportAction("search", { fetchImpl: impl, place: "/#/hub" });
|
|
137
|
+
expect(responses.length).toBeGreaterThan(0);
|
|
138
|
+
expect(responses.map((r) => r.bodyUsed)).toEqual(responses.map(() => true));
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
131
142
|
describe("a caller that already knows may skip the probe", () => {
|
|
132
143
|
test("assumeSignedIn reports with no second answer to the same question", async () => {
|
|
133
144
|
const { impl, calls } = recordingFetch({ "/api/engagement/view": { status: 200 } });
|
|
@@ -121,6 +121,23 @@ export function resetEngagementBeacon(): void {
|
|
|
121
121
|
sessionAnswer = null;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Read an answer to its end, and drop it.
|
|
126
|
+
*
|
|
127
|
+
* 🔴 Chromium reports a fetch whose body nobody reads as STILL LOADING, so Playwright's
|
|
128
|
+
* `networkidle` never comes on a page that fired the beacon. flix's desktop design audit waited
|
|
129
|
+
* out its 15 s cap at every place it visited — 60 s of a 98 s gate (forge task 3884); the resume
|
|
130
|
+
* client learned the same thing in task 2574 (`deep-link/resume.ts`). Every answer this file
|
|
131
|
+
* receives — the probe's refusal, the recorder's reply — is read through here.
|
|
132
|
+
*/
|
|
133
|
+
async function drain(res: Response): Promise<void> {
|
|
134
|
+
try {
|
|
135
|
+
await res.text();
|
|
136
|
+
} catch {
|
|
137
|
+
// The request was made; an answer that cannot be read changes nothing it reports.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
124
141
|
/**
|
|
125
142
|
* Is there a session? Silent, cached, and false on any doubt.
|
|
126
143
|
*
|
|
@@ -136,7 +153,10 @@ async function hasSession(options: ReportPlaceOptions): Promise<boolean> {
|
|
|
136
153
|
const res = await doFetch(options.sessionPath ?? SESSION_PATH, {
|
|
137
154
|
credentials: "same-origin",
|
|
138
155
|
});
|
|
139
|
-
if (!res.ok)
|
|
156
|
+
if (!res.ok) {
|
|
157
|
+
await drain(res);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
140
160
|
const body = (await res.json()) as { authenticated?: unknown } | null;
|
|
141
161
|
return body?.authenticated === true;
|
|
142
162
|
} catch {
|
|
@@ -170,7 +190,7 @@ export async function reportPlace(
|
|
|
170
190
|
if (place === lastReported) return false;
|
|
171
191
|
lastReported = place;
|
|
172
192
|
try {
|
|
173
|
-
await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/view`, {
|
|
193
|
+
const res = await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/view`, {
|
|
174
194
|
method: "POST",
|
|
175
195
|
headers: { "content-type": "application/json" },
|
|
176
196
|
// Same-origin credentials: the session cookie is the only identity
|
|
@@ -179,6 +199,7 @@ export async function reportPlace(
|
|
|
179
199
|
body: JSON.stringify({ place }),
|
|
180
200
|
keepalive: true,
|
|
181
201
|
});
|
|
202
|
+
await drain(res);
|
|
182
203
|
return true;
|
|
183
204
|
} catch {
|
|
184
205
|
// See the header: telemetry never becomes the user's problem.
|
|
@@ -223,13 +244,14 @@ export async function reportAction(action: string, options: ReportActionOptions
|
|
|
223
244
|
}
|
|
224
245
|
const place = options.place ?? (typeof window === "undefined" ? "/" : currentLocationPlace());
|
|
225
246
|
try {
|
|
226
|
-
await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/action`, {
|
|
247
|
+
const res = await doFetch(`${options.basePath ?? ENGAGEMENT_BASE_PATH}/action`, {
|
|
227
248
|
method: "POST",
|
|
228
249
|
headers: { "content-type": "application/json" },
|
|
229
250
|
credentials: "same-origin",
|
|
230
251
|
body: JSON.stringify({ place, action }),
|
|
231
252
|
keepalive: true,
|
|
232
253
|
});
|
|
254
|
+
await drain(res);
|
|
233
255
|
return true;
|
|
234
256
|
} catch {
|
|
235
257
|
return false;
|