@rankwrangler/http-client 0.12.0 → 0.14.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
 
@@ -187,17 +187,31 @@ declare const rankwranglerServiceAccounts: import("drizzle-orm/pg-core").PgTable
187
187
  export type RankWranglerServicePrincipal = Omit<typeof rankwranglerServiceAccounts.$inferSelect, "merchbaseUserId"> & {
188
188
  merchbaseUserId: string;
189
189
  };
190
- export interface ClerkUser {
190
+ export interface MerchbaseUser {
191
191
  sub: string;
192
192
  email?: string;
193
193
  }
194
- export type CredentialKind = "api_key" | "oauth" | "session";
195
194
  export interface ContextRequest {
195
+ method?: string;
196
+ url?: string;
196
197
  headers: {
197
198
  authorization?: string;
198
199
  host?: string;
200
+ "cf-access-jwt-assertion"?: string;
201
+ "x-merchbase-user-id"?: string | string[];
199
202
  };
200
203
  }
204
+ declare const keywordStages: readonly [
205
+ "graduated",
206
+ "trial",
207
+ "dropped",
208
+ "untracked"
209
+ ];
210
+ declare const keywordFreshness: readonly [
211
+ "live",
212
+ "needsSync",
213
+ "syncing"
214
+ ];
201
215
  export type PublicCutoutThumbnail = {
202
216
  status: "available";
203
217
  url: string;
@@ -341,10 +355,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
341
355
  accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
342
356
  request: ContextRequest;
343
357
  } | {
344
- user: ClerkUser;
358
+ user: MerchbaseUser;
345
359
  isAdmin: boolean;
346
360
  authType: "access";
347
- credentialKind: CredentialKind;
361
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
348
362
  authExpiresAtMs: number | null;
349
363
  accessPrincipal: RankWranglerServicePrincipal;
350
364
  accessError: null;
@@ -365,10 +379,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
365
379
  accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
366
380
  request: ContextRequest;
367
381
  } | {
368
- user: ClerkUser;
382
+ user: MerchbaseUser;
369
383
  isAdmin: boolean;
370
384
  authType: "access";
371
- credentialKind: CredentialKind;
385
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
372
386
  authExpiresAtMs: number | null;
373
387
  accessPrincipal: RankWranglerServicePrincipal;
374
388
  accessError: null;
@@ -389,10 +403,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
389
403
  accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
390
404
  request: ContextRequest;
391
405
  } | {
392
- user: ClerkUser;
406
+ user: MerchbaseUser;
393
407
  isAdmin: boolean;
394
408
  authType: "access";
395
- credentialKind: CredentialKind;
409
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
396
410
  authExpiresAtMs: number | null;
397
411
  accessPrincipal: RankWranglerServicePrincipal;
398
412
  accessError: null;
@@ -413,10 +427,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
413
427
  accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
414
428
  request: ContextRequest;
415
429
  } | {
416
- user: ClerkUser;
430
+ user: MerchbaseUser;
417
431
  isAdmin: boolean;
418
432
  authType: "access";
419
- credentialKind: CredentialKind;
433
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
420
434
  authExpiresAtMs: number | null;
421
435
  accessPrincipal: RankWranglerServicePrincipal;
422
436
  accessError: null;
@@ -437,13 +451,14 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
437
451
  lastCheckedAt: string | null;
438
452
  nextCheckAt: string | null;
439
453
  id: string;
440
- queryId: string | null;
454
+ queryId: string;
441
455
  term: string;
442
- status: "queued" | "scanning" | "upToDate" | "untracked";
443
- untrackedReason: "notRelevant" | "noMerchFound" | "notScheduled" | null;
456
+ stage: (typeof keywordStages)[number];
457
+ freshness: (typeof keywordFreshness)[number] | null;
444
458
  retryScheduled: boolean;
445
459
  heat: string;
446
460
  score: number | null;
461
+ searchFrequencyRank: number | null;
447
462
  sources: string[];
448
463
  products: number;
449
464
  merch: number;
@@ -463,9 +478,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
463
478
  search: import("@trpc/server").TRPCQueryProcedure<{
464
479
  input: {
465
480
  text: string;
466
- state?: "all" | "queued" | "scanning" | "upToDate" | "untracked" | undefined;
481
+ freshness?: "live" | "needsSync" | "syncing" | "all" | undefined;
467
482
  sort?: "work" | "newest" | undefined;
468
483
  limit?: number | undefined;
484
+ stage?: "trial" | "dropped" | "graduated" | "untracked" | "all" | undefined;
469
485
  heat?: "unrated" | "hot" | "warm" | "cold" | "all" | undefined;
470
486
  cursor?: {
471
487
  key: [
@@ -478,7 +494,6 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
478
494
  string
479
495
  ];
480
496
  asOf: string;
481
- trialTurn: boolean;
482
497
  sort: "work" | "newest";
483
498
  } | undefined;
484
499
  };
@@ -488,19 +503,22 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
488
503
  lastCheckedAt: string | null;
489
504
  nextCheckAt: string | null;
490
505
  id: string;
491
- queryId: string | null;
506
+ queryId: string;
492
507
  term: string;
493
- status: "queued" | "scanning" | "upToDate" | "untracked";
494
- untrackedReason: "notRelevant" | "noMerchFound" | "notScheduled" | null;
508
+ stage: (typeof keywordStages)[number];
509
+ freshness: (typeof keywordFreshness)[number] | null;
495
510
  retryScheduled: boolean;
496
511
  heat: string;
497
512
  score: number | null;
513
+ searchFrequencyRank: number | null;
498
514
  sources: string[];
499
515
  products: number;
500
516
  merch: number;
501
517
  intervalDays: number | null;
502
518
  }[];
503
519
  nextCursor: {
520
+ sort: "fresh" | "work" | "newest" | "score" | "medianBsr" | "merch";
521
+ asOf: string;
504
522
  key: [
505
523
  number,
506
524
  number,
@@ -510,9 +528,6 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
510
528
  number,
511
529
  string
512
530
  ];
513
- asOf: string;
514
- trialTurn: boolean;
515
- sort: "work" | "newest";
516
531
  } | null;
517
532
  };
518
533
  meta: object;
@@ -529,10 +544,10 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
529
544
  accessError: import("@merchbaseco/access").ServiceAccessErrorCode | null;
530
545
  request: ContextRequest;
531
546
  } | {
532
- user: ClerkUser;
547
+ user: MerchbaseUser;
533
548
  isAdmin: boolean;
534
549
  authType: "access";
535
- credentialKind: CredentialKind;
550
+ credentialKind: "session" | "api_key" | "service_delegated" | "oauth";
536
551
  authExpiresAtMs: number | null;
537
552
  accessPrincipal: RankWranglerServicePrincipal;
538
553
  accessError: null;
@@ -565,7 +580,7 @@ export declare const publicAppRouter: import("@trpc/server").TRPCBuiltRouter<{
565
580
  input: {
566
581
  marketplaceId: string;
567
582
  asin: string;
568
- bucket?: "auto" | "day" | "week" | "month" | undefined;
583
+ bucket?: "week" | "day" | "auto" | "month" | undefined;
569
584
  days?: unknown;
570
585
  endAt?: unknown;
571
586
  limit?: unknown;
@@ -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.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Typed tRPC client for RankWrangler",
5
5
  "repository": {
6
6
  "type": "git",