Initial commit: bface library, build fixes, and refreshed docs

- Externalize all @tamagui/* and tamagui subpaths so dist no longer vendors Tamagui.
- Emit TypeScript declarations with vite-plugin-dts; fix package exports types for ui/*.
- Align initEnv with profiles: displayName, brandLogo, api.baseURL, themeColor, uiShell.
- Stabilize tests with Node localStorage file; env tests pass.
- Update README and component docs for services, menus, API client, and development.
This commit is contained in:
Amer Agovic
2026-04-18 10:43:52 -05:00
commit 94a9f32969
87 changed files with 19750 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
/**
* API Client
* Fetch wrappers for /api/* endpoints with offline support
*/
class APIClient {
constructor(baseURL = '/api') {
this.baseURL = baseURL;
this.interceptors = {
request: [],
response: []
};
}
/**
* Add request interceptor
* @param {Function} interceptor - (config) => config
*/
addRequestInterceptor(interceptor) {
this.interceptors.request.push(interceptor);
}
/**
* Add response interceptor
* @param {Function} interceptor - (response) => response
*/
addResponseInterceptor(interceptor) {
this.interceptors.response.push(interceptor);
}
/**
* Execute request interceptors
* @param {RequestInit} config
* @returns {RequestInit}
*/
_applyRequestInterceptors(config) {
return this.interceptors.request.reduce(
(acc, interceptor) => interceptor(acc),
config
);
}
/**
* Execute response interceptors
* @param {Response} response
* @returns {Response}
*/
_applyResponseInterceptors(response) {
return this.interceptors.response.reduce(
(acc, interceptor) => interceptor(acc),
response
);
}
/**
* Make API request
* @param {string} endpoint
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = this._applyRequestInterceptors({
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
try {
const response = await fetch(url, config);
return this._applyResponseInterceptors(response);
} catch (error) {
// TODO: Implement offline queue management
throw error;
}
}
/**
* GET request
* @param {string} endpoint
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
async get(endpoint, options = {}) {
return this.request(endpoint, { ...options, method: 'GET' });
}
/**
* POST request
* @param {string} endpoint
* @param {any} data
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
async post(endpoint, data, options = {}) {
return this.request(endpoint, {
...options,
method: 'POST',
body: JSON.stringify(data)
});
}
/**
* PUT request
* @param {string} endpoint
* @param {any} data
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
async put(endpoint, data, options = {}) {
return this.request(endpoint, {
...options,
method: 'PUT',
body: JSON.stringify(data)
});
}
/**
* DELETE request
* @param {string} endpoint
* @param {RequestInit} options
* @returns {Promise<Response>}
*/
async delete(endpoint, options = {}) {
return this.request(endpoint, { ...options, method: 'DELETE' });
}
}
// Export singleton instance
export const api = new APIClient();