@bloque/sdk-core 0.0.2 → 0.0.3

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.
@@ -2,7 +2,9 @@ import type { BloqueConfig, RequestOptions } from './types';
2
2
  export declare class HttpClient {
3
3
  private readonly config;
4
4
  private readonly baseUrl;
5
+ private readonly publicRoutes;
5
6
  constructor(config: BloqueConfig);
7
+ private isPublicRoute;
6
8
  private validateConfig;
7
- request<T>(options: RequestOptions): Promise<T>;
9
+ request<T, U = unknown>(options: RequestOptions<U>): Promise<T>;
8
10
  }
package/dist/index.cjs CHANGED
@@ -55,21 +55,46 @@ class BloqueConfigError extends Error {
55
55
  Object.setPrototypeOf(this, BloqueConfigError.prototype);
56
56
  }
57
57
  }
58
+ const createLocalStorageAdapter = ()=>({
59
+ get: ()=>{
60
+ if ('undefined' == typeof localStorage) return null;
61
+ return localStorage.getItem('access_token');
62
+ },
63
+ set: (token)=>{
64
+ if ('undefined' != typeof localStorage) localStorage.setItem('access_token', token);
65
+ },
66
+ clear: ()=>{
67
+ if ('undefined' != typeof localStorage) localStorage.removeItem('access_token');
68
+ }
69
+ });
58
70
  class HttpClient {
59
71
  config;
60
72
  baseUrl;
73
+ publicRoutes = [
74
+ '/api/aliases',
75
+ '/api/origins/*/assert'
76
+ ];
61
77
  constructor(config){
62
78
  this.validateConfig(config);
63
79
  this.config = config;
64
- this.baseUrl = API_BASE_URLS[config.mode];
80
+ this.baseUrl = API_BASE_URLS[config.mode ?? 'production'];
81
+ }
82
+ isPublicRoute(path) {
83
+ const pathWithoutQuery = path.split('?')[0];
84
+ return this.publicRoutes.some((route)=>{
85
+ const pattern = route.replace(/\*/g, '[^/]+');
86
+ const regex = new RegExp(`^${pattern}$`);
87
+ return regex.test(pathWithoutQuery);
88
+ });
65
89
  }
66
90
  validateConfig(config) {
67
91
  if (!config.apiKey || '' === config.apiKey.trim()) throw new BloqueConfigError('API key is required');
68
- if (!config.mode) throw new BloqueConfigError('Mode is required');
92
+ if (!config.mode) config.mode = 'production';
69
93
  if (![
70
94
  'sandbox',
71
95
  'production'
72
96
  ].includes(config.mode)) throw new BloqueConfigError('Mode must be either "sandbox" or "production"');
97
+ if ('client' === config.runtime && !config.tokenStorage) config.tokenStorage = createLocalStorageAdapter();
73
98
  }
74
99
  async request(options) {
75
100
  const { method, path, body, headers = {} } = options;
@@ -79,6 +104,11 @@ class HttpClient {
79
104
  Authorization: this.config.apiKey,
80
105
  ...headers
81
106
  };
107
+ if ('client' === this.config.runtime && !this.isPublicRoute(path)) {
108
+ const token = this.config.tokenStorage?.get();
109
+ if (!token) throw new BloqueConfigError('Authentication token is missing');
110
+ requestHeaders.Authorization = `Bearer ${token}`;
111
+ }
82
112
  try {
83
113
  const response = await fetch(url, {
84
114
  method,
package/dist/index.js CHANGED
@@ -23,21 +23,46 @@ class BloqueConfigError extends Error {
23
23
  Object.setPrototypeOf(this, BloqueConfigError.prototype);
24
24
  }
25
25
  }
26
+ const createLocalStorageAdapter = ()=>({
27
+ get: ()=>{
28
+ if ('undefined' == typeof localStorage) return null;
29
+ return localStorage.getItem('access_token');
30
+ },
31
+ set: (token)=>{
32
+ if ('undefined' != typeof localStorage) localStorage.setItem('access_token', token);
33
+ },
34
+ clear: ()=>{
35
+ if ('undefined' != typeof localStorage) localStorage.removeItem('access_token');
36
+ }
37
+ });
26
38
  class HttpClient {
27
39
  config;
28
40
  baseUrl;
41
+ publicRoutes = [
42
+ '/api/aliases',
43
+ '/api/origins/*/assert'
44
+ ];
29
45
  constructor(config){
30
46
  this.validateConfig(config);
31
47
  this.config = config;
32
- this.baseUrl = API_BASE_URLS[config.mode];
48
+ this.baseUrl = API_BASE_URLS[config.mode ?? 'production'];
49
+ }
50
+ isPublicRoute(path) {
51
+ const pathWithoutQuery = path.split('?')[0];
52
+ return this.publicRoutes.some((route)=>{
53
+ const pattern = route.replace(/\*/g, '[^/]+');
54
+ const regex = new RegExp(`^${pattern}$`);
55
+ return regex.test(pathWithoutQuery);
56
+ });
33
57
  }
34
58
  validateConfig(config) {
35
59
  if (!config.apiKey || '' === config.apiKey.trim()) throw new BloqueConfigError('API key is required');
36
- if (!config.mode) throw new BloqueConfigError('Mode is required');
60
+ if (!config.mode) config.mode = 'production';
37
61
  if (![
38
62
  'sandbox',
39
63
  'production'
40
64
  ].includes(config.mode)) throw new BloqueConfigError('Mode must be either "sandbox" or "production"');
65
+ if ('client' === config.runtime && !config.tokenStorage) config.tokenStorage = createLocalStorageAdapter();
41
66
  }
42
67
  async request(options) {
43
68
  const { method, path, body, headers = {} } = options;
@@ -47,6 +72,11 @@ class HttpClient {
47
72
  Authorization: this.config.apiKey,
48
73
  ...headers
49
74
  };
75
+ if ('client' === this.config.runtime && !this.isPublicRoute(path)) {
76
+ const token = this.config.tokenStorage?.get();
77
+ if (!token) throw new BloqueConfigError('Authentication token is missing');
78
+ requestHeaders.Authorization = `Bearer ${token}`;
79
+ }
50
80
  try {
51
81
  const response = await fetch(url, {
52
82
  method,
package/dist/types.d.ts CHANGED
@@ -1,12 +1,116 @@
1
- export type Mode = 'sandbox' | 'production';
1
+ export type Mode =
2
+ /**
3
+ * Production environment.
4
+ *
5
+ * Uses live endpoints and real data.
6
+ * This is the default mode.
7
+ */
8
+ 'production'
9
+ /**
10
+ * Sandbox environment.
11
+ *
12
+ * Uses isolated endpoints and mock data for development and testing.
13
+ */
14
+ | 'sandbox';
15
+ export type Runtime =
16
+ /**
17
+ * Server runtime (default).
18
+ *
19
+ * Intended for backend environments such as Node.js, Bun,
20
+ * Deno, Edge runtimes, or any server-side execution context.
21
+ *
22
+ * Allows the use of private API keys.
23
+ */
24
+ 'server'
25
+ /**
26
+ * Client runtime.
27
+ *
28
+ * Intended for browsers or frontend runtimes.
29
+ *
30
+ * In the current version of the SDK:
31
+ * - The client authenticates using the `auth` module.
32
+ * - The SDK automatically obtains and manages a JWT.
33
+ * - The JWT is used to authenticate all subsequent requests.
34
+ * - An authenticated client can execute any operation
35
+ * exposed by the API.
36
+ *
37
+ * Authorization and security enforcement are handled
38
+ * exclusively by the backend.
39
+ */
40
+ | 'client';
41
+ /**
42
+ * Interface that defines how the SDK stores and retrieves
43
+ * the JWT token when running in `client` runtime.
44
+ *
45
+ * This allows consumers to customize the storage mechanism
46
+ * (localStorage, sessionStorage, cookies, in-memory, etc.).
47
+ */
48
+ export interface TokenStorage {
49
+ /**
50
+ * Retrieves the currently stored JWT.
51
+ *
52
+ * @returns The JWT token, or `null` if no token is stored.
53
+ */
54
+ get(): string | null;
55
+ /**
56
+ * Persists a JWT token.
57
+ *
58
+ * @param token The JWT token to store.
59
+ */
60
+ set(token: string): void;
61
+ /**
62
+ * Clears the stored JWT token.
63
+ */
64
+ clear(): void;
65
+ }
66
+ /**
67
+ * Main configuration object for the Bloque SDK.
68
+ *
69
+ * This configuration is resolved once when initializing
70
+ * the SDK (via `new SDK()` or `init()` in the modular API).
71
+ */
2
72
  export interface BloqueConfig {
73
+ /**
74
+ * Bloque API key.
75
+ *
76
+ * - In `server` runtime, a private API key is expected.
77
+ * - In `client` runtime, the API key is not used directly;
78
+ * authentication is performed via JWT instead.
79
+ */
3
80
  apiKey: string;
4
- mode: Mode;
81
+ /**
82
+ * SDK operation mode.
83
+ *
84
+ * - `production` (default): production environment.
85
+ * - `sandbox`: sandbox environment.
86
+ *
87
+ * If not specified, the SDK defaults to `production`.
88
+ */
89
+ mode?: Mode;
90
+ /**
91
+ * Runtime environment where the SDK is executed.
92
+ *
93
+ * - `server` (default): backend runtime using API keys.
94
+ * - `client`: frontend runtime using JWT authentication.
95
+ *
96
+ * If not specified, the SDK defaults to `server`.
97
+ */
98
+ runtime?: 'server' | 'client';
99
+ /**
100
+ * JWT token storage strategy.
101
+ *
102
+ * Only applies when `runtime` is set to `client`.
103
+ *
104
+ * By default, the SDK uses `localStorage` to persist
105
+ * the JWT token. This behavior can be overridden to
106
+ * use alternative storage mechanisms.
107
+ */
108
+ tokenStorage?: TokenStorage;
5
109
  }
6
- export interface RequestOptions {
110
+ export interface RequestOptions<U = unknown> {
7
111
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
8
112
  path: string;
9
- body?: unknown;
113
+ body?: U;
10
114
  headers?: Record<string, string>;
11
115
  }
12
116
  export interface BloqueResponse<T> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloque/sdk-core",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Core utilities and types for Bloque SDK.",
5
5
  "type": "module",
6
6
  "keywords": [