{"_id":"@dloizides/tenant-theme-web","name":"@dloizides/tenant-theme-web","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.1":{"name":"@dloizides/tenant-theme-web","version":"1.0.1","description":"Product-agnostic RN-web tenant-theme client for the dloizides.com portfolio: the ETag-conditional theme fetch, the DTO -> TenantThemeConfig mapper, the localStorage ETag cache, and the save-request body mapper that erevna-web and katalogos-web shared byte","keywords":["tenant-theme","theme","etag","cache","multitenant","dloizides"],"author":{"name":"dloizides"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/openmindednewby/tenant-theme-web.git"},"homepage":"https://github.com/openmindednewby/tenant-theme-web#readme","bugs":{"url":"https://github.com/openmindednewby/tenant-theme-web/issues"},"main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"}}},"sideEffects":false,"engines":{"node":">=18.0.0"},"scripts":{"build":"rimraf dist && tsup","build:watch":"tsup --watch","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src --ext .ts","lint:fix":"eslint src --ext .ts --fix","typecheck":"tsc --noEmit","clean":"rimraf dist","security:audit":"npm audit --audit-level=high","deps:outdated":"npm outdated || exit 0","deps:unused":"npx depcheck --ignores \"@types/jest,@types/node,rimraf\"","deps:licenses":"npx license-checker --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD;Unlicense;CC0-1.0\"","deps:health":"npm run deps:outdated && npm run deps:unused","prepublishOnly":"npm run clean && npm run build && npm run test"},"peerDependencies":{"@dloizides/utils":"^1.0.0"},"devDependencies":{"@dloizides/utils":"^1.0.2","@types/jest":"^29.5.0","@types/node":"^20.19.32","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0","eslint":"^8.57.0","eslint-plugin-sonarjs":"^4.0.3","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","rimraf":"^5.0.0","ts-jest":"^29.1.0","tsup":"^8.0.0","typescript":"^5.4.0"},"_id":"@dloizides/tenant-theme-web@1.0.1","gitHead":"f66c8babfd1471460c3df514c0de7ae2a0e325c8","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-D+O5fHMsAy3rqD1wuv9rznPYNvOytfcIjttIO7F2DMgjmf1asTuoD03dTuSkwUzL3xU3XM6kkktyuJlc8agXeg==","shasum":"a7373455911c118a82af5dd373f69aaa7e39bd4e","tarball":"https://registry.npmjs.org/@dloizides/tenant-theme-web/-/tenant-theme-web-1.0.1.tgz","fileCount":10,"unpackedSize":91535,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIACje8P3B9BsGbz0AVJBhNQhFj9xDAuUFqvonFXDEOwRAiB23YtZLvmleATA7TEnBIt9OO0mvfwcC+k8Y+De2ZlEeA=="}]},"_npmUser":{"name":"dloizides","email":"loizidesdemetris@gmail.com"},"directories":{},"maintainers":[{"name":"dloizides","email":"loizidesdemetris@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tenant-theme-web_1.0.1_1781444914186_0.24961308127845272"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-14T13:48:33.973Z","1.0.1":"2026-06-14T13:48:34.324Z","modified":"2026-06-14T13:48:34.585Z"},"maintainers":[{"name":"dloizides","email":"loizidesdemetris@gmail.com"}],"description":"Product-agnostic RN-web tenant-theme client for the dloizides.com portfolio: the ETag-conditional theme fetch, the DTO -> TenantThemeConfig mapper, the localStorage ETag cache, and the save-request body mapper that erevna-web and katalogos-web shared byte","homepage":"https://github.com/openmindednewby/tenant-theme-web#readme","keywords":["tenant-theme","theme","etag","cache","multitenant","dloizides"],"repository":{"type":"git","url":"git+https://github.com/openmindednewby/tenant-theme-web.git"},"author":{"name":"dloizides"},"bugs":{"url":"https://github.com/openmindednewby/tenant-theme-web/issues"},"license":"MIT","readme":"# @dloizides/tenant-theme-web\n\nProduct-agnostic RN-web **tenant-theme client** for the dloizides.com portfolio.\n\nOwns the tenant-theme logic that `erevna-web` and `katalogos-web` shared byte-for-byte:\n\n- `fetchTenantTheme(tenantId, opts)` — ETag-conditional `GET /api/tenants/{id}/theme`, mapping\n  the flat API DTO into a `TenantThemeConfig` (returns `{ themeConfig, etag, notModified }`).\n- `saveTenantTheme(tenantId, config, httpPut)` + `toApiThemeRequest(config)` — the\n  `PUT /api/tenants/{id}/theme` body mapper.\n- `toTenantThemeConfig` / `hasThemeData` — the DTO mapper helpers.\n- A localStorage **ETag cache**: `readThemeCache`, `writeThemeCache`, `clearThemeCache`,\n  `clearAllThemeCaches` (24h expiry, `tenant-theme-{id}` key convention).\n\nApp-specific concerns are **ports** the consumer supplies:\n\n- the **HTTP transport** (`httpGet` / `httpPut`) — wire to the app's axios apiClient /\n  `@dloizides/bff-web-client` / Orval mutator;\n- the **theme preset VALUES** (`defaultThemeConfig`) — each product keeps its own palette;\n- the **API base URL** (`baseURL`).\n\nThis package is intentionally **react-query-free** — the consuming app owns its `QueryClient` and\nits theme hooks. It never imports a product, realm, hardcoded URL, or palette.\n\n## Quick start\n\n```ts\nimport {\n  fetchTenantTheme,\n  saveTenantTheme,\n  readThemeCache,\n  writeThemeCache,\n  configureThemeCacheLogger,\n  type HttpGet,\n  type HttpPut,\n} from '@dloizides/tenant-theme-web';\n\nimport { DEFAULT_THEME_CONFIG } from '../theme/presets'; // app-owned palette\nimport { apiClient } from '../lib/api/apiClient';\n\n// 1. Wire the transports\nconst httpGet: HttpGet = (args) => apiClient.request({ method: 'GET', ...args });\nconst httpPut: HttpPut = (args) => apiClient.request({ method: 'PUT', ...args }).then((r) => r.data);\n\n// 2. Optional: route cache-failure warnings to your logger\nconfigureThemeCacheLogger((ctx, msg, err) => loggingService.warn(ctx, msg, err));\n\n// 3. Fetch (ETag-conditional)\nconst cached = readThemeCache(tenantId);\nconst res = await fetchTenantTheme(tenantId, {\n  httpGet,\n  defaultThemeConfig: DEFAULT_THEME_CONFIG,\n  baseURL: env.IDENTITY_API_URL,\n  cachedEtag: cached?.etag,\n});\nif (!res.notModified && res.themeConfig) {\n  writeThemeCache(tenantId, res.themeConfig, res.etag ?? '');\n}\n\n// 4. Save\nawait saveTenantTheme(tenantId, config, httpPut);\n```\n\n## Ports\n\n| Port | Type | Owned by | Notes |\n|------|------|----------|-------|\n| `httpGet` | `HttpGet` | app | Must resolve for statuses `validateStatus` accepts (2xx + 304), reject otherwise with axios-style `error.response.status`. |\n| `httpPut` | `HttpPut` | app | Resolves with the parsed response body. |\n| `defaultThemeConfig` | `TenantThemeConfig` | app | The product's `DEFAULT_THEME_CONFIG` fallback palette. |\n| `baseURL` | `string?` | app | Identity API base; omit to use the transport's own base. |\n| cache warn logger | `ThemeCacheWarn?` | app | Via `configureThemeCacheLogger`; defaults to silent. |\n\n## Scripts\n\n```bash\nnpm run build      # tsup -> dual CJS/ESM + d.ts\nnpm test           # jest (100% coverage gate)\nnpm run lint       # eslint\nnpm run typecheck  # tsc --noEmit\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-ce7dfb924843a96dd689bae86bd827bd"}