clixad 0.0.1-beta.24 → 0.0.1-beta.25
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/clixad.mjs +169 -8
- package/package.json +1 -1
package/dist/clixad.mjs
CHANGED
|
@@ -94,14 +94,62 @@ var init_estimate = __esm({
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
// src/sponsor/formats.ts
|
|
97
|
-
|
|
97
|
+
function isAdFormat(value) {
|
|
98
|
+
return typeof value === "string" && AD_FORMATS.includes(value);
|
|
99
|
+
}
|
|
100
|
+
var AD_FORMATS, EXTRA_AD_FORMATS;
|
|
98
101
|
var init_formats = __esm({
|
|
99
102
|
"src/sponsor/formats.ts"() {
|
|
100
103
|
"use strict";
|
|
104
|
+
AD_FORMATS = ["top_bar", "sidebar_card", "status_bar"];
|
|
101
105
|
EXTRA_AD_FORMATS = ["sidebar_card", "status_bar"];
|
|
102
106
|
}
|
|
103
107
|
});
|
|
104
108
|
|
|
109
|
+
// src/sponsor/direct.ts
|
|
110
|
+
function sanitizeAdText(raw) {
|
|
111
|
+
return raw.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g, "").replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").replace(/[--]/g, "").replace(/[-]/g, "").replace(/\s+/g, " ").trim();
|
|
112
|
+
}
|
|
113
|
+
function parseDirectLines(body) {
|
|
114
|
+
const raw = body?.lines;
|
|
115
|
+
if (!Array.isArray(raw)) return [];
|
|
116
|
+
const out = [];
|
|
117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
118
|
+
for (const item of raw) {
|
|
119
|
+
if (!item || typeof item !== "object") continue;
|
|
120
|
+
const e = item;
|
|
121
|
+
if (!isAdFormat(e.format) || seen.has(e.format)) continue;
|
|
122
|
+
if (e.hold === true) {
|
|
123
|
+
out.push({ format: e.format, hold: true });
|
|
124
|
+
seen.add(e.format);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (typeof e.text !== "string" || typeof e.serve_token !== "string" || typeof e.click_url !== "string") continue;
|
|
128
|
+
const text = sanitizeAdText(e.text).slice(0, MAX_TEXT);
|
|
129
|
+
if (!text || !e.serve_token || e.serve_token.length > 2e3) continue;
|
|
130
|
+
let clickUrl;
|
|
131
|
+
try {
|
|
132
|
+
const url = new URL(e.click_url);
|
|
133
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") continue;
|
|
134
|
+
clickUrl = url.toString();
|
|
135
|
+
} catch {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
out.push({ format: e.format, text, serveToken: e.serve_token, clickUrl });
|
|
139
|
+
seen.add(e.format);
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
var DIRECT_ASK_TIMEOUT_MS, MAX_TEXT;
|
|
144
|
+
var init_direct = __esm({
|
|
145
|
+
"src/sponsor/direct.ts"() {
|
|
146
|
+
"use strict";
|
|
147
|
+
init_formats();
|
|
148
|
+
DIRECT_ASK_TIMEOUT_MS = 1500;
|
|
149
|
+
MAX_TEXT = 200;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
105
153
|
// src/edge.ts
|
|
106
154
|
function detectEdgeBlock(status, headers, body) {
|
|
107
155
|
if (status !== 403) return void 0;
|
|
@@ -498,6 +546,7 @@ var init_client = __esm({
|
|
|
498
546
|
"use strict";
|
|
499
547
|
init_estimate();
|
|
500
548
|
init_formats();
|
|
549
|
+
init_direct();
|
|
501
550
|
init_edge();
|
|
502
551
|
CHAT_ENCODED_CONTENT_TYPE = "application/vnd.clixad.chat+gzip";
|
|
503
552
|
LEDGER_REASONS = {
|
|
@@ -645,6 +694,7 @@ var init_client = __esm({
|
|
|
645
694
|
return { status: "closed", message: closed.message, waitlistUrl: closed.waitlist_url };
|
|
646
695
|
}
|
|
647
696
|
if (!res.ok && !("status" in data && data.status)) {
|
|
697
|
+
if (res.status >= 500 || res.status === 429) return { status: "unavailable", error: `http ${res.status}` };
|
|
648
698
|
return { status: "error", error: `http ${res.status}` };
|
|
649
699
|
}
|
|
650
700
|
return data;
|
|
@@ -981,6 +1031,51 @@ var init_client = __esm({
|
|
|
981
1031
|
return 0;
|
|
982
1032
|
}
|
|
983
1033
|
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Ask for booked lines (step 2): one ask per model call, naming the slots
|
|
1036
|
+
* that fit the terminal now. See `sponsor/direct.ts` for the contract.
|
|
1037
|
+
*
|
|
1038
|
+
* Never throws, for `sponsorConfig`'s reason. A 404 means a gateway that
|
|
1039
|
+
* does not know the route, so `supported: false` tells the caller to stop
|
|
1040
|
+
* asking for the rest of the session; every other failure (a timeout, a 429,
|
|
1041
|
+
* a 5xx, a malformed body) is simply "nothing booked" this time.
|
|
1042
|
+
*/
|
|
1043
|
+
async directSponsor(formats, cols, signal) {
|
|
1044
|
+
try {
|
|
1045
|
+
const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/sponsor/direct`, {
|
|
1046
|
+
method: "POST",
|
|
1047
|
+
headers: this.headers(),
|
|
1048
|
+
...signal ? { signal } : {},
|
|
1049
|
+
body: JSON.stringify({ formats, cols })
|
|
1050
|
+
});
|
|
1051
|
+
if (res.status === 404) return { supported: false, lines: [] };
|
|
1052
|
+
if (!res.ok) return { supported: true, lines: [] };
|
|
1053
|
+
return { supported: true, lines: parseDirectLines(await res.json()) };
|
|
1054
|
+
} catch {
|
|
1055
|
+
return { supported: true, lines: [] };
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Report the booked lines a turn drew, in one call, after the turn is over.
|
|
1060
|
+
* Never to `/v1/sponsor/impression`, which would credit them as ADtention's.
|
|
1061
|
+
* Never throws; answers what was credited, for the tally and for tests.
|
|
1062
|
+
*/
|
|
1063
|
+
async reportDirectImpressions(impressions, signal) {
|
|
1064
|
+
if (impressions.length === 0) return 0;
|
|
1065
|
+
try {
|
|
1066
|
+
const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/sponsor/direct/impression`, {
|
|
1067
|
+
method: "POST",
|
|
1068
|
+
headers: this.headers(),
|
|
1069
|
+
...signal ? { signal } : {},
|
|
1070
|
+
body: JSON.stringify({ impressions: impressions.map((i) => ({ format: i.format, serve_token: i.serveToken })) })
|
|
1071
|
+
});
|
|
1072
|
+
if (!res.ok) return 0;
|
|
1073
|
+
const body = await res.json();
|
|
1074
|
+
return typeof body.credited === "number" ? body.credited : 0;
|
|
1075
|
+
} catch {
|
|
1076
|
+
return 0;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
984
1079
|
/**
|
|
985
1080
|
* Search the web, through the gateway.
|
|
986
1081
|
*
|
|
@@ -1443,11 +1538,24 @@ async function githubLogin(client, config, start, invite, report, signal) {
|
|
|
1443
1538
|
openBrowser(start.verification_uri);
|
|
1444
1539
|
const deadline = Date.now() + start.expires_in * 1e3;
|
|
1445
1540
|
let interval = Math.max(start.interval, 1);
|
|
1541
|
+
let unavailableSince;
|
|
1446
1542
|
while (Date.now() < deadline) {
|
|
1447
1543
|
await sleep2(interval * 1e3, signal);
|
|
1448
1544
|
if (signal?.aborted) return { status: "aborted" };
|
|
1449
1545
|
report.tick?.();
|
|
1450
|
-
|
|
1546
|
+
let poll;
|
|
1547
|
+
try {
|
|
1548
|
+
poll = await client.devicePoll(start.session, invite);
|
|
1549
|
+
} catch (err) {
|
|
1550
|
+
if (!(err instanceof GatewayUnreachableError)) throw err;
|
|
1551
|
+
poll = { status: "unavailable", error: err.message };
|
|
1552
|
+
}
|
|
1553
|
+
if (poll.status === "unavailable") {
|
|
1554
|
+
unavailableSince ??= Date.now();
|
|
1555
|
+
if (Date.now() - unavailableSince > UNAVAILABLE_GIVE_UP_MS) return { status: "failed", error: poll.error };
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
unavailableSince = void 0;
|
|
1451
1559
|
if (poll.status === "pending") {
|
|
1452
1560
|
if (poll.interval) interval = poll.interval;
|
|
1453
1561
|
continue;
|
|
@@ -1475,10 +1583,19 @@ async function githubLogin(client, config, start, invite, report, signal) {
|
|
|
1475
1583
|
}
|
|
1476
1584
|
};
|
|
1477
1585
|
}
|
|
1478
|
-
return { status: "failed", error: poll.error ?? poll.status };
|
|
1586
|
+
return { status: "failed", error: describePollError(poll.error ?? poll.status) };
|
|
1479
1587
|
}
|
|
1480
1588
|
return { status: "timeout" };
|
|
1481
1589
|
}
|
|
1590
|
+
function describePollError(error) {
|
|
1591
|
+
if (error === "unknown_session") {
|
|
1592
|
+
return "the server restarted while you were signing in, so this code no longer works. Sign in again.";
|
|
1593
|
+
}
|
|
1594
|
+
if (error === "login_unavailable") {
|
|
1595
|
+
return "GitHub approved the sign-in, but the server could not finish it. Try again in a minute.";
|
|
1596
|
+
}
|
|
1597
|
+
return error;
|
|
1598
|
+
}
|
|
1482
1599
|
async function devLogin(client, config, email, invite) {
|
|
1483
1600
|
let res;
|
|
1484
1601
|
try {
|
|
@@ -1517,7 +1634,7 @@ function sleep2(ms, signal) {
|
|
|
1517
1634
|
signal.addEventListener("abort", done, { once: true });
|
|
1518
1635
|
});
|
|
1519
1636
|
}
|
|
1520
|
-
var TERMS_URL, PRIVACY_URL, TERMS_NOTICE;
|
|
1637
|
+
var TERMS_URL, PRIVACY_URL, TERMS_NOTICE, UNAVAILABLE_GIVE_UP_MS;
|
|
1521
1638
|
var init_login = __esm({
|
|
1522
1639
|
"src/login.ts"() {
|
|
1523
1640
|
"use strict";
|
|
@@ -1529,6 +1646,7 @@ var init_login = __esm({
|
|
|
1529
1646
|
TERMS_NOTICE = `By creating an account you confirm that you are at least 18 and that you agree to
|
|
1530
1647
|
the Terms \u2014 ${TERMS_URL}
|
|
1531
1648
|
and the Privacy Policy \u2014 ${PRIVACY_URL}`;
|
|
1649
|
+
UNAVAILABLE_GIVE_UP_MS = 18e4;
|
|
1532
1650
|
}
|
|
1533
1651
|
});
|
|
1534
1652
|
|
|
@@ -5865,6 +5983,7 @@ function App({
|
|
|
5865
5983
|
notices: notices2,
|
|
5866
5984
|
sponsorServe,
|
|
5867
5985
|
sponsorFormats,
|
|
5986
|
+
sponsorDirect,
|
|
5868
5987
|
initHint
|
|
5869
5988
|
}) {
|
|
5870
5989
|
const { exit } = useApp();
|
|
@@ -5973,6 +6092,8 @@ function App({
|
|
|
5973
6092
|
const sponsorPrevRef = useRef(void 0);
|
|
5974
6093
|
const sponsorCallRef = useRef(0);
|
|
5975
6094
|
const pendingImpressionRef = useRef(/* @__PURE__ */ new Map());
|
|
6095
|
+
const pendingDirectRef = useRef(/* @__PURE__ */ new Map());
|
|
6096
|
+
const directSupportedRef = useRef(true);
|
|
5976
6097
|
const drawnRef = useRef(/* @__PURE__ */ new Set());
|
|
5977
6098
|
const sponsorSlotRef = useRef(
|
|
5978
6099
|
/* @__PURE__ */ new Map()
|
|
@@ -6184,8 +6305,7 @@ function App({
|
|
|
6184
6305
|
} else {
|
|
6185
6306
|
setSponsor(null);
|
|
6186
6307
|
}
|
|
6187
|
-
|
|
6188
|
-
if (!adFitsFormat(format, colsRef.current)) continue;
|
|
6308
|
+
const fetchNetwork = (format) => {
|
|
6189
6309
|
void remoteSponsorRef.current[format].fetch().then((line2) => {
|
|
6190
6310
|
if (!line2 || call !== sponsorCallRef.current) return;
|
|
6191
6311
|
if (!adFitsFormat(format, colsRef.current)) return;
|
|
@@ -6203,15 +6323,55 @@ function App({
|
|
|
6203
6323
|
recordImpression(tallyRef.current, line2.id);
|
|
6204
6324
|
pendingImpressionRef.current.set(format, { call, impression: line2.impression });
|
|
6205
6325
|
});
|
|
6206
|
-
}
|
|
6326
|
+
};
|
|
6327
|
+
const paintDirect = (line2) => {
|
|
6328
|
+
const { format } = line2;
|
|
6329
|
+
if (call !== sponsorCallRef.current) return;
|
|
6330
|
+
if (!adFitsFormat(format, colsRef.current)) return;
|
|
6331
|
+
if (format !== "top_bar" && modalOpenRef.current) return;
|
|
6332
|
+
const drawnId = `direct:${call}:${format}`;
|
|
6333
|
+
if (format === "top_bar") {
|
|
6334
|
+
if (!sponsorRow(line2.text, colsRef.current, "network")) return;
|
|
6335
|
+
setSponsor({ text: line2.text, origin: "network", impressionId: drawnId });
|
|
6336
|
+
} else {
|
|
6337
|
+
setAdSlots((slots) => ({ ...slots, [format]: { text: line2.text, impressionId: drawnId } }));
|
|
6338
|
+
}
|
|
6339
|
+
pendingDirectRef.current.set(format, { call, serveToken: line2.serveToken, drawnId });
|
|
6340
|
+
};
|
|
6341
|
+
const fitting = AD_FORMATS.filter((format) => adFitsFormat(format, colsRef.current));
|
|
6342
|
+
void (async () => {
|
|
6343
|
+
let direct = [];
|
|
6344
|
+
if (directSupportedRef.current && fitting.length > 0) {
|
|
6345
|
+
const ask3 = sponsorDirect ?? ((formats, cols2, signal) => client.directSponsor(formats, cols2, signal));
|
|
6346
|
+
let answer = { supported: true, lines: [] };
|
|
6347
|
+
try {
|
|
6348
|
+
answer = await ask3(fitting, colsRef.current, AbortSignal.timeout(DIRECT_ASK_TIMEOUT_MS));
|
|
6349
|
+
} catch {
|
|
6350
|
+
}
|
|
6351
|
+
if (!answer.supported) directSupportedRef.current = false;
|
|
6352
|
+
direct = answer.lines;
|
|
6353
|
+
}
|
|
6354
|
+
if (call !== sponsorCallRef.current) return;
|
|
6355
|
+
for (const format of ["top_bar", ...EXTRA_AD_FORMATS]) {
|
|
6356
|
+
if (!adFitsFormat(format, colsRef.current)) continue;
|
|
6357
|
+
const booked = direct.find((line2) => line2.format === format);
|
|
6358
|
+
if (booked && "hold" in booked) continue;
|
|
6359
|
+
if (booked) paintDirect(booked);
|
|
6360
|
+
else fetchNetwork(format);
|
|
6361
|
+
}
|
|
6362
|
+
})();
|
|
6207
6363
|
},
|
|
6208
|
-
[config.sponsor, stdout, wallet]
|
|
6364
|
+
[config.sponsor, stdout, wallet, client, sponsorDirect]
|
|
6209
6365
|
);
|
|
6210
6366
|
const reportSponsor = useCallback(() => {
|
|
6211
6367
|
const pending = [...pendingImpressionRef.current.values()];
|
|
6368
|
+
const pendingDirect = [...pendingDirectRef.current.entries()];
|
|
6212
6369
|
const drawn = drawnRef.current;
|
|
6213
6370
|
pendingImpressionRef.current = /* @__PURE__ */ new Map();
|
|
6371
|
+
pendingDirectRef.current = /* @__PURE__ */ new Map();
|
|
6214
6372
|
drawnRef.current = /* @__PURE__ */ new Set();
|
|
6373
|
+
const directReports = pendingDirect.filter(([, p]) => p.call === sponsorCallRef.current && drawn.has(p.drawnId)).map(([format, p]) => ({ format, serveToken: p.serveToken }));
|
|
6374
|
+
if (directReports.length > 0) void client.reportDirectImpressions(directReports);
|
|
6215
6375
|
const reports = pending.filter((p) => p.call === sponsorCallRef.current).filter((p) => drawn.has(p.impression.impressionId)).map((p) => p.impression);
|
|
6216
6376
|
if (reports.length === 0) return;
|
|
6217
6377
|
void (async () => {
|
|
@@ -7876,6 +8036,7 @@ var init_app = __esm({
|
|
|
7876
8036
|
init_provider();
|
|
7877
8037
|
init_network();
|
|
7878
8038
|
init_formats();
|
|
8039
|
+
init_direct();
|
|
7879
8040
|
init_render();
|
|
7880
8041
|
init_commands();
|
|
7881
8042
|
init_editor();
|