@ledat-code/core (0.2.1)
Installation
@ledat-code:registry=https://git.ledat96.synology.me/api/packages/ledat-code/npm/npm install @ledat-code/core@0.2.1"@ledat-code/core": "0.2.1"About this package
@ledat-code/core
Shared GraphQL transport, UI preferences and reusable React/MUI UI primitives for Client and Admin apps.
Root exports are framework-neutral GraphQL and UI settings APIs. UI primitives use the @ledat-code/core/ui subpath. No React Admin, auth policy, RBAC, or domain operations.
Install
Declare @ledat-code/core directly in each consuming app's manifest/lockfile; do not rely on the transitive dependency of admin/client. Run package commands inside the app's Docker service:
npm install @ledat-code/core
Use a verified released version containing the required APIs, not an assumed future version or an unverified latest.
GraphQL
Configure GraphQL per request:
import { graphqlRequest } from "@ledat-code/core";
const result = await graphqlRequest({
apiUrl: import.meta.env.VITE_API_URL,
query,
variables,
accessToken,
});
apiUrl must be an absolute HTTP(S) URL without embedded credentials. Token is optional and must be supplied by consumer auth code. GraphQL transport does not read environment variables or browser storage; UI settings APIs access browser storage only when explicitly called. They never read auth/session tokens.
UI settings
Only the root of @ledat-code/core exports these APIs; admin/client do not re-export them:
import {
getUiSettings,
getUiSetting,
updateUiSettings,
updateUiSetting,
type UiSettings,
} from "@ledat-code/core";
// UiSettings = { colorMode: "light" | "dark" }
getUiSettings();
getUiSettings({ colorMode: "dark" });
getUiSetting("colorMode", "dark");
updateUiSettings({ colorMode: "dark" });
updateUiSetting("colorMode", "light");
Storage key: lightnovel.ui.setting. Value is a flat JSON object, not a Zustand state/version envelope:
{ "colorMode": "dark" }
- Default is
light; nosystem, language or density API yet. - Getters never write. Per key, a valid stored value wins over a valid optional fallback, then the default. Stored
lightis a real preference and wins over fallbackdark. - Setters read the latest storage snapshot, validate and merge known patch fields, then return effective settings (or the selected value for
updateUiSetting). Invalid values andundefineddo not delete existing fields. - Safe unknown fields already in storage survive updates for forward compatibility, but are not exposed in typed results or accepted as arbitrary setter keys. Updates are shallow, not deep merges. Dangerous keys
__proto__,constructorandprototypeare discarded from stored JSON, including nested objects. - Invalid JSON, null, arrays and primitive storage values are treated as missing settings. Returned settings objects are fresh on each call.
- Module import is Node/SSR-safe. Storage access happens per call in try/catch; there is no singleton persistence cache.
- If storage access or writes fail, calls do not throw because of that failure. Setters return the effective choice for local UI state, not confirmation that it was persisted. Reload may restore a previously saved value or the default.
- Preferences are shared only within the same browser and origin (protocol, hostname and port), not per account or across devices.
- No subscriptions, storage listeners, events, backend sync or automatic React state updates. The active app updates its own state; other tabs read the choice on reload.
- Read/merge/write protects sequential updates to unrelated fields, not simultaneous cross-tab writes. No atomic conflict resolution is promised.
- Never store sensitive data here. Core does not read legacy app keys, log raw storage or own theme palettes.
Project wrapper and migration
Only the shell or standalone entry that owns theme state bootstraps once, before initializing that state. Components and UI stores call a project wrapper, not storage or core directly. Embedded remotes receive mode from their shell and must not write preferences merely because props changed.
Example wrapper; the project owns legacy parsing/validation and passes the result in:
import {
getUiSetting,
updateUiSetting,
type UiSettings,
} from "@ledat-code/core";
export function bootstrapColorMode(legacyMode?: UiSettings["colorMode"]) {
const mode = getUiSetting("colorMode", legacyMode);
return updateUiSetting("colorMode", mode);
}
export function saveColorMode(mode: UiSettings["colorMode"]) {
return updateUiSetting("colorMode", mode);
}
Project legacy readers must validate data and catch storage errors. Do not read the canonical key directly in projects. Do not delete legacy keys or migrate project-specific settings. If no valid canonical value exists, the first app to bootstrap and persist successfully establishes the shared choice; there is no admin-over-client priority.
Admin apps that migrate must keep their theme state separate from legacy useUiStore.colorMode. The old admin store, lightnovel.admin.ui, finance visibility and persistence remain unchanged for existing consumers. Old app versions using only legacy keys do not synchronize with migrated apps.
UI primitives
Root APIs require no React/MUI/Emotion installation. UI peers are optional at package level because npm cannot require peers per subpath.
To use @ledat-code/core/ui, explicitly install compatible React, React DOM, MUI material/icons, and Emotion react/styled peers listed in package.json:
import { AppButton } from "@ledat-code/core/ui";
Development and verification
Run from the ledat-mfe/ workspace root, with the admin-shell service running and library dependencies installed inside Docker:
docker compose -f projects/admin-shell/dockers/dev/docker-compose.yml exec -T admin-shell sh -lc 'cd /workspace/libs/core && npm run type-check && npm test'
docker compose -f projects/admin-shell/dockers/dev/docker-compose.yml exec -T admin-shell sh -lc 'cd /workspace/libs && node scripts/pack.mjs && node scripts/smoke-core.mjs && node scripts/smoke-client.mjs && node scripts/smoke-admin.mjs'
npm test builds real JavaScript, runs Node built-in runtime tests and checks positive/negative TypeScript cases with npm run test:types. The core tarball smoke repeats the settings contract through the public root and checks NodeNext declarations and installation without UI peers. Client/admin smoke tests cover dependent packages and UI builds; admin also checks legacy UI store persistence/finance compatibility. Smoke tests need npm registry connectivity and use temporary projects. No lint script is currently configured.
Tests are excluded from dist. pack.mjs normalizes published ESM imports and packs artifacts without publishing. Existing release policy still releases core and both dependents for a core change; this does not require settings facades or runtime source edits in admin/client. Publishing and pushing nas require separate authorization. Consumer version upgrades and project wrappers are separate app work.
Dependencies
Peer Dependencies
| ID | Version |
|---|---|
| @emotion/react | ^11.14.0 |
| @emotion/styled | ^11.14.1 |
| @mui/icons-material | ^9.2.0 |
| @mui/material | ^9.2.0 |
| react | ^19.0.0 |
| react-dom | ^19.0.0 |