@codixus/client 0.1.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.
@@ -0,0 +1,112 @@
1
+ import { DeviceInfo, DeviceMetadata } from '@codixus/shared';
2
+ export { ApiResponse, AuthTokenRequest, AuthTokenResponse, AuthUser, CodixusError, DeviceInfo, DeviceMetadata, ErrorCodes, TokenPair } from '@codixus/shared';
3
+ import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
4
+
5
+ declare class ApiClient {
6
+ private authManager;
7
+ readonly instance: AxiosInstance;
8
+ private refreshPromise;
9
+ private appSecret;
10
+ constructor(baseURL: string, authManager: AuthManager, appSecret?: string);
11
+ get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
12
+ post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
13
+ patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
14
+ delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
15
+ put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
16
+ }
17
+
18
+ interface StorageAdapter {
19
+ getItem(key: string): Promise<string | null> | string | null;
20
+ setItem(key: string, value: string): Promise<void> | void;
21
+ removeItem(key: string): Promise<void> | void;
22
+ }
23
+ interface SignInParams {
24
+ deviceId?: string;
25
+ deviceInfo: DeviceInfo;
26
+ metadata?: DeviceMetadata;
27
+ }
28
+ interface AuthState {
29
+ isAuthenticated: boolean;
30
+ isLoading: boolean;
31
+ user: Record<string, unknown> | null;
32
+ }
33
+ type AuthStateListener = (state: AuthState) => void;
34
+ declare class AuthManager {
35
+ private storage;
36
+ private accessToken;
37
+ private refreshTokenValue;
38
+ private user;
39
+ private listeners;
40
+ private _isLoading;
41
+ private storageKey;
42
+ private api;
43
+ private deviceId;
44
+ constructor(storage: StorageAdapter | null, storageKey: string);
45
+ /** @internal */
46
+ _setApi(api: ApiClient): void;
47
+ init(): Promise<void>;
48
+ signIn(params: SignInParams): Promise<{
49
+ user: Record<string, unknown>;
50
+ isNewUser: boolean;
51
+ }>;
52
+ getToken(): Promise<string | null>;
53
+ forceRefresh(): Promise<string | null>;
54
+ signOut(): Promise<void>;
55
+ getState(): AuthState;
56
+ getDeviceId(): string | null;
57
+ onAuthStateChange(listener: AuthStateListener): () => void;
58
+ /** @internal - used by API interceptor */
59
+ _getAccessToken(): string | null;
60
+ /** @internal - called by API interceptor after refresh */
61
+ _setAccessToken(token: string): void;
62
+ private notify;
63
+ private generateDeviceId;
64
+ }
65
+
66
+ interface FindOptions {
67
+ sort?: Record<string, 1 | -1>;
68
+ limit?: number;
69
+ skip?: number;
70
+ cursor?: string | number;
71
+ }
72
+ declare class Collection<T = Record<string, unknown>> {
73
+ private api;
74
+ private basePath;
75
+ constructor(api: ApiClient, basePath: string);
76
+ find(filter?: Record<string, unknown>, options?: FindOptions): Promise<T[]>;
77
+ findWithCursor(filter?: Record<string, unknown>, options?: FindOptions): Promise<{
78
+ data: T[];
79
+ nextCursor: string | number | null;
80
+ }>;
81
+ findById(id: string): Promise<T | null>;
82
+ create(doc: Partial<T> & Record<string, unknown>): Promise<T>;
83
+ updateById(id: string, update: Record<string, unknown>): Promise<T>;
84
+ deleteById(id: string): Promise<void>;
85
+ count(filter?: Record<string, unknown>): Promise<number>;
86
+ }
87
+
88
+ interface CodixusClientConfig {
89
+ apiUrl: string;
90
+ storageKey?: string;
91
+ storage?: StorageAdapter | null;
92
+ appSecret?: string;
93
+ }
94
+ declare class CodixusClient {
95
+ readonly auth: AuthManager;
96
+ readonly api: ApiClient;
97
+ constructor(config: CodixusClientConfig);
98
+ /**
99
+ * Create a typed collection accessor.
100
+ * Maps to auto REST routes on server: codixus.rest(Model)
101
+ *
102
+ * @example
103
+ * interface Product { _id: string; name: string; price: number; }
104
+ * const products = codixus.collection<Product>("products");
105
+ * const all = await products.find(); // Product[]
106
+ * const one = await products.findById("abc"); // Product | null
107
+ */
108
+ collection<T>(name: string): Collection<T>;
109
+ init(): Promise<void>;
110
+ }
111
+
112
+ export { ApiClient, type AuthState, CodixusClient, type CodixusClientConfig, Collection, type FindOptions, type SignInParams, type StorageAdapter };
package/dist/index.js ADDED
@@ -0,0 +1,413 @@
1
+ // src/auth/auth-manager.ts
2
+ var AuthManager = class {
3
+ constructor(storage, storageKey) {
4
+ this.storage = storage;
5
+ this.storageKey = storageKey;
6
+ }
7
+ accessToken = null;
8
+ refreshTokenValue = null;
9
+ user = null;
10
+ listeners = /* @__PURE__ */ new Set();
11
+ _isLoading = true;
12
+ storageKey;
13
+ api;
14
+ deviceId = null;
15
+ /** @internal */
16
+ _setApi(api) {
17
+ this.api = api;
18
+ }
19
+ async init() {
20
+ if (!this.storage) {
21
+ this._isLoading = false;
22
+ return;
23
+ }
24
+ try {
25
+ const storedRefresh = await this.storage.getItem(
26
+ `${this.storageKey}_refresh_token`
27
+ );
28
+ const storedUser = await this.storage.getItem(
29
+ `${this.storageKey}_user`
30
+ );
31
+ const storedDeviceId = await this.storage.getItem(
32
+ `${this.storageKey}_device_id`
33
+ );
34
+ if (storedRefresh) {
35
+ this.refreshTokenValue = storedRefresh;
36
+ }
37
+ if (storedUser) {
38
+ try {
39
+ this.user = JSON.parse(storedUser);
40
+ } catch {
41
+ }
42
+ }
43
+ if (storedDeviceId) {
44
+ this.deviceId = storedDeviceId;
45
+ }
46
+ } catch (error) {
47
+ console.warn("[CodixusClient] Failed to restore auth state from storage:", error);
48
+ }
49
+ this._isLoading = false;
50
+ this.notify();
51
+ }
52
+ async signIn(params) {
53
+ const deviceId = params.deviceId ?? this.deviceId ?? this.generateDeviceId();
54
+ const response = await this.api.post("/auth/token", {
55
+ deviceId,
56
+ deviceInfo: params.deviceInfo,
57
+ metadata: params.metadata
58
+ });
59
+ const data = response.data;
60
+ this.accessToken = data.accessToken;
61
+ this.refreshTokenValue = data.refreshToken;
62
+ this.user = data.user;
63
+ this.deviceId = deviceId;
64
+ if (this.storage) {
65
+ await this.storage.setItem(
66
+ `${this.storageKey}_refresh_token`,
67
+ data.refreshToken
68
+ );
69
+ await this.storage.setItem(
70
+ `${this.storageKey}_user`,
71
+ JSON.stringify(data.user)
72
+ );
73
+ await this.storage.setItem(`${this.storageKey}_device_id`, deviceId);
74
+ }
75
+ this.notify();
76
+ return { user: data.user, isNewUser: data.isNewUser };
77
+ }
78
+ async getToken() {
79
+ if (this.accessToken) return this.accessToken;
80
+ if (this.refreshTokenValue) {
81
+ try {
82
+ await this.forceRefresh();
83
+ return this.accessToken;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ async forceRefresh() {
91
+ if (!this.refreshTokenValue) return null;
92
+ try {
93
+ const response = await this.api.post("/auth/refresh", {
94
+ refreshToken: this.refreshTokenValue
95
+ });
96
+ this.accessToken = response.data.accessToken;
97
+ this.refreshTokenValue = response.data.refreshToken;
98
+ if (this.storage) {
99
+ await this.storage.setItem(
100
+ `${this.storageKey}_refresh_token`,
101
+ response.data.refreshToken
102
+ );
103
+ }
104
+ return this.accessToken;
105
+ } catch {
106
+ await this.signOut();
107
+ return null;
108
+ }
109
+ }
110
+ async signOut() {
111
+ if (this.refreshTokenValue) {
112
+ try {
113
+ await this.api.post("/auth/logout", {
114
+ refreshToken: this.refreshTokenValue
115
+ });
116
+ } catch {
117
+ }
118
+ }
119
+ this.accessToken = null;
120
+ this.refreshTokenValue = null;
121
+ this.user = null;
122
+ if (this.storage) {
123
+ await this.storage.removeItem(`${this.storageKey}_refresh_token`);
124
+ await this.storage.removeItem(`${this.storageKey}_user`);
125
+ await this.storage.removeItem(`${this.storageKey}_device_id`);
126
+ }
127
+ this.notify();
128
+ }
129
+ getState() {
130
+ return {
131
+ isAuthenticated: this.accessToken !== null || this.refreshTokenValue !== null,
132
+ isLoading: this._isLoading,
133
+ user: this.user
134
+ };
135
+ }
136
+ getDeviceId() {
137
+ return this.deviceId;
138
+ }
139
+ onAuthStateChange(listener) {
140
+ this.listeners.add(listener);
141
+ listener(this.getState());
142
+ return () => {
143
+ this.listeners.delete(listener);
144
+ };
145
+ }
146
+ /** @internal - used by API interceptor */
147
+ _getAccessToken() {
148
+ return this.accessToken;
149
+ }
150
+ /** @internal - called by API interceptor after refresh */
151
+ _setAccessToken(token) {
152
+ this.accessToken = token;
153
+ }
154
+ notify() {
155
+ const state = this.getState();
156
+ for (const listener of this.listeners) {
157
+ listener(state);
158
+ }
159
+ }
160
+ generateDeviceId() {
161
+ const bytes = new Uint8Array(16);
162
+ crypto.getRandomValues(bytes);
163
+ return "dev-" + Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
164
+ }
165
+ };
166
+
167
+ // src/api/api-client.ts
168
+ import axios from "axios";
169
+ async function sha256(data) {
170
+ if (typeof globalThis.crypto?.subtle !== "undefined") {
171
+ const encoded = new TextEncoder().encode(data);
172
+ const hash = await globalThis.crypto.subtle.digest("SHA-256", encoded);
173
+ return Array.from(
174
+ new Uint8Array(hash),
175
+ (b) => b.toString(16).padStart(2, "0")
176
+ ).join("");
177
+ }
178
+ return simpleHash(data);
179
+ }
180
+ async function hmacSha256(secret, data) {
181
+ if (typeof globalThis.crypto?.subtle !== "undefined") {
182
+ const key = await globalThis.crypto.subtle.importKey(
183
+ "raw",
184
+ new TextEncoder().encode(secret),
185
+ { name: "HMAC", hash: "SHA-256" },
186
+ false,
187
+ ["sign"]
188
+ );
189
+ const sig = await globalThis.crypto.subtle.sign(
190
+ "HMAC",
191
+ key,
192
+ new TextEncoder().encode(data)
193
+ );
194
+ return Array.from(
195
+ new Uint8Array(sig),
196
+ (b) => b.toString(16).padStart(2, "0")
197
+ ).join("");
198
+ }
199
+ return simpleHmac(secret, data);
200
+ }
201
+ function simpleHash(str) {
202
+ let hash = 0;
203
+ for (let i = 0; i < str.length; i++) {
204
+ const char = str.charCodeAt(i);
205
+ hash = (hash << 5) - hash + char;
206
+ hash |= 0;
207
+ }
208
+ return Math.abs(hash).toString(16).padStart(16, "0");
209
+ }
210
+ function simpleHmac(key, data) {
211
+ return simpleHash(key + ":" + data);
212
+ }
213
+ function generateNonce() {
214
+ if (typeof globalThis.crypto?.randomUUID === "function") {
215
+ return globalThis.crypto.randomUUID();
216
+ }
217
+ const bytes = new Uint8Array(16);
218
+ if (typeof globalThis.crypto?.getRandomValues === "function") {
219
+ globalThis.crypto.getRandomValues(bytes);
220
+ } else {
221
+ for (let i = 0; i < 16; i++) {
222
+ bytes[i] = Math.floor(Math.random() * 256);
223
+ }
224
+ }
225
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
226
+ }
227
+ var ApiClient = class {
228
+ constructor(baseURL, authManager, appSecret) {
229
+ this.authManager = authManager;
230
+ this.appSecret = appSecret;
231
+ this.instance = axios.create({
232
+ baseURL,
233
+ headers: { "Content-Type": "application/json" }
234
+ });
235
+ this.instance.interceptors.request.use(async (config) => {
236
+ if (!config.url?.includes("/auth/")) {
237
+ const token = this.authManager._getAccessToken();
238
+ if (token) {
239
+ config.headers.Authorization = `Bearer ${token}`;
240
+ }
241
+ }
242
+ if (this.appSecret) {
243
+ const timestamp = Date.now().toString();
244
+ const nonce = generateNonce();
245
+ const body = config.data && typeof config.data === "object" ? JSON.stringify(config.data) : config.data ?? "";
246
+ const bodyHash = body ? await sha256(body) : "";
247
+ const method = (config.method ?? "GET").toUpperCase();
248
+ const url = config.url ?? "/";
249
+ const payload = [method, url, timestamp, nonce, bodyHash].join("\n");
250
+ const signature = await hmacSha256(this.appSecret, payload);
251
+ config.headers["X-Codixus-Timestamp"] = timestamp;
252
+ config.headers["X-Codixus-Nonce"] = nonce;
253
+ config.headers["X-Codixus-Signature"] = signature;
254
+ }
255
+ return config;
256
+ });
257
+ this.instance.interceptors.response.use(
258
+ (response) => response,
259
+ async (error) => {
260
+ if (error.response?.status === 401) {
261
+ const originalRequest = error.config;
262
+ if (originalRequest && !originalRequest._retry) {
263
+ originalRequest._retry = true;
264
+ try {
265
+ if (!this.refreshPromise) {
266
+ this.refreshPromise = this.authManager.forceRefresh().finally(() => {
267
+ this.refreshPromise = null;
268
+ });
269
+ }
270
+ const newToken = await this.refreshPromise;
271
+ if (newToken) {
272
+ originalRequest.headers.Authorization = `Bearer ${newToken}`;
273
+ return this.instance(originalRequest);
274
+ }
275
+ } catch (refreshError) {
276
+ console.warn("[CodixusClient] Token refresh failed:", refreshError);
277
+ }
278
+ }
279
+ }
280
+ return Promise.reject(error);
281
+ }
282
+ );
283
+ }
284
+ instance;
285
+ refreshPromise = null;
286
+ appSecret;
287
+ async get(url, config) {
288
+ return this.instance.get(url, config);
289
+ }
290
+ async post(url, data, config) {
291
+ return this.instance.post(url, data, config);
292
+ }
293
+ async patch(url, data, config) {
294
+ return this.instance.patch(url, data, config);
295
+ }
296
+ async delete(url, config) {
297
+ return this.instance.delete(url, config);
298
+ }
299
+ async put(url, data, config) {
300
+ return this.instance.put(url, data, config);
301
+ }
302
+ };
303
+
304
+ // src/db/collection.ts
305
+ var Collection = class {
306
+ constructor(api, basePath) {
307
+ this.api = api;
308
+ this.basePath = basePath;
309
+ }
310
+ async find(filter, options) {
311
+ const params = new URLSearchParams();
312
+ if (filter && Object.keys(filter).length > 0) {
313
+ params.set("filter", JSON.stringify(filter));
314
+ }
315
+ if (options?.sort) params.set("sort", JSON.stringify(options.sort));
316
+ if (options?.limit) params.set("limit", String(options.limit));
317
+ if (options?.skip) params.set("skip", String(options.skip));
318
+ if (options?.cursor) params.set("cursor", String(options.cursor));
319
+ const qs = params.toString();
320
+ const url = qs ? `${this.basePath}?${qs}` : this.basePath;
321
+ const { data } = await this.api.get(url);
322
+ return data.data;
323
+ }
324
+ async findWithCursor(filter, options) {
325
+ const params = new URLSearchParams();
326
+ if (filter && Object.keys(filter).length > 0) {
327
+ params.set("filter", JSON.stringify(filter));
328
+ }
329
+ if (options?.sort) params.set("sort", JSON.stringify(options.sort));
330
+ if (options?.limit) params.set("limit", String(options.limit));
331
+ if (options?.cursor) params.set("cursor", String(options.cursor));
332
+ const qs = params.toString();
333
+ const url = qs ? `${this.basePath}?${qs}` : this.basePath;
334
+ const { data } = await this.api.get(url);
335
+ return { data: data.data, nextCursor: data.nextCursor };
336
+ }
337
+ async findById(id) {
338
+ try {
339
+ const { data } = await this.api.get(
340
+ `${this.basePath}/${id}`
341
+ );
342
+ return data.data;
343
+ } catch (error) {
344
+ if (error.response?.status === 404) return null;
345
+ throw error;
346
+ }
347
+ }
348
+ async create(doc) {
349
+ const { data } = await this.api.post(
350
+ this.basePath,
351
+ doc
352
+ );
353
+ return data.data;
354
+ }
355
+ async updateById(id, update) {
356
+ const { data } = await this.api.patch(
357
+ `${this.basePath}/${id}`,
358
+ { update }
359
+ );
360
+ return data.data;
361
+ }
362
+ async deleteById(id) {
363
+ await this.api.delete(`${this.basePath}/${id}`);
364
+ }
365
+ async count(filter) {
366
+ const { data } = await this.api.post(
367
+ `${this.basePath}/count`,
368
+ { filter: filter ?? {} }
369
+ );
370
+ return data.count;
371
+ }
372
+ };
373
+
374
+ // src/index.ts
375
+ import {
376
+ CodixusError,
377
+ ErrorCodes
378
+ } from "@codixus/shared";
379
+ var CodixusClient = class {
380
+ auth;
381
+ api;
382
+ constructor(config) {
383
+ const storageKey = config.storageKey ?? "codixus";
384
+ const storage = config.storage ?? null;
385
+ this.auth = new AuthManager(storage, storageKey);
386
+ this.api = new ApiClient(config.apiUrl, this.auth, config.appSecret);
387
+ this.auth._setApi(this.api);
388
+ }
389
+ /**
390
+ * Create a typed collection accessor.
391
+ * Maps to auto REST routes on server: codixus.rest(Model)
392
+ *
393
+ * @example
394
+ * interface Product { _id: string; name: string; price: number; }
395
+ * const products = codixus.collection<Product>("products");
396
+ * const all = await products.find(); // Product[]
397
+ * const one = await products.findById("abc"); // Product | null
398
+ */
399
+ collection(name) {
400
+ return new Collection(this.api, `/api/${name}`);
401
+ }
402
+ async init() {
403
+ await this.auth.init();
404
+ }
405
+ };
406
+ export {
407
+ ApiClient,
408
+ CodixusClient,
409
+ CodixusError,
410
+ Collection,
411
+ ErrorCodes
412
+ };
413
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/auth-manager.ts","../src/api/api-client.ts","../src/db/collection.ts","../src/index.ts"],"sourcesContent":["import type {\n AuthTokenResponse,\n DeviceInfo,\n DeviceMetadata,\n} from \"@codixus/shared\";\nimport type { ApiClient } from \"../api/api-client.js\";\n\nexport interface StorageAdapter {\n getItem(key: string): Promise<string | null> | string | null;\n setItem(key: string, value: string): Promise<void> | void;\n removeItem(key: string): Promise<void> | void;\n}\n\nexport interface SignInParams {\n deviceId?: string;\n deviceInfo: DeviceInfo;\n metadata?: DeviceMetadata;\n}\n\nexport interface AuthState {\n isAuthenticated: boolean;\n isLoading: boolean;\n user: Record<string, unknown> | null;\n}\n\ntype AuthStateListener = (state: AuthState) => void;\n\nexport class AuthManager {\n private accessToken: string | null = null;\n private refreshTokenValue: string | null = null;\n private user: Record<string, unknown> | null = null;\n private listeners: Set<AuthStateListener> = new Set();\n private _isLoading = true;\n private storageKey: string;\n private api!: ApiClient;\n private deviceId: string | null = null;\n\n constructor(\n private storage: StorageAdapter | null,\n storageKey: string,\n ) {\n this.storageKey = storageKey;\n }\n\n /** @internal */\n _setApi(api: ApiClient): void {\n this.api = api;\n }\n\n async init(): Promise<void> {\n if (!this.storage) {\n this._isLoading = false;\n return;\n }\n\n try {\n const storedRefresh = await this.storage.getItem(\n `${this.storageKey}_refresh_token`,\n );\n const storedUser = await this.storage.getItem(\n `${this.storageKey}_user`,\n );\n const storedDeviceId = await this.storage.getItem(\n `${this.storageKey}_device_id`,\n );\n\n if (storedRefresh) {\n this.refreshTokenValue = storedRefresh;\n }\n if (storedUser) {\n try {\n this.user = JSON.parse(storedUser);\n } catch {\n // Corrupted user data in storage, ignore\n }\n }\n if (storedDeviceId) {\n this.deviceId = storedDeviceId;\n }\n } catch (error) {\n console.warn(\"[CodixusClient] Failed to restore auth state from storage:\", error);\n }\n\n this._isLoading = false;\n this.notify();\n }\n\n async signIn(\n params: SignInParams,\n ): Promise<{ user: Record<string, unknown>; isNewUser: boolean }> {\n const deviceId = params.deviceId ?? this.deviceId ?? this.generateDeviceId();\n\n const response = await this.api.post<AuthTokenResponse>(\"/auth/token\", {\n deviceId,\n deviceInfo: params.deviceInfo,\n metadata: params.metadata,\n });\n\n const data = response.data;\n this.accessToken = data.accessToken;\n this.refreshTokenValue = data.refreshToken;\n this.user = data.user;\n this.deviceId = deviceId;\n\n // Persist\n if (this.storage) {\n await this.storage.setItem(\n `${this.storageKey}_refresh_token`,\n data.refreshToken,\n );\n await this.storage.setItem(\n `${this.storageKey}_user`,\n JSON.stringify(data.user),\n );\n await this.storage.setItem(`${this.storageKey}_device_id`, deviceId);\n }\n\n this.notify();\n return { user: data.user, isNewUser: data.isNewUser };\n }\n\n async getToken(): Promise<string | null> {\n if (this.accessToken) return this.accessToken;\n\n // Try refresh\n if (this.refreshTokenValue) {\n try {\n await this.forceRefresh();\n return this.accessToken;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async forceRefresh(): Promise<string | null> {\n if (!this.refreshTokenValue) return null;\n\n try {\n const response = await this.api.post<{\n success: boolean;\n accessToken: string;\n refreshToken: string;\n }>(\"/auth/refresh\", {\n refreshToken: this.refreshTokenValue,\n });\n\n this.accessToken = response.data.accessToken;\n this.refreshTokenValue = response.data.refreshToken;\n\n if (this.storage) {\n await this.storage.setItem(\n `${this.storageKey}_refresh_token`,\n response.data.refreshToken,\n );\n }\n\n return this.accessToken;\n } catch {\n // Refresh failed, clear auth state\n await this.signOut();\n return null;\n }\n }\n\n async signOut(): Promise<void> {\n // Notify server\n if (this.refreshTokenValue) {\n try {\n await this.api.post(\"/auth/logout\", {\n refreshToken: this.refreshTokenValue,\n });\n } catch {}\n }\n\n this.accessToken = null;\n this.refreshTokenValue = null;\n this.user = null;\n\n if (this.storage) {\n await this.storage.removeItem(`${this.storageKey}_refresh_token`);\n await this.storage.removeItem(`${this.storageKey}_user`);\n await this.storage.removeItem(`${this.storageKey}_device_id`);\n }\n\n this.notify();\n }\n\n getState(): AuthState {\n return {\n isAuthenticated: this.accessToken !== null || this.refreshTokenValue !== null,\n isLoading: this._isLoading,\n user: this.user,\n };\n }\n\n getDeviceId(): string | null {\n return this.deviceId;\n }\n\n onAuthStateChange(listener: AuthStateListener): () => void {\n this.listeners.add(listener);\n // Call immediately with current state\n listener(this.getState());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** @internal - used by API interceptor */\n _getAccessToken(): string | null {\n return this.accessToken;\n }\n\n /** @internal - called by API interceptor after refresh */\n _setAccessToken(token: string): void {\n this.accessToken = token;\n }\n\n private notify(): void {\n const state = this.getState();\n for (const listener of this.listeners) {\n listener(state);\n }\n }\n\n private generateDeviceId(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return \"dev-\" + Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n }\n}\n","import axios, {\n type AxiosInstance,\n type AxiosRequestConfig,\n type AxiosResponse,\n} from \"axios\";\nimport type { AuthManager } from \"../auth/auth-manager.js\";\n\n// Lightweight crypto helpers that work in both browser and React Native\nasync function sha256(data: string): Promise<string> {\n if (typeof globalThis.crypto?.subtle !== \"undefined\") {\n const encoded = new TextEncoder().encode(data);\n const hash = await globalThis.crypto.subtle.digest(\"SHA-256\", encoded);\n return Array.from(new Uint8Array(hash), (b) =>\n b.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n }\n // Fallback: simple hash (React Native without crypto.subtle)\n return simpleHash(data);\n}\n\nasync function hmacSha256(secret: string, data: string): Promise<string> {\n if (typeof globalThis.crypto?.subtle !== \"undefined\") {\n const key = await globalThis.crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await globalThis.crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(data),\n );\n return Array.from(new Uint8Array(sig), (b) =>\n b.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n }\n // Fallback for environments without SubtleCrypto\n return simpleHmac(secret, data);\n}\n\n// Simple fallback hash for environments without SubtleCrypto\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash |= 0;\n }\n return Math.abs(hash).toString(16).padStart(16, \"0\");\n}\n\nfunction simpleHmac(key: string, data: string): string {\n return simpleHash(key + \":\" + data);\n}\n\nfunction generateNonce(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback\n const bytes = new Uint8Array(16);\n if (typeof globalThis.crypto?.getRandomValues === \"function\") {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport class ApiClient {\n readonly instance: AxiosInstance;\n private refreshPromise: Promise<string | null> | null = null;\n private appSecret: string | undefined;\n\n constructor(\n baseURL: string,\n private authManager: AuthManager,\n appSecret?: string,\n ) {\n this.appSecret = appSecret;\n\n this.instance = axios.create({\n baseURL,\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n // Request interceptor: add Bearer token + signing\n this.instance.interceptors.request.use(async (config) => {\n // Skip auth for auth endpoints\n if (!config.url?.includes(\"/auth/\")) {\n const token = this.authManager._getAccessToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n }\n\n // Request signing\n if (this.appSecret) {\n const timestamp = Date.now().toString();\n const nonce = generateNonce();\n const body =\n config.data && typeof config.data === \"object\"\n ? JSON.stringify(config.data)\n : (config.data ?? \"\");\n const bodyHash = body ? await sha256(body) : \"\";\n const method = (config.method ?? \"GET\").toUpperCase();\n const url = config.url ?? \"/\";\n const payload = [method, url, timestamp, nonce, bodyHash].join(\"\\n\");\n const signature = await hmacSha256(this.appSecret, payload);\n\n config.headers[\"X-Codixus-Timestamp\"] = timestamp;\n config.headers[\"X-Codixus-Nonce\"] = nonce;\n config.headers[\"X-Codixus-Signature\"] = signature;\n }\n\n return config;\n });\n\n // Response interceptor: handle 401 with token refresh\n this.instance.interceptors.response.use(\n (response) => response,\n async (error) => {\n if (error.response?.status === 401) {\n const originalRequest = error.config;\n if (originalRequest && !originalRequest._retry) {\n originalRequest._retry = true;\n\n try {\n // Promise lock to prevent concurrent refreshes\n if (!this.refreshPromise) {\n this.refreshPromise = this.authManager\n .forceRefresh()\n .finally(() => {\n this.refreshPromise = null;\n });\n }\n\n const newToken = await this.refreshPromise;\n if (newToken) {\n originalRequest.headers.Authorization = `Bearer ${newToken}`;\n return this.instance(originalRequest);\n }\n } catch (refreshError) {\n console.warn(\"[CodixusClient] Token refresh failed:\", refreshError);\n }\n }\n }\n return Promise.reject(error);\n },\n );\n }\n\n async get<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse<T>> {\n return this.instance.get<T>(url, config);\n }\n\n async post<T = unknown>(\n url: string,\n data?: unknown,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse<T>> {\n return this.instance.post<T>(url, data, config);\n }\n\n async patch<T = unknown>(\n url: string,\n data?: unknown,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse<T>> {\n return this.instance.patch<T>(url, data, config);\n }\n\n async delete<T = unknown>(\n url: string,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse<T>> {\n return this.instance.delete<T>(url, config);\n }\n\n async put<T = unknown>(\n url: string,\n data?: unknown,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse<T>> {\n return this.instance.put<T>(url, data, config);\n }\n}\n","import type { ApiClient } from \"../api/api-client.js\";\n\nexport interface FindOptions {\n sort?: Record<string, 1 | -1>;\n limit?: number;\n skip?: number;\n cursor?: string | number;\n}\n\nexport class Collection<T = Record<string, unknown>> {\n constructor(\n private api: ApiClient,\n private basePath: string,\n ) {}\n\n async find(\n filter?: Record<string, unknown>,\n options?: FindOptions,\n ): Promise<T[]> {\n const params = new URLSearchParams();\n if (filter && Object.keys(filter).length > 0) {\n params.set(\"filter\", JSON.stringify(filter));\n }\n if (options?.sort) params.set(\"sort\", JSON.stringify(options.sort));\n if (options?.limit) params.set(\"limit\", String(options.limit));\n if (options?.skip) params.set(\"skip\", String(options.skip));\n if (options?.cursor) params.set(\"cursor\", String(options.cursor));\n\n const qs = params.toString();\n const url = qs ? `${this.basePath}?${qs}` : this.basePath;\n const { data } = await this.api.get<{ success: boolean; data: T[]; nextCursor?: string | null }>(url);\n return data.data;\n }\n\n async findWithCursor(\n filter?: Record<string, unknown>,\n options?: FindOptions,\n ): Promise<{ data: T[]; nextCursor: string | number | null }> {\n const params = new URLSearchParams();\n if (filter && Object.keys(filter).length > 0) {\n params.set(\"filter\", JSON.stringify(filter));\n }\n if (options?.sort) params.set(\"sort\", JSON.stringify(options.sort));\n if (options?.limit) params.set(\"limit\", String(options.limit));\n if (options?.cursor) params.set(\"cursor\", String(options.cursor));\n\n const qs = params.toString();\n const url = qs ? `${this.basePath}?${qs}` : this.basePath;\n const { data } = await this.api.get<{ success: boolean; data: T[]; nextCursor: string | number | null }>(url);\n return { data: data.data, nextCursor: data.nextCursor };\n }\n\n async findById(id: string): Promise<T | null> {\n try {\n const { data } = await this.api.get<{ success: boolean; data: T }>(\n `${this.basePath}/${id}`,\n );\n return data.data;\n } catch (error: any) {\n if (error.response?.status === 404) return null;\n throw error;\n }\n }\n\n async create(doc: Partial<T> & Record<string, unknown>): Promise<T> {\n const { data } = await this.api.post<{ success: boolean; data: T }>(\n this.basePath,\n doc,\n );\n return data.data;\n }\n\n async updateById(\n id: string,\n update: Record<string, unknown>,\n ): Promise<T> {\n const { data } = await this.api.patch<{ success: boolean; data: T }>(\n `${this.basePath}/${id}`,\n { update },\n );\n return data.data;\n }\n\n async deleteById(id: string): Promise<void> {\n await this.api.delete(`${this.basePath}/${id}`);\n }\n\n async count(filter?: Record<string, unknown>): Promise<number> {\n const { data } = await this.api.post<{ success: boolean; count: number }>(\n `${this.basePath}/count`,\n { filter: filter ?? {} },\n );\n return data.count;\n }\n}\n","import {\n AuthManager,\n type StorageAdapter,\n type SignInParams,\n type AuthState,\n} from \"./auth/auth-manager.js\";\nimport { ApiClient } from \"./api/api-client.js\";\nimport { Collection, type FindOptions } from \"./db/collection.js\";\n\nexport interface CodixusClientConfig {\n apiUrl: string;\n storageKey?: string;\n storage?: StorageAdapter | null;\n appSecret?: string;\n}\n\nexport class CodixusClient {\n readonly auth: AuthManager;\n readonly api: ApiClient;\n\n constructor(config: CodixusClientConfig) {\n const storageKey = config.storageKey ?? \"codixus\";\n const storage = config.storage ?? null;\n\n this.auth = new AuthManager(storage, storageKey);\n this.api = new ApiClient(config.apiUrl, this.auth, config.appSecret);\n this.auth._setApi(this.api);\n }\n\n /**\n * Create a typed collection accessor.\n * Maps to auto REST routes on server: codixus.rest(Model)\n *\n * @example\n * interface Product { _id: string; name: string; price: number; }\n * const products = codixus.collection<Product>(\"products\");\n * const all = await products.find(); // Product[]\n * const one = await products.findById(\"abc\"); // Product | null\n */\n collection<T>(name: string): Collection<T> {\n return new Collection<T>(this.api, `/api/${name}`);\n }\n\n async init(): Promise<void> {\n await this.auth.init();\n }\n}\n\nexport {\n type StorageAdapter,\n type SignInParams,\n type AuthState,\n} from \"./auth/auth-manager.js\";\nexport { ApiClient } from \"./api/api-client.js\";\nexport { Collection, type FindOptions } from \"./db/collection.js\";\n\n// Re-export shared types\nexport {\n type AuthUser,\n type TokenPair,\n type DeviceInfo,\n type DeviceMetadata,\n type AuthTokenRequest,\n type AuthTokenResponse,\n type ApiResponse,\n CodixusError,\n ErrorCodes,\n} from \"@codixus/shared\";\n"],"mappings":"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","names":[]}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react/index.ts
21
+ var react_exports = {};
22
+ __export(react_exports, {
23
+ CodixusProvider: () => CodixusProvider,
24
+ useAuth: () => useAuth,
25
+ useCodixus: () => useCodixus
26
+ });
27
+ module.exports = __toCommonJS(react_exports);
28
+
29
+ // src/react/provider.tsx
30
+ var import_react = require("react");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ var CodixusContext = (0, import_react.createContext)(null);
33
+ function CodixusProvider({
34
+ client,
35
+ children
36
+ }) {
37
+ const [authState, setAuthState] = (0, import_react.useState)({
38
+ isAuthenticated: false,
39
+ isLoading: true,
40
+ user: null
41
+ });
42
+ (0, import_react.useEffect)(() => {
43
+ client.init();
44
+ const unsubscribe = client.auth.onAuthStateChange(setAuthState);
45
+ return unsubscribe;
46
+ }, [client]);
47
+ const value = {
48
+ client,
49
+ auth: {
50
+ ...authState,
51
+ signIn: (params) => client.auth.signIn(params),
52
+ signOut: () => client.auth.signOut()
53
+ },
54
+ api: client.api
55
+ };
56
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CodixusContext.Provider, { value, children });
57
+ }
58
+ function useCodixus() {
59
+ const ctx = (0, import_react.useContext)(CodixusContext);
60
+ if (!ctx) {
61
+ throw new Error("useCodixus must be used within a CodixusProvider");
62
+ }
63
+ return ctx;
64
+ }
65
+ function useAuth() {
66
+ const { auth } = useCodixus();
67
+ return auth;
68
+ }
69
+ // Annotate the CommonJS export names for ESM import in node:
70
+ 0 && (module.exports = {
71
+ CodixusProvider,
72
+ useAuth,
73
+ useCodixus
74
+ });
75
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/provider.tsx"],"sourcesContent":["export { CodixusProvider, useCodixus, useAuth } from \"./provider.js\";\n","import {\n createContext,\n useContext,\n useState,\n useEffect,\n type ReactNode,\n} from \"react\";\nimport type { CodixusClient } from \"../index.js\";\nimport type { AuthState, SignInParams } from \"../auth/auth-manager.js\";\n\ninterface CodixusContextValue {\n client: CodixusClient;\n auth: AuthState & {\n signIn: (params: SignInParams) => Promise<{ user: Record<string, unknown>; isNewUser: boolean }>;\n signOut: () => Promise<void>;\n };\n api: CodixusClient[\"api\"];\n}\n\nconst CodixusContext = createContext<CodixusContextValue | null>(null);\n\nexport function CodixusProvider({\n client,\n children,\n}: {\n client: CodixusClient;\n children: ReactNode;\n}) {\n const [authState, setAuthState] = useState<AuthState>({\n isAuthenticated: false,\n isLoading: true,\n user: null,\n });\n\n useEffect(() => {\n client.init();\n const unsubscribe = client.auth.onAuthStateChange(setAuthState);\n return unsubscribe;\n }, [client]);\n\n const value: CodixusContextValue = {\n client,\n auth: {\n ...authState,\n signIn: (params) => client.auth.signIn(params),\n signOut: () => client.auth.signOut(),\n },\n api: client.api,\n };\n\n return (\n <CodixusContext.Provider value={value}>{children}</CodixusContext.Provider>\n );\n}\n\nexport function useCodixus(): CodixusContextValue {\n const ctx = useContext(CodixusContext);\n if (!ctx) {\n throw new Error(\"useCodixus must be used within a CodixusProvider\");\n }\n return ctx;\n}\n\nexport function useAuth() {\n const { auth } = useCodixus();\n return auth;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAMO;AA6CH;AAhCJ,IAAM,qBAAiB,4BAA0C,IAAI;AAE9D,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAoB;AAAA,IACpD,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,MAAM;AAAA,EACR,CAAC;AAED,8BAAU,MAAM;AACd,WAAO,KAAK;AACZ,UAAM,cAAc,OAAO,KAAK,kBAAkB,YAAY;AAC9D,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAA6B;AAAA,IACjC;AAAA,IACA,MAAM;AAAA,MACJ,GAAG;AAAA,MACH,QAAQ,CAAC,WAAW,OAAO,KAAK,OAAO,MAAM;AAAA,MAC7C,SAAS,MAAM,OAAO,KAAK,QAAQ;AAAA,IACrC;AAAA,IACA,KAAK,OAAO;AAAA,EACd;AAEA,SACE,4CAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAErD;AAEO,SAAS,aAAkC;AAChD,QAAM,UAAM,yBAAW,cAAc;AACrC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,SAAO;AACT;AAEO,SAAS,UAAU;AACxB,QAAM,EAAE,KAAK,IAAI,WAAW;AAC5B,SAAO;AACT;","names":[]}