@rankwrangler/http-client 0.13.0 → 0.15.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 CHANGED
@@ -13,7 +13,7 @@ npm install @rankwrangler/http-client
13
13
  import { createRankWranglerClient } from '@rankwrangler/http-client';
14
14
 
15
15
  const client = createRankWranglerClient({
16
- baseUrl: 'https://rankwrangler.merchbase.co',
16
+ baseUrl: 'https://rankwrangler-api.merchbase.co',
17
17
  apiKey: process.env.MERCHBASE_API_KEY,
18
18
  });
19
19
 
@@ -1,5 +1,6 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
+ export type ServiceAccessErrorCode = "access_denied" | "access_unavailable" | "insufficient_scope" | "unauthenticated" | "unknown_service";
3
4
  export type AmazonListingStatus = "active" | "deleted";
4
5
  declare const rankwranglerServiceAccounts: import("drizzle-orm/pg-core").PgTableWithColumns<{
5
6
  name: "rankwrangler_service_accounts";
@@ -184,18 +185,22 @@ declare const rankwranglerServiceAccounts: import("drizzle-orm/pg-core").PgTable
184
185
  };
185
186
  dialect: "pg";
186
187
  }>;
187
- export type RankWranglerServicePrincipal = Omit<typeof rankwranglerServiceAccounts.$inferSelect, "merchbaseUserId"> & {
188
+ /** The immutable identity of a user's service account; usage is read fresh where it is metered. */
189
+ export type RankWranglerServicePrincipal = Pick<typeof rankwranglerServiceAccounts.$inferSelect, "id" | "service"> & {
188
190
  merchbaseUserId: string;
189
191
  };
190
- export interface ClerkUser {
192
+ export interface MerchbaseUser {
191
193
  sub: string;
192
194
  email?: string;
193
195
  }
194
- export type CredentialKind = "api_key" | "oauth" | "session";
195
196
  export interface ContextRequest {
197
+ method?: string;
198
+ url?: string;
196
199
  headers: {
197
200
  authorization?: string;
198
201
  host?: string;
202
+ "cf-access-jwt-assertion"?: string;
203
+ "x-merchbase-user-id"?: string | string[];
199
204
  };
200
205
  }
201
206
  declare const keywordStages: readonly [
@@ -231,7 +236,7 @@ export interface Product {
231
236
  } | {
232
237
  status: "unavailable";
233
238
  };
234
- isMerchListing: boolean | null;
239
+ isMerchListing: boolean;
235
240
  amazonListingStatus: AmazonListingStatus;
236
241
  };
237
242
  category: {
@@ -270,7 +275,7 @@ export interface KeywordResultsProduct {
270
275
  } | {
271
276
  status: "unavailable";
272
277
  };
273
- isMerchListing: boolean | null;
278
+ isMerchListing: boolean;
274
279
  amazonListingStatus: AmazonListingStatus;
275
280
  category: {
276
281
  id: number;
@@ -349,13 +354,13 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
349
354
  credentialKind: null;
350
355
  authExpiresAtMs: null;
351
356
  accessPrincipal: null;
352
- accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
357
+ accessError: ServiceAccessErrorCode | null;
353
358
  request: ContextRequest;
354
359
  } | {
355
- user: ClerkUser;
360
+ user: MerchbaseUser;
356
361
  isAdmin: boolean;
357
362
  authType: "access";
358
- credentialKind: CredentialKind;
363
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
359
364
  authExpiresAtMs: number | null;
360
365
  accessPrincipal: RankWranglerServicePrincipal;
361
366
  accessError: null;
@@ -373,13 +378,13 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
373
378
  credentialKind: null;
374
379
  authExpiresAtMs: null;
375
380
  accessPrincipal: null;
376
- accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
381
+ accessError: ServiceAccessErrorCode | null;
377
382
  request: ContextRequest;
378
383
  } | {
379
- user: ClerkUser;
384
+ user: MerchbaseUser;
380
385
  isAdmin: boolean;
381
386
  authType: "access";
382
- credentialKind: CredentialKind;
387
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
383
388
  authExpiresAtMs: number | null;
384
389
  accessPrincipal: RankWranglerServicePrincipal;
385
390
  accessError: null;
@@ -397,13 +402,13 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
397
402
  credentialKind: null;
398
403
  authExpiresAtMs: null;
399
404
  accessPrincipal: null;
400
- accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
405
+ accessError: ServiceAccessErrorCode | null;
401
406
  request: ContextRequest;
402
407
  } | {
403
- user: ClerkUser;
408
+ user: MerchbaseUser;
404
409
  isAdmin: boolean;
405
410
  authType: "access";
406
- credentialKind: CredentialKind;
411
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
407
412
  authExpiresAtMs: number | null;
408
413
  accessPrincipal: RankWranglerServicePrincipal;
409
414
  accessError: null;
@@ -421,13 +426,13 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
421
426
  credentialKind: null;
422
427
  authExpiresAtMs: null;
423
428
  accessPrincipal: null;
424
- accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
429
+ accessError: ServiceAccessErrorCode | null;
425
430
  request: ContextRequest;
426
431
  } | {
427
- user: ClerkUser;
432
+ user: MerchbaseUser;
428
433
  isAdmin: boolean;
429
434
  authType: "access";
430
- credentialKind: CredentialKind;
435
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
431
436
  authExpiresAtMs: number | null;
432
437
  accessPrincipal: RankWranglerServicePrincipal;
433
438
  accessError: null;
@@ -444,22 +449,22 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
444
449
  output: {
445
450
  keyword: string;
446
451
  data: {
447
- createdAt: string;
448
- lastCheckedAt: string | null;
449
- nextCheckAt: string | null;
452
+ freshness: (typeof keywordFreshness)[number] | null;
453
+ products: number;
450
454
  id: string;
455
+ createdAt: string;
456
+ searchFrequencyRank: number | null;
457
+ intervalDays: number | null;
451
458
  queryId: string;
452
- term: string;
459
+ nextCheckAt: string | null;
453
460
  stage: (typeof keywordStages)[number];
454
- freshness: (typeof keywordFreshness)[number] | null;
455
- retryScheduled: boolean;
456
461
  heat: string;
457
462
  score: number | null;
458
- searchFrequencyRank: number | null;
459
- sources: string[];
460
- products: number;
461
463
  merch: number;
462
- intervalDays: number | null;
464
+ term: string;
465
+ retryScheduled: boolean;
466
+ sources: string[];
467
+ lastCheckedAt: string | null;
463
468
  };
464
469
  };
465
470
  meta: object;
@@ -476,9 +481,9 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
476
481
  input: {
477
482
  text: string;
478
483
  freshness?: "live" | "needsSync" | "syncing" | "all" | undefined;
479
- sort?: "work" | "newest" | undefined;
480
- stage?: "trial" | "graduated" | "dropped" | "untracked" | "all" | undefined;
481
484
  limit?: number | undefined;
485
+ sort?: "work" | "newest" | undefined;
486
+ stage?: "trial" | "dropped" | "graduated" | "untracked" | "all" | undefined;
482
487
  heat?: "unrated" | "hot" | "warm" | "cold" | "all" | undefined;
483
488
  cursor?: {
484
489
  key: [
@@ -496,22 +501,22 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
496
501
  };
497
502
  output: {
498
503
  items: {
499
- createdAt: string;
500
- lastCheckedAt: string | null;
501
- nextCheckAt: string | null;
504
+ freshness: (typeof keywordFreshness)[number] | null;
505
+ products: number;
502
506
  id: string;
507
+ createdAt: string;
508
+ searchFrequencyRank: number | null;
509
+ intervalDays: number | null;
503
510
  queryId: string;
504
- term: string;
511
+ nextCheckAt: string | null;
505
512
  stage: (typeof keywordStages)[number];
506
- freshness: (typeof keywordFreshness)[number] | null;
507
- retryScheduled: boolean;
508
513
  heat: string;
509
514
  score: number | null;
510
- searchFrequencyRank: number | null;
511
- sources: string[];
512
- products: number;
513
515
  merch: number;
514
- intervalDays: number | null;
516
+ term: string;
517
+ retryScheduled: boolean;
518
+ sources: string[];
519
+ lastCheckedAt: string | null;
515
520
  }[];
516
521
  nextCursor: {
517
522
  sort: "fresh" | "work" | "newest" | "score" | "medianBsr" | "merch";
@@ -538,13 +543,13 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
538
543
  credentialKind: null;
539
544
  authExpiresAtMs: null;
540
545
  accessPrincipal: null;
541
- accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
546
+ accessError: ServiceAccessErrorCode | null;
542
547
  request: ContextRequest;
543
548
  } | {
544
- user: ClerkUser;
549
+ user: MerchbaseUser;
545
550
  isAdmin: boolean;
546
551
  authType: "access";
547
- credentialKind: CredentialKind;
552
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
548
553
  authExpiresAtMs: number | null;
549
554
  accessPrincipal: RankWranglerServicePrincipal;
550
555
  accessError: null;
@@ -593,7 +598,7 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
593
598
  textFields?: "title" | "all" | undefined;
594
599
  excludeWords?: string[] | undefined;
595
600
  marketplaceIds?: string[] | undefined;
596
- isMerchListing?: boolean | "unknown" | "all" | undefined;
601
+ isMerchListing?: boolean | "all" | undefined;
597
602
  amazonListingStatus?: "active" | "deleted" | "all" | undefined;
598
603
  brands?: string[] | undefined;
599
604
  excludeBrands?: string[] | undefined;
@@ -643,6 +648,7 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
643
648
  } | null;
644
649
  } | null;
645
650
  items: {
651
+ extra?: undefined;
646
652
  marketplaceId: string;
647
653
  asin: string;
648
654
  title: string | null;
@@ -656,7 +662,7 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
656
662
  status: "unavailable";
657
663
  url?: undefined;
658
664
  };
659
- isMerchListing: boolean | null;
665
+ isMerchListing: boolean;
660
666
  amazonListingStatus: AmazonListingStatus;
661
667
  categoryId: number | null;
662
668
  salesRank: number | null;
@@ -0,0 +1 @@
1
+ export declare const credentialHeaders: (credential: string) => Record<string, string>;
@@ -0,0 +1,23 @@
1
+ export const credentialHeaders = (credential) => {
2
+ if (!credential.startsWith('mbk_v1.'))
3
+ return { Authorization: `Bearer ${credential}` };
4
+ try {
5
+ if (credential.length > 2048)
6
+ throw new Error();
7
+ const encoded = credential.slice(7);
8
+ if (!/^[A-Za-z0-9_-]+$/.test(encoded))
9
+ throw new Error();
10
+ const decoded = atob(encoded.replace(/-/g, '+').replace(/_/g, '/'));
11
+ if (btoa(decoded).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') !== encoded)
12
+ throw new Error();
13
+ const parsed = JSON.parse(decoded);
14
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || Object.keys(parsed).length !== 2 || !('clientId' in parsed) || !('clientSecret' in parsed) ||
15
+ typeof parsed.clientId !== 'string' || !/^[A-Za-z0-9._-]{1,256}$/.test(parsed.clientId) ||
16
+ typeof parsed.clientSecret !== 'string' || !/^[A-Za-z0-9_-]{32,512}$/.test(parsed.clientSecret))
17
+ throw new Error();
18
+ return { 'CF-Access-Client-Id': parsed.clientId, 'CF-Access-Client-Secret': parsed.clientSecret };
19
+ }
20
+ catch {
21
+ throw new Error('Invalid Merchbase machine credential.');
22
+ }
23
+ };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createTRPCProxyClient } from '@trpc/client';
2
2
  import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
3
3
  import type { PublicAppRouter } from './app-router';
4
- export declare const DEFAULT_API_BASE_URL = "https://rankwrangler.merchbase.co";
4
+ export declare const DEFAULT_API_BASE_URL = "https://rankwrangler-api.merchbase.co";
5
5
  export type AppRouter = PublicAppRouter;
6
6
  export type RouterInputs = inferRouterInputs<PublicAppRouter>;
7
7
  export type RouterOutputs = inferRouterOutputs<PublicAppRouter>;
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { credentialHeaders } from './credential-headers.js';
1
2
  import { createTRPCProxyClient, httpBatchLink, httpLink } from '@trpc/client';
2
3
  const TRAILING_SLASHES_REGEX = /\/+$/;
3
- export const DEFAULT_API_BASE_URL = 'https://rankwrangler.merchbase.co';
4
+ export const DEFAULT_API_BASE_URL = 'https://rankwrangler-api.merchbase.co';
4
5
  export const createRankWranglerClient = ({ baseUrl, apiKey, headers, batch = true, }) => {
5
6
  const url = `${normalizeBaseUrl(baseUrl)}/api`;
6
7
  const resolveHeaders = () => ({
7
- ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
8
+ ...(apiKey ? credentialHeaders(apiKey) : {}),
8
9
  ...(headers ?? {}),
9
10
  });
10
11
  if (batch) {
@@ -12,6 +13,7 @@ export const createRankWranglerClient = ({ baseUrl, apiKey, headers, batch = tru
12
13
  links: [
13
14
  httpBatchLink({
14
15
  url,
16
+ fetch: (input, init) => fetch(input, { ...init, redirect: 'manual' }),
15
17
  headers: resolveHeaders,
16
18
  }),
17
19
  ],
@@ -22,6 +24,7 @@ export const createRankWranglerClient = ({ baseUrl, apiKey, headers, batch = tru
22
24
  links: [
23
25
  httpLink({
24
26
  url,
27
+ fetch: (input, init) => fetch(input, { ...init, redirect: 'manual' }),
25
28
  headers: resolveHeaders,
26
29
  }),
27
30
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rankwrangler/http-client",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Typed tRPC client for RankWrangler",
5
5
  "repository": {
6
6
  "type": "git",