@quikturn/logos 0.2.0 → 0.3.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/README.md +80 -82
- package/dist/client/index.cjs +18 -57
- package/dist/client/index.d.cts +2 -187
- package/dist/client/index.d.ts +2 -187
- package/dist/client/index.mjs +19 -55
- package/dist/element/index.cjs +288 -0
- package/dist/element/index.d.cts +29 -0
- package/dist/element/index.d.ts +29 -0
- package/dist/element/index.mjs +286 -0
- package/dist/index.cjs +1 -91
- package/dist/index.d.cts +1 -167
- package/dist/index.d.ts +1 -167
- package/dist/index.mjs +2 -83
- package/dist/server/index.cjs +15 -24
- package/dist/server/index.d.cts +2 -94
- package/dist/server/index.d.ts +2 -94
- package/dist/server/index.mjs +16 -23
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
> TypeScript SDK for the Quikturn Logos API -- fetch company logos with type safety.
|
|
4
4
|
|
|
5
|
+
## Packages
|
|
6
|
+
|
|
7
|
+
| Package | Description | Install |
|
|
8
|
+
|---------|-------------|---------|
|
|
9
|
+
| [`@quikturn/logos`](./README.md) | Core SDK -- URL builder, browser client, server client, web component | `pnpm add @quikturn/logos` |
|
|
10
|
+
| [`@quikturn/logos-react`](./packages/react/) | React components -- `<QuikturnLogo>`, `<QuikturnLogoCarousel>`, `<QuikturnLogoGrid>` | `pnpm add @quikturn/logos-react` |
|
|
11
|
+
|
|
5
12
|
## Features
|
|
6
13
|
|
|
7
14
|
- **Zero-dependency URL builder** -- universal, works in any JavaScript runtime
|
|
8
|
-
- **Browser client** -- blob URL management, retry/backoff,
|
|
15
|
+
- **Browser client** -- blob URL management, retry/backoff, scrape polling, event emission
|
|
9
16
|
- **Server client** -- Buffer output, ReadableStream streaming, concurrent batch operations
|
|
17
|
+
- **`<quikturn-logo>` web component** -- zero-effort attribution element with shadow DOM
|
|
18
|
+
- **React components** -- see [`@quikturn/logos-react`](./packages/react/) for `<QuikturnLogo>`, `<QuikturnLogoCarousel>`, and `<QuikturnLogoGrid>`
|
|
10
19
|
- **Full TypeScript support** -- strict types, discriminated union error codes, generic response shapes
|
|
11
20
|
- **Tree-shakeable** -- ESM and CJS dual builds; import only what you need
|
|
12
21
|
|
|
@@ -103,11 +112,64 @@ const stream = await client.getStream("github.com", { format: "png" });
|
|
|
103
112
|
Readable.fromWeb(stream).pipe(createWriteStream("logo.png"));
|
|
104
113
|
```
|
|
105
114
|
|
|
115
|
+
### Web Component
|
|
116
|
+
|
|
117
|
+
The `<quikturn-logo>` custom element renders a logo with built-in attribution. It uses shadow DOM to protect the attribution badge and requires no framework.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<script type="module">
|
|
121
|
+
import "@quikturn/logos/element";
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<quikturn-logo
|
|
125
|
+
domain="github.com"
|
|
126
|
+
token="qt_abc123"
|
|
127
|
+
size="64"
|
|
128
|
+
format="webp"
|
|
129
|
+
theme="dark"
|
|
130
|
+
></quikturn-logo>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
| Attribute | Type | Description |
|
|
134
|
+
|-----------|------|-------------|
|
|
135
|
+
| `domain` | `string` | Domain to fetch logo for (required for rendering) |
|
|
136
|
+
| `token` | `string` | Publishable API key |
|
|
137
|
+
| `size` | `string` | Image width in pixels |
|
|
138
|
+
| `format` | `string` | `"png"`, `"jpeg"`, `"webp"`, or `"avif"` |
|
|
139
|
+
| `greyscale` | presence | When present, applies greyscale transformation |
|
|
140
|
+
| `theme` | `string` | `"light"` or `"dark"` |
|
|
141
|
+
|
|
142
|
+
The element automatically registers as `quikturn-logo` on import and fires an attribution beacon on first render. Attribution styling uses `!important` rules inside the shadow DOM to prevent accidental removal.
|
|
143
|
+
|
|
144
|
+
### React Components
|
|
145
|
+
|
|
146
|
+
For React applications, install the companion package:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pnpm add @quikturn/logos-react
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { QuikturnProvider, QuikturnLogo, QuikturnLogoCarousel } from "@quikturn/logos-react";
|
|
154
|
+
|
|
155
|
+
<QuikturnProvider token="qt_your_key">
|
|
156
|
+
<QuikturnLogo domain="github.com" size={64} />
|
|
157
|
+
<QuikturnLogoCarousel
|
|
158
|
+
domains={["github.com", "stripe.com", "vercel.com"]}
|
|
159
|
+
speed={120}
|
|
160
|
+
fadeOut
|
|
161
|
+
pauseOnHover
|
|
162
|
+
/>
|
|
163
|
+
</QuikturnProvider>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
See the full API reference in [`@quikturn/logos-react` README](./packages/react/README.md).
|
|
167
|
+
|
|
106
168
|
## API Reference
|
|
107
169
|
|
|
108
170
|
### Universal (`@quikturn/logos`)
|
|
109
171
|
|
|
110
|
-
The universal entry point exports the URL builder, types, constants, error classes
|
|
172
|
+
The universal entry point exports the URL builder, types, constants, and error classes. No network calls are made from this module.
|
|
111
173
|
|
|
112
174
|
#### `logoUrl(domain, options?)`
|
|
113
175
|
|
|
@@ -127,24 +189,17 @@ Constructs a fully-qualified Logos API URL. Pure function with no side effects.
|
|
|
127
189
|
| Property | Type | Default | Description |
|
|
128
190
|
|----------|------|---------|-------------|
|
|
129
191
|
| `token` | `string` | -- | Publishable key (`qt_`/`pk_`) appended as a query parameter |
|
|
130
|
-
| `size` | `number` | `128` | Output width in pixels
|
|
192
|
+
| `size` | `number` | `128` | Output width in pixels |
|
|
131
193
|
| `width` | `number` | `128` | Alias for `size` |
|
|
132
194
|
| `greyscale` | `boolean` | `false` | When `true`, applies saturation: 0 transformation |
|
|
133
|
-
| `theme` | `"light" \| "dark"` | -- |
|
|
195
|
+
| `theme` | `"light" \| "dark"` | -- | Optimize logo for light or dark backgrounds |
|
|
134
196
|
| `format` | `SupportedOutputFormat \| FormatShorthand` | `"image/png"` | Output image format |
|
|
135
|
-
| `autoScrape` | `boolean` | `false` | Trigger background scrape if logo is not found |
|
|
136
197
|
| `baseUrl` | `string` | `"https://logos.getquikturn.io"` | Override the API base URL |
|
|
137
198
|
|
|
138
199
|
#### Types
|
|
139
200
|
|
|
140
201
|
```ts
|
|
141
202
|
import type {
|
|
142
|
-
// Key & Auth
|
|
143
|
-
KeyType, // "publishable" | "secret"
|
|
144
|
-
KeyPrefix, // "qt_" | "pk_" | "sk_"
|
|
145
|
-
Tier, // "free" | "launch" | "growth" | "enterprise"
|
|
146
|
-
TokenStatus, // "active" | "suspended" | "revoked"
|
|
147
|
-
|
|
148
203
|
// Request
|
|
149
204
|
ThemeOption, // "light" | "dark"
|
|
150
205
|
SupportedOutputFormat, // "image/png" | "image/jpeg" | "image/webp" | "image/avif"
|
|
@@ -156,16 +211,9 @@ import type {
|
|
|
156
211
|
BrowserLogoResponse,
|
|
157
212
|
ServerLogoResponse,
|
|
158
213
|
|
|
159
|
-
//
|
|
160
|
-
ScrapeJob,
|
|
161
|
-
ScrapePendingResponse,
|
|
162
|
-
ScrapeJobStatus, // "pending" | "complete" | "failed"
|
|
214
|
+
// Scrape polling
|
|
163
215
|
ScrapeProgressEvent,
|
|
164
216
|
|
|
165
|
-
// Attribution (free tier)
|
|
166
|
-
AttributionStatus,
|
|
167
|
-
AttributionInfo,
|
|
168
|
-
|
|
169
217
|
// Error codes
|
|
170
218
|
LogoErrorCode,
|
|
171
219
|
} from "@quikturn/logos";
|
|
@@ -177,24 +225,9 @@ import type {
|
|
|
177
225
|
|----------|-------|-------------|
|
|
178
226
|
| `BASE_URL` | `"https://logos.getquikturn.io"` | Root API endpoint |
|
|
179
227
|
| `DEFAULT_WIDTH` | `128` | Default logo width (px) |
|
|
180
|
-
| `MAX_WIDTH` | `800` | Max width for publishable keys |
|
|
181
|
-
| `MAX_WIDTH_SERVER` | `1200` | Max width for secret keys |
|
|
182
228
|
| `DEFAULT_FORMAT` | `"image/png"` | Default output MIME type |
|
|
183
229
|
| `SUPPORTED_FORMATS` | `Set<SupportedOutputFormat>` | All supported MIME types |
|
|
184
230
|
| `FORMAT_ALIASES` | `Record<FormatShorthand, SupportedOutputFormat>` | Shorthand-to-MIME mapping |
|
|
185
|
-
| `RATE_LIMITS` | `Record<Tier, { requests, windowSeconds }>` | Per-tier publishable rate limits |
|
|
186
|
-
| `SERVER_RATE_LIMITS` | `Record<Tier, { requests, windowSeconds }>` | Per-tier server rate limits |
|
|
187
|
-
| `MONTHLY_LIMITS` | `Record<Tier, number>` | Monthly request quotas |
|
|
188
|
-
| `TIERS` | `readonly Tier[]` | All tiers as a runtime array |
|
|
189
|
-
| `KEY_TYPES` | `readonly KeyType[]` | All key types as a runtime array |
|
|
190
|
-
|
|
191
|
-
#### `parseLogoHeaders(headers)`
|
|
192
|
-
|
|
193
|
-
Parses a `Headers` object from a fetch `Response` into a structured `LogoMetadata` object.
|
|
194
|
-
|
|
195
|
-
#### `parseRetryAfter(headers)`
|
|
196
|
-
|
|
197
|
-
Extracts the `Retry-After` header value in seconds. Returns `number | null`.
|
|
198
231
|
|
|
199
232
|
---
|
|
200
233
|
|
|
@@ -219,7 +252,6 @@ Fetches a logo and returns a `BrowserLogoResponse`.
|
|
|
219
252
|
| `greyscale` | `boolean` | `false` | Greyscale transformation |
|
|
220
253
|
| `theme` | `"light" \| "dark"` | -- | Gamma curve adjustment |
|
|
221
254
|
| `format` | `SupportedOutputFormat \| FormatShorthand` | `"image/png"` | Output format |
|
|
222
|
-
| `autoScrape` | `boolean` | `false` | Enable auto-scrape polling |
|
|
223
255
|
| `scrapeTimeout` | `number` | -- | Max time (ms) to wait for scrape completion |
|
|
224
256
|
| `onScrapeProgress` | `(event: ScrapeProgressEvent) => void` | -- | Callback for scrape progress |
|
|
225
257
|
| `signal` | `AbortSignal` | -- | Cancel the request |
|
|
@@ -319,7 +351,7 @@ Accepts the same options as `get()`.
|
|
|
319
351
|
|
|
320
352
|
#### `client.getUrl(domain, options?)`
|
|
321
353
|
|
|
322
|
-
Returns a plain URL string
|
|
354
|
+
Returns a plain URL string without making a network request. Does **not** include the secret key -- use the `Authorization: Bearer` header when fetching.
|
|
323
355
|
|
|
324
356
|
**Returns:** `string`
|
|
325
357
|
|
|
@@ -438,60 +470,26 @@ client.get("github.com", { format: "webp" });
|
|
|
438
470
|
|
|
439
471
|
### Theme Options
|
|
440
472
|
|
|
441
|
-
| Theme |
|
|
442
|
-
|
|
443
|
-
| `"light"` |
|
|
444
|
-
| `"dark"` |
|
|
445
|
-
|
|
446
|
-
## Auto-Scrape
|
|
447
|
-
|
|
448
|
-
When a logo is not found in the database, the SDK can automatically trigger a background scrape and poll for completion.
|
|
449
|
-
|
|
450
|
-
**Flow:** Request with `autoScrape: true` --> API returns `202 Accepted` with a scrape job --> SDK polls the job URL --> Logo becomes available --> SDK returns the final image.
|
|
451
|
-
|
|
452
|
-
```ts
|
|
453
|
-
const { url } = await client.get("brand-new-startup.com", {
|
|
454
|
-
autoScrape: true,
|
|
455
|
-
scrapeTimeout: 30_000, // wait up to 30 seconds
|
|
456
|
-
onScrapeProgress: (event) => {
|
|
457
|
-
console.log(`Scrape status: ${event.status}, progress: ${event.progress}%`);
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
If the scrape does not complete within `scrapeTimeout`, a `ScrapeTimeoutError` is thrown.
|
|
473
|
+
| Theme | Use Case |
|
|
474
|
+
|-------|----------|
|
|
475
|
+
| `"light"` | Optimized for light backgrounds |
|
|
476
|
+
| `"dark"` | Optimized for dark backgrounds |
|
|
463
477
|
|
|
464
478
|
## Rate Limits & Quotas
|
|
465
479
|
|
|
466
|
-
|
|
480
|
+
Rate limits and monthly quotas are enforced by the API server and vary by plan. The SDK automatically reads rate-limit headers to provide warnings via the event system and retries with backoff when limits are hit. See [Quikturn pricing](https://getquikturn.io/pricing) for details on your plan's limits.
|
|
467
481
|
|
|
468
|
-
|
|
469
|
-
|------|-------------|--------|
|
|
470
|
-
| Free | 100 | 60s |
|
|
471
|
-
| Launch | 500 | 60s |
|
|
472
|
-
| Growth | 5,000 | 60s |
|
|
473
|
-
| Enterprise | 50,000 | 60s |
|
|
482
|
+
## Related Packages
|
|
474
483
|
|
|
475
|
-
###
|
|
484
|
+
### [`@quikturn/logos-react`](./packages/react/)
|
|
476
485
|
|
|
477
|
-
|
|
478
|
-
|------|-------------|--------|
|
|
479
|
-
| Launch | 1,000 | 60s |
|
|
480
|
-
| Growth | 10,000 | 60s |
|
|
481
|
-
| Enterprise | 100,000 | 60s |
|
|
486
|
+
Ready-made React components for displaying Quikturn logos. Includes an infinite scrolling carousel, responsive grid, single logo image, context provider for token propagation, and a `useLogoUrl()` hook. Zero CSS dependencies -- inline styles only.
|
|
482
487
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
| Tier | Monthly Limit |
|
|
488
|
-
|------|--------------|
|
|
489
|
-
| Free | 500,000 |
|
|
490
|
-
| Launch | 1,000,000 |
|
|
491
|
-
| Growth | 5,000,000 |
|
|
492
|
-
| Enterprise | 10,000,000 |
|
|
488
|
+
```bash
|
|
489
|
+
pnpm add @quikturn/logos-react @quikturn/logos
|
|
490
|
+
```
|
|
493
491
|
|
|
494
|
-
|
|
492
|
+
See the full documentation at [`packages/react/README.md`](./packages/react/README.md).
|
|
495
493
|
|
|
496
494
|
## License
|
|
497
495
|
|
package/dist/client/index.cjs
CHANGED
|
@@ -183,7 +183,6 @@ function logoUrl(domain, options) {
|
|
|
183
183
|
greyscale,
|
|
184
184
|
theme,
|
|
185
185
|
format,
|
|
186
|
-
autoScrape,
|
|
187
186
|
baseUrl
|
|
188
187
|
} = options ?? {};
|
|
189
188
|
const maxWidth = token?.startsWith("sk_") ? MAX_WIDTH_SERVER : MAX_WIDTH;
|
|
@@ -210,9 +209,7 @@ function logoUrl(domain, options) {
|
|
|
210
209
|
if (resolvedFormat !== void 0) {
|
|
211
210
|
url.searchParams.set("format", resolvedFormat);
|
|
212
211
|
}
|
|
213
|
-
|
|
214
|
-
url.searchParams.set("autoScrape", "true");
|
|
215
|
-
}
|
|
212
|
+
url.searchParams.set("autoScrape", "true");
|
|
216
213
|
return url.toString();
|
|
217
214
|
}
|
|
218
215
|
|
|
@@ -520,47 +517,16 @@ async function handleScrapeResponse(response, originalUrl, fetchFn, options) {
|
|
|
520
517
|
}
|
|
521
518
|
}
|
|
522
519
|
|
|
523
|
-
// src/
|
|
524
|
-
var
|
|
525
|
-
|
|
526
|
-
"
|
|
527
|
-
|
|
528
|
-
"
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
"verified",
|
|
534
|
-
"pending"
|
|
535
|
-
]);
|
|
536
|
-
function safeParseDate(value) {
|
|
537
|
-
if (value === null) return void 0;
|
|
538
|
-
const date = new Date(value);
|
|
539
|
-
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
540
|
-
}
|
|
541
|
-
function computeIsValid(status, graceDeadline) {
|
|
542
|
-
if (ALWAYS_VALID_STATUSES.has(status)) return true;
|
|
543
|
-
if (status === "grace-period") {
|
|
544
|
-
return graceDeadline !== void 0 && graceDeadline.getTime() > Date.now();
|
|
545
|
-
}
|
|
546
|
-
return false;
|
|
547
|
-
}
|
|
548
|
-
function parseAttributionStatus(headers) {
|
|
549
|
-
const rawStatus = headers.get("X-Attribution-Status");
|
|
550
|
-
if (rawStatus === null) return null;
|
|
551
|
-
if (!VALID_ATTRIBUTION_STATUSES.has(rawStatus)) {
|
|
552
|
-
return { status: "error", isValid: false };
|
|
553
|
-
}
|
|
554
|
-
const status = rawStatus;
|
|
555
|
-
const graceDeadline = safeParseDate(headers.get("X-Attribution-Grace-Deadline"));
|
|
556
|
-
const verifiedAt = safeParseDate(headers.get("X-Attribution-Verified-At"));
|
|
557
|
-
const isValid = computeIsValid(status, graceDeadline);
|
|
558
|
-
return {
|
|
559
|
-
status,
|
|
560
|
-
isValid,
|
|
561
|
-
...graceDeadline !== void 0 ? { graceDeadline } : {},
|
|
562
|
-
...verifiedAt !== void 0 ? { verifiedAt } : {}
|
|
563
|
-
};
|
|
520
|
+
// src/internal/beacon.ts
|
|
521
|
+
var firedTokens = /* @__PURE__ */ new Set();
|
|
522
|
+
function fireBeacon(token) {
|
|
523
|
+
if (typeof window === "undefined") return;
|
|
524
|
+
if (!token) return;
|
|
525
|
+
if (token.startsWith("sk_")) return;
|
|
526
|
+
if (firedTokens.has(token)) return;
|
|
527
|
+
firedTokens.add(token);
|
|
528
|
+
const img = new Image();
|
|
529
|
+
img.src = `${BASE_URL}/_beacon?token=${token}&page=${encodeURIComponent(location.href)}`;
|
|
564
530
|
}
|
|
565
531
|
|
|
566
532
|
// src/client/index.ts
|
|
@@ -606,7 +572,6 @@ var QuikturnLogos = class {
|
|
|
606
572
|
greyscale: options?.greyscale,
|
|
607
573
|
theme: options?.theme,
|
|
608
574
|
format: options?.format,
|
|
609
|
-
autoScrape: options?.autoScrape,
|
|
610
575
|
baseUrl: this.baseUrl
|
|
611
576
|
});
|
|
612
577
|
const format = options?.format;
|
|
@@ -618,14 +583,12 @@ var QuikturnLogos = class {
|
|
|
618
583
|
onRateLimitWarning: (remaining, limit) => this.emit("rateLimitWarning", remaining, limit),
|
|
619
584
|
onQuotaWarning: (remaining, limit) => this.emit("quotaWarning", remaining, limit)
|
|
620
585
|
});
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
});
|
|
628
|
-
}
|
|
586
|
+
response = await handleScrapeResponse(response, url, browserFetch, {
|
|
587
|
+
scrapeTimeout: options?.scrapeTimeout,
|
|
588
|
+
onScrapeProgress: options?.onScrapeProgress,
|
|
589
|
+
signal: options?.signal,
|
|
590
|
+
token: this.token
|
|
591
|
+
});
|
|
629
592
|
const contentLength = response.headers.get("Content-Length");
|
|
630
593
|
if (contentLength) {
|
|
631
594
|
const size = parseInt(contentLength, 10);
|
|
@@ -641,6 +604,7 @@ var QuikturnLogos = class {
|
|
|
641
604
|
const metadata = parseLogoHeaders(response.headers);
|
|
642
605
|
const objectUrl = URL.createObjectURL(blob);
|
|
643
606
|
this.objectUrls.add(objectUrl);
|
|
607
|
+
fireBeacon(this.token);
|
|
644
608
|
return { url: objectUrl, blob, contentType, metadata };
|
|
645
609
|
}
|
|
646
610
|
/**
|
|
@@ -721,6 +685,3 @@ var QuikturnLogos = class {
|
|
|
721
685
|
};
|
|
722
686
|
|
|
723
687
|
exports.QuikturnLogos = QuikturnLogos;
|
|
724
|
-
exports.browserFetch = browserFetch;
|
|
725
|
-
exports.handleScrapeResponse = handleScrapeResponse;
|
|
726
|
-
exports.parseAttributionStatus = parseAttributionStatus;
|
package/dist/client/index.d.cts
CHANGED
|
@@ -67,189 +67,6 @@ interface ScrapeProgressEvent {
|
|
|
67
67
|
};
|
|
68
68
|
error?: string;
|
|
69
69
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Possible attribution verification states for free-tier consumers.
|
|
72
|
-
*
|
|
73
|
-
* - "verified" — Attribution confirmed and valid.
|
|
74
|
-
* - "pending" — Verification in progress; treated as valid.
|
|
75
|
-
* - "unverified" — Attribution not yet set up.
|
|
76
|
-
* - "failed" — Verification attempted but failed.
|
|
77
|
-
* - "grace-period" — Temporary grace; validity depends on graceDeadline.
|
|
78
|
-
* - "error" — An error occurred during verification.
|
|
79
|
-
*/
|
|
80
|
-
type AttributionStatus = "verified" | "pending" | "unverified" | "failed" | "grace-period" | "error";
|
|
81
|
-
/**
|
|
82
|
-
* Structured attribution information parsed from response headers.
|
|
83
|
-
*
|
|
84
|
-
* - `status` — Current attribution verification state.
|
|
85
|
-
* - `graceDeadline` — If in grace period, the deadline by which attribution must be verified.
|
|
86
|
-
* - `verifiedAt` — Timestamp of when attribution was last verified.
|
|
87
|
-
* - `isValid` — Derived boolean: true when status is "verified", "pending",
|
|
88
|
-
* or "grace-period" with a future graceDeadline.
|
|
89
|
-
*/
|
|
90
|
-
interface AttributionInfo {
|
|
91
|
-
status: AttributionStatus;
|
|
92
|
-
graceDeadline?: Date;
|
|
93
|
-
verifiedAt?: Date;
|
|
94
|
-
isValid: boolean;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* @quikturn/logos SDK — Browser Fetch Wrapper
|
|
99
|
-
*
|
|
100
|
-
* Low-level fetch wrapper for the browser client entry point. Handles HTTP
|
|
101
|
-
* error mapping, retry logic for rate-limited and server-error responses,
|
|
102
|
-
* AbortSignal propagation, and proactive warning callbacks when rate-limit
|
|
103
|
-
* or quota headroom drops below 10%.
|
|
104
|
-
*
|
|
105
|
-
* This module NEVER sets an Authorization header. Publishable keys are
|
|
106
|
-
* passed as query parameters by the URL builder, not as bearer tokens.
|
|
107
|
-
*/
|
|
108
|
-
/**
|
|
109
|
-
* Options accepted by {@link browserFetch}.
|
|
110
|
-
*
|
|
111
|
-
* - `maxRetries` — Maximum number of retry attempts for 429/500 responses. Default: 2.
|
|
112
|
-
* - `signal` — An `AbortSignal` to cancel the in-flight request.
|
|
113
|
-
* - `format` — MIME type string used as the `Accept` request header.
|
|
114
|
-
* - `onRateLimitWarning` — Called when `X-RateLimit-Remaining` drops below 10% of the limit.
|
|
115
|
-
* - `onQuotaWarning` — Called when `X-Quota-Remaining` drops below 10% of the limit.
|
|
116
|
-
*/
|
|
117
|
-
interface FetcherOptions {
|
|
118
|
-
maxRetries?: number;
|
|
119
|
-
signal?: AbortSignal;
|
|
120
|
-
format?: string;
|
|
121
|
-
onRateLimitWarning?: (remaining: number, limit: number) => void;
|
|
122
|
-
onQuotaWarning?: (remaining: number, limit: number) => void;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Performs a fetch request to the Logos API with error mapping and retry logic.
|
|
126
|
-
*
|
|
127
|
-
* **Error mapping:**
|
|
128
|
-
* | Status | Error Class | Notes |
|
|
129
|
-
* |--------|-----------------------|--------------------------------------------|
|
|
130
|
-
* | 401 | AuthenticationError | Invalid or missing API token |
|
|
131
|
-
* | 403 | ForbiddenError | Body text used as `reason` |
|
|
132
|
-
* | 404 | NotFoundError | Domain extracted from URL path |
|
|
133
|
-
* | 429 | QuotaExceededError | When `X-Quota-Limit` header is present |
|
|
134
|
-
* | 429 | RateLimitError | After retries are exhausted |
|
|
135
|
-
* | 500 | LogoError | After a single retry attempt |
|
|
136
|
-
*
|
|
137
|
-
* **Retry behavior:**
|
|
138
|
-
* - 429: Waits `Retry-After` seconds (default 60s), retries up to `maxRetries`.
|
|
139
|
-
* If `X-Quota-Limit` is present, throws `QuotaExceededError` immediately.
|
|
140
|
-
* - 500: Retries once after a 1-second delay, then throws.
|
|
141
|
-
* - AbortError: Never retried; throws immediately.
|
|
142
|
-
* - Network errors: Never retried; throws immediately.
|
|
143
|
-
*
|
|
144
|
-
* @param url - Fully-qualified Logos API URL (built by `logoUrl`).
|
|
145
|
-
* @param options - Optional fetch configuration.
|
|
146
|
-
* @returns The raw `Response` object on success (2xx).
|
|
147
|
-
*
|
|
148
|
-
* @throws {AuthenticationError} On 401.
|
|
149
|
-
* @throws {ForbiddenError} On 403.
|
|
150
|
-
* @throws {NotFoundError} On 404.
|
|
151
|
-
* @throws {RateLimitError} On 429 after retries exhausted (no quota header).
|
|
152
|
-
* @throws {QuotaExceededError} On 429 with `X-Quota-Limit` header.
|
|
153
|
-
* @throws {LogoError} On 500, network error, or abort.
|
|
154
|
-
*/
|
|
155
|
-
declare function browserFetch(url: string, options?: FetcherOptions): Promise<Response>;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @quikturn/logos SDK -- Scrape Poller
|
|
159
|
-
*
|
|
160
|
-
* Handles 202 (scrape_pending) responses from the Logos API. When the API
|
|
161
|
-
* returns a 202 with a {@link ScrapePendingResponse} body, this module polls
|
|
162
|
-
* the scrape job status URL with exponential backoff until the job completes,
|
|
163
|
-
* fails, or the configured timeout elapses.
|
|
164
|
-
*
|
|
165
|
-
* On completion, re-fetches the original logo URL (with optional token) and
|
|
166
|
-
* returns the final Response to the caller.
|
|
167
|
-
*/
|
|
168
|
-
|
|
169
|
-
/** A fetch function that takes a URL and returns a Response promise. */
|
|
170
|
-
type FetchFn = (url: string) => Promise<Response>;
|
|
171
|
-
/**
|
|
172
|
-
* Options accepted by {@link handleScrapeResponse}.
|
|
173
|
-
*
|
|
174
|
-
* - `scrapeTimeout` -- Maximum time (ms) to wait for the scrape to complete. Default: 30_000.
|
|
175
|
-
* - `onScrapeProgress` -- Called after each poll with the latest progress event.
|
|
176
|
-
* - `signal` -- An `AbortSignal` to cancel the polling loop.
|
|
177
|
-
* - `token` -- API token appended to the final logo re-fetch URL.
|
|
178
|
-
*/
|
|
179
|
-
interface ScrapePollerOptions {
|
|
180
|
-
scrapeTimeout?: number;
|
|
181
|
-
onScrapeProgress?: (event: ScrapeProgressEvent) => void;
|
|
182
|
-
signal?: AbortSignal;
|
|
183
|
-
token?: string;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Inspects a response from the Logos API and, if it is a 202 (scrape pending),
|
|
187
|
-
* polls the scrape job until completion and re-fetches the logo.
|
|
188
|
-
*
|
|
189
|
-
* **Non-202 responses** are returned immediately as a passthrough.
|
|
190
|
-
*
|
|
191
|
-
* **202 responses** trigger the following lifecycle:
|
|
192
|
-
* 1. Parse the {@link ScrapePendingResponse} JSON body to extract `scrapeJob`.
|
|
193
|
-
* 2. Wait `estimatedWaitMs` (the initial backoff interval).
|
|
194
|
-
* 3. Poll `scrapeJob.pollUrl` via `fetchFn`, parse the {@link ScrapeProgressEvent}.
|
|
195
|
-
* 4. Call `onScrapeProgress` with the event.
|
|
196
|
-
* 5. If `status === "complete"`, re-fetch `originalUrl` (with token if provided).
|
|
197
|
-
* 6. If `status === "failed"`, throw `LogoError` with code `SCRAPE_FAILED`.
|
|
198
|
-
* 7. If `status === "pending"`, double the backoff (capped at 5 s) and repeat.
|
|
199
|
-
* 8. If `scrapeTimeout` elapses, throw {@link ScrapeTimeoutError}.
|
|
200
|
-
* 9. If `signal` is aborted, throw `LogoError` with code `ABORT_ERROR`.
|
|
201
|
-
*
|
|
202
|
-
* @param response -- The raw Response from the initial logo fetch.
|
|
203
|
-
* @param originalUrl -- The original logo URL (re-fetched after scrape completes).
|
|
204
|
-
* @param fetchFn -- The fetch function (typically `browserFetch`).
|
|
205
|
-
* @param options -- Optional polling configuration.
|
|
206
|
-
* @returns The final logo Response on success.
|
|
207
|
-
*
|
|
208
|
-
* @throws {ScrapeTimeoutError} When polling exceeds `scrapeTimeout`.
|
|
209
|
-
* @throws {LogoError} When the scrape job fails, is aborted, or encounters a network error.
|
|
210
|
-
*/
|
|
211
|
-
declare function handleScrapeResponse(response: Response, originalUrl: string, fetchFn: FetchFn, options?: ScrapePollerOptions): Promise<Response>;
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @quikturn/logos SDK — Attribution Header Parser
|
|
215
|
-
*
|
|
216
|
-
* Parses attribution-related response headers returned by the Cloudflare Worker
|
|
217
|
-
* for free-tier consumers. The worker attaches these headers to indicate whether
|
|
218
|
-
* the consumer has properly attributed Quikturn on their site.
|
|
219
|
-
*
|
|
220
|
-
* | Header | Field |
|
|
221
|
-
* |----------------------------------|-----------------|
|
|
222
|
-
* | `X-Attribution-Status` | `status` |
|
|
223
|
-
* | `X-Attribution-Grace-Deadline` | `graceDeadline` |
|
|
224
|
-
* | `X-Attribution-Verified-At` | `verifiedAt` |
|
|
225
|
-
*
|
|
226
|
-
* The derived `isValid` field is computed from the status:
|
|
227
|
-
* - "verified" -> true
|
|
228
|
-
* - "pending" -> true
|
|
229
|
-
* - "grace-period" -> true only if graceDeadline exists and is in the future
|
|
230
|
-
* - "unverified" -> false
|
|
231
|
-
* - "failed" -> false
|
|
232
|
-
* - "error" -> false
|
|
233
|
-
*/
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Parses attribution response headers into a typed `AttributionInfo` object.
|
|
237
|
-
*
|
|
238
|
-
* Returns `null` when the `X-Attribution-Status` header is absent, indicating
|
|
239
|
-
* that the response is not from a free-tier request (attribution does not apply).
|
|
240
|
-
*
|
|
241
|
-
* @param headers - The `Headers` object from a fetch `Response`.
|
|
242
|
-
* @returns An `AttributionInfo` object, or `null` if no attribution header is present.
|
|
243
|
-
*
|
|
244
|
-
* @example
|
|
245
|
-
* ```ts
|
|
246
|
-
* const attribution = parseAttributionStatus(response.headers);
|
|
247
|
-
* if (attribution && !attribution.isValid) {
|
|
248
|
-
* console.warn("Attribution required for free-tier usage");
|
|
249
|
-
* }
|
|
250
|
-
* ```
|
|
251
|
-
*/
|
|
252
|
-
declare function parseAttributionStatus(headers: Headers): AttributionInfo | null;
|
|
253
70
|
|
|
254
71
|
/**
|
|
255
72
|
* @quikturn/logos SDK — Browser Client Class
|
|
@@ -291,7 +108,6 @@ interface BrowserClientOptions {
|
|
|
291
108
|
* - `greyscale` — When true, returns a greyscale image.
|
|
292
109
|
* - `theme` — "light" or "dark" gamma adjustment.
|
|
293
110
|
* - `format` — Output image format (MIME type or shorthand).
|
|
294
|
-
* - `autoScrape` — When true, triggers background scrape if logo is not found.
|
|
295
111
|
* - `scrapeTimeout` — Maximum time (ms) to wait for a scrape to complete.
|
|
296
112
|
* - `onScrapeProgress`— Callback fired on each scrape poll.
|
|
297
113
|
* - `signal` — AbortSignal to cancel the request.
|
|
@@ -302,7 +118,6 @@ interface GetOptions {
|
|
|
302
118
|
greyscale?: boolean;
|
|
303
119
|
theme?: ThemeOption;
|
|
304
120
|
format?: SupportedOutputFormat | FormatShorthand;
|
|
305
|
-
autoScrape?: boolean;
|
|
306
121
|
scrapeTimeout?: number;
|
|
307
122
|
onScrapeProgress?: (event: ScrapeProgressEvent) => void;
|
|
308
123
|
signal?: AbortSignal;
|
|
@@ -363,7 +178,7 @@ declare class QuikturnLogos {
|
|
|
363
178
|
* // => "https://logos.getquikturn.io/github.com?token=qt_abc123&format=webp"
|
|
364
179
|
* ```
|
|
365
180
|
*/
|
|
366
|
-
getUrl(domain: string, options?: Omit<GetOptions, "
|
|
181
|
+
getUrl(domain: string, options?: Omit<GetOptions, "scrapeTimeout" | "onScrapeProgress" | "signal">): string;
|
|
367
182
|
/**
|
|
368
183
|
* Registers an event listener for a client event.
|
|
369
184
|
*
|
|
@@ -396,4 +211,4 @@ declare class QuikturnLogos {
|
|
|
396
211
|
private emit;
|
|
397
212
|
}
|
|
398
213
|
|
|
399
|
-
export { type BrowserClientOptions, type ClientEvent, type EventHandler, type
|
|
214
|
+
export { type BrowserClientOptions, type ClientEvent, type EventHandler, type GetOptions, QuikturnLogos };
|