@xzibit/ui 0.1.0 → 0.1.1
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.
- package/CHANGELOG.md +28 -1
- package/README.md +9 -1
- package/dist/index.cjs +16 -1
- package/dist/index.d.cts +38 -7
- package/dist/index.d.ts +38 -7
- package/dist/index.js +15 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@xzibit/ui` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) loosely; versioning follows [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
-
## [0.1.
|
|
5
|
+
## [0.1.1] — 2026-05-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Peer dependency widened** to accept React 19 (`react@"^18.0.0 || ^19.0.0"` and `react-dom` same). Surfaced during ERP Overview migration — `--legacy-peer-deps` was needed to install on the React 19 app. Affects every React 19 app planning to adopt; v0.1.0 blocked clean install.
|
|
9
|
+
- **API field-name contract resilience.** `useApps` now normalizes raw Supabase column names (`app_url`, `display_section`, `display_order`) to the canonical `App` shape (`url`, `section`, `section_order`) via internal `normalizeApp()` at the fetch boundary. Apps with existing endpoints returning raw query rows now work without changes; new endpoints can return either shape. Surfaced during ERP Overview migration — package rendered empty dropdown until CC added a manual `.map()` normalization in the route handler.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `RawApp` type exported for apps that want to type their endpoint response explicitly
|
|
13
|
+
- `normalizeApp(raw: RawApp): App` helper exported for apps that want to call it directly (rare; `useApps` calls it automatically)
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
- README's `/api/me/apps` section now documents both accepted field-name conventions and the normalization behaviour.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## [0.1.0] — 2026-05-22 (drafted) / 2026-05-23 (published)
|
|
21
|
+
|
|
22
|
+
### Published artifact
|
|
23
|
+
- npm: https://www.npmjs.com/package/@xzibit/ui
|
|
24
|
+
- Provenance attestation: present
|
|
25
|
+
- Install: `npm install @xzibit/ui`
|
|
26
|
+
|
|
27
|
+
### Bug fixes applied between draft and publish (sync'd back into this draft 2026-05-23)
|
|
28
|
+
- **`package-lock.json` generated** via `npm install` (not present in initial draft — required for `npm ci` in CI).
|
|
29
|
+
- **Removed stale `import React` namespace imports** from 4 source files (`TopBar.tsx`, `XzibitMark.tsx`, `BackToLauncher.tsx`, `AppsDropdown.tsx`). The package's `tsconfig.json` uses `"jsx": "react-jsx"` (modern transform, doesn't need React import) + `noUnusedLocals: true`, so bare `import React from 'react'` fails the DTS build. Fixed by removing the unused namespace import — named imports (`useState`, `useEffect`, etc.) where actually needed are kept.
|
|
30
|
+
|
|
31
|
+
### Future-package note
|
|
32
|
+
When drafting any future React component package for the `@xzibit/*` scope, omit `import React from 'react'` unless React is actually used directly (e.g. `React.forwardRef`). Just import the hooks / types you need by name.
|
|
6
33
|
|
|
7
34
|
### Added
|
|
8
35
|
|
package/README.md
CHANGED
|
@@ -58,7 +58,15 @@ That's it. The bar renders:
|
|
|
58
58
|
|
|
59
59
|
### `/api/me/apps` endpoint required
|
|
60
60
|
|
|
61
|
-
`AppsDropdown` calls `GET /api/me/apps` (same-origin) for the cross-app navigation list. Each consuming app must expose this endpoint per CODING-STANDARDS §6.3
|
|
61
|
+
`AppsDropdown` calls `GET /api/me/apps` (same-origin) for the cross-app navigation list. Each consuming app must expose this endpoint per CODING-STANDARDS §6.3.
|
|
62
|
+
|
|
63
|
+
**Field-name contract (v0.1.1+):** the endpoint can return EITHER:
|
|
64
|
+
- **Canonical shape** (recommended for new endpoints): `{ name, url, description?, section?, section_order? }`
|
|
65
|
+
- **Supabase column-name passthrough** (for endpoints that return raw `public.apps` rows): `{ name, app_url, description?, display_section?, display_order? }`
|
|
66
|
+
|
|
67
|
+
`useApps` normalizes both shapes to the canonical `App` interface at the fetch boundary — components downstream never see the raw form. Apps with existing endpoints that return raw column names work without changes.
|
|
68
|
+
|
|
69
|
+
Example route handler:
|
|
62
70
|
|
|
63
71
|
```typescript
|
|
64
72
|
// src/app/api/me/apps/route.ts
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
BackToLauncher: () => BackToLauncher,
|
|
25
25
|
TopBar: () => TopBar,
|
|
26
26
|
XzibitMark: () => XzibitMark,
|
|
27
|
+
normalizeApp: () => normalizeApp,
|
|
27
28
|
useApps: () => useApps
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -133,6 +134,19 @@ var import_react3 = require("react");
|
|
|
133
134
|
|
|
134
135
|
// src/useApps.ts
|
|
135
136
|
var import_react2 = require("react");
|
|
137
|
+
|
|
138
|
+
// src/types.ts
|
|
139
|
+
function normalizeApp(raw) {
|
|
140
|
+
return {
|
|
141
|
+
name: raw.name,
|
|
142
|
+
url: raw.url ?? raw.app_url ?? "",
|
|
143
|
+
description: raw.description,
|
|
144
|
+
section: raw.section ?? raw.display_section ?? null,
|
|
145
|
+
section_order: raw.section_order ?? raw.display_order
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/useApps.ts
|
|
136
150
|
function useApps(options = {}) {
|
|
137
151
|
const { endpoint = "/api/me/apps", lazy = false } = options;
|
|
138
152
|
const [apps, setApps] = (0, import_react2.useState)([]);
|
|
@@ -153,7 +167,7 @@ function useApps(options = {}) {
|
|
|
153
167
|
if (data.error) {
|
|
154
168
|
throw new Error(data.error);
|
|
155
169
|
}
|
|
156
|
-
setApps(data.apps ?? []);
|
|
170
|
+
setApps((data.apps ?? []).map(normalizeApp));
|
|
157
171
|
} catch (err) {
|
|
158
172
|
console.error("[@xzibit/ui useApps] fetch failed:", err);
|
|
159
173
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -551,5 +565,6 @@ function BuildBadge({
|
|
|
551
565
|
BackToLauncher,
|
|
552
566
|
TopBar,
|
|
553
567
|
XzibitMark,
|
|
568
|
+
normalizeApp,
|
|
554
569
|
useApps
|
|
555
570
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -95,11 +95,12 @@ declare function XzibitMark({ size, className, ariaLabel }: XzibitMarkProps): re
|
|
|
95
95
|
* Shared types for @xzibit/ui.
|
|
96
96
|
*/
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* Canonical app shape consumed by `@xzibit/ui` components.
|
|
99
99
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* This is what `useApps` returns after normalization. App authors writing
|
|
101
|
+
* NEW `/api/me/apps` endpoints SHOULD return this shape directly. Apps with
|
|
102
|
+
* existing endpoints that return raw Supabase column names (see `RawApp` below)
|
|
103
|
+
* are also accepted — `useApps` normalizes at the fetch boundary.
|
|
103
104
|
*/
|
|
104
105
|
interface App {
|
|
105
106
|
/** Display name, e.g. "Capacity Planner". */
|
|
@@ -113,16 +114,46 @@ interface App {
|
|
|
113
114
|
/** Sort order for the section itself (matches launcher curation). */
|
|
114
115
|
section_order?: number;
|
|
115
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Raw shape accepted from `/api/me/apps` endpoints.
|
|
119
|
+
*
|
|
120
|
+
* Supports two field-naming conventions:
|
|
121
|
+
* - **Canonical** (recommended for new endpoints): `url`, `section`, `section_order`
|
|
122
|
+
* - **Supabase column-name passthrough** (for endpoints that return raw query rows):
|
|
123
|
+
* `app_url`, `display_section`, `display_order`
|
|
124
|
+
*
|
|
125
|
+
* `useApps` normalizes to the canonical `App` shape via `normalizeApp()` at the
|
|
126
|
+
* fetch boundary — components downstream only see the canonical shape.
|
|
127
|
+
*
|
|
128
|
+
* Added in v0.1.1 (2026-05-24) after ERP Overview migration surfaced the contract
|
|
129
|
+
* mismatch with raw Supabase column names.
|
|
130
|
+
*/
|
|
131
|
+
interface RawApp {
|
|
132
|
+
name: string;
|
|
133
|
+
url?: string;
|
|
134
|
+
app_url?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
section?: string | null;
|
|
137
|
+
display_section?: string | null;
|
|
138
|
+
section_order?: number;
|
|
139
|
+
display_order?: number;
|
|
140
|
+
}
|
|
116
141
|
/**
|
|
117
142
|
* Response shape from `/api/me/apps`.
|
|
118
143
|
*
|
|
119
144
|
* Per CODING-STANDARDS §6.4 — successful responses are wrapped in a `data`
|
|
120
|
-
* or domain-specific key (in this case `apps`).
|
|
145
|
+
* or domain-specific key (in this case `apps`). The `apps` array contains
|
|
146
|
+
* `RawApp` items; `useApps` normalizes them to `App`.
|
|
121
147
|
*/
|
|
122
148
|
interface AppsResponse {
|
|
123
|
-
apps?:
|
|
149
|
+
apps?: RawApp[];
|
|
124
150
|
error?: string;
|
|
125
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Internal helper — normalizes a `RawApp` to the canonical `App` shape.
|
|
154
|
+
* Used by `useApps` at the fetch boundary; not typically called by consumers.
|
|
155
|
+
*/
|
|
156
|
+
declare function normalizeApp(raw: RawApp): App;
|
|
126
157
|
|
|
127
158
|
interface UseAppsResult {
|
|
128
159
|
apps: App[];
|
|
@@ -150,4 +181,4 @@ interface UseAppsOptions {
|
|
|
150
181
|
*/
|
|
151
182
|
declare function useApps(options?: UseAppsOptions): UseAppsResult;
|
|
152
183
|
|
|
153
|
-
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, useApps };
|
|
184
|
+
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, type RawApp, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, normalizeApp, useApps };
|
package/dist/index.d.ts
CHANGED
|
@@ -95,11 +95,12 @@ declare function XzibitMark({ size, className, ariaLabel }: XzibitMarkProps): re
|
|
|
95
95
|
* Shared types for @xzibit/ui.
|
|
96
96
|
*/
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* Canonical app shape consumed by `@xzibit/ui` components.
|
|
99
99
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* This is what `useApps` returns after normalization. App authors writing
|
|
101
|
+
* NEW `/api/me/apps` endpoints SHOULD return this shape directly. Apps with
|
|
102
|
+
* existing endpoints that return raw Supabase column names (see `RawApp` below)
|
|
103
|
+
* are also accepted — `useApps` normalizes at the fetch boundary.
|
|
103
104
|
*/
|
|
104
105
|
interface App {
|
|
105
106
|
/** Display name, e.g. "Capacity Planner". */
|
|
@@ -113,16 +114,46 @@ interface App {
|
|
|
113
114
|
/** Sort order for the section itself (matches launcher curation). */
|
|
114
115
|
section_order?: number;
|
|
115
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Raw shape accepted from `/api/me/apps` endpoints.
|
|
119
|
+
*
|
|
120
|
+
* Supports two field-naming conventions:
|
|
121
|
+
* - **Canonical** (recommended for new endpoints): `url`, `section`, `section_order`
|
|
122
|
+
* - **Supabase column-name passthrough** (for endpoints that return raw query rows):
|
|
123
|
+
* `app_url`, `display_section`, `display_order`
|
|
124
|
+
*
|
|
125
|
+
* `useApps` normalizes to the canonical `App` shape via `normalizeApp()` at the
|
|
126
|
+
* fetch boundary — components downstream only see the canonical shape.
|
|
127
|
+
*
|
|
128
|
+
* Added in v0.1.1 (2026-05-24) after ERP Overview migration surfaced the contract
|
|
129
|
+
* mismatch with raw Supabase column names.
|
|
130
|
+
*/
|
|
131
|
+
interface RawApp {
|
|
132
|
+
name: string;
|
|
133
|
+
url?: string;
|
|
134
|
+
app_url?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
section?: string | null;
|
|
137
|
+
display_section?: string | null;
|
|
138
|
+
section_order?: number;
|
|
139
|
+
display_order?: number;
|
|
140
|
+
}
|
|
116
141
|
/**
|
|
117
142
|
* Response shape from `/api/me/apps`.
|
|
118
143
|
*
|
|
119
144
|
* Per CODING-STANDARDS §6.4 — successful responses are wrapped in a `data`
|
|
120
|
-
* or domain-specific key (in this case `apps`).
|
|
145
|
+
* or domain-specific key (in this case `apps`). The `apps` array contains
|
|
146
|
+
* `RawApp` items; `useApps` normalizes them to `App`.
|
|
121
147
|
*/
|
|
122
148
|
interface AppsResponse {
|
|
123
|
-
apps?:
|
|
149
|
+
apps?: RawApp[];
|
|
124
150
|
error?: string;
|
|
125
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Internal helper — normalizes a `RawApp` to the canonical `App` shape.
|
|
154
|
+
* Used by `useApps` at the fetch boundary; not typically called by consumers.
|
|
155
|
+
*/
|
|
156
|
+
declare function normalizeApp(raw: RawApp): App;
|
|
126
157
|
|
|
127
158
|
interface UseAppsResult {
|
|
128
159
|
apps: App[];
|
|
@@ -150,4 +181,4 @@ interface UseAppsOptions {
|
|
|
150
181
|
*/
|
|
151
182
|
declare function useApps(options?: UseAppsOptions): UseAppsResult;
|
|
152
183
|
|
|
153
|
-
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, useApps };
|
|
184
|
+
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, type RawApp, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, normalizeApp, useApps };
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,19 @@ import { useState as useState3, useEffect as useEffect2, useRef, Fragment } from
|
|
|
103
103
|
|
|
104
104
|
// src/useApps.ts
|
|
105
105
|
import { useState as useState2, useEffect, useCallback } from "react";
|
|
106
|
+
|
|
107
|
+
// src/types.ts
|
|
108
|
+
function normalizeApp(raw) {
|
|
109
|
+
return {
|
|
110
|
+
name: raw.name,
|
|
111
|
+
url: raw.url ?? raw.app_url ?? "",
|
|
112
|
+
description: raw.description,
|
|
113
|
+
section: raw.section ?? raw.display_section ?? null,
|
|
114
|
+
section_order: raw.section_order ?? raw.display_order
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/useApps.ts
|
|
106
119
|
function useApps(options = {}) {
|
|
107
120
|
const { endpoint = "/api/me/apps", lazy = false } = options;
|
|
108
121
|
const [apps, setApps] = useState2([]);
|
|
@@ -123,7 +136,7 @@ function useApps(options = {}) {
|
|
|
123
136
|
if (data.error) {
|
|
124
137
|
throw new Error(data.error);
|
|
125
138
|
}
|
|
126
|
-
setApps(data.apps ?? []);
|
|
139
|
+
setApps((data.apps ?? []).map(normalizeApp));
|
|
127
140
|
} catch (err) {
|
|
128
141
|
console.error("[@xzibit/ui useApps] fetch failed:", err);
|
|
129
142
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -520,5 +533,6 @@ export {
|
|
|
520
533
|
BackToLauncher,
|
|
521
534
|
TopBar,
|
|
522
535
|
XzibitMark,
|
|
536
|
+
normalizeApp,
|
|
523
537
|
useApps
|
|
524
538
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xzibit/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Shared chrome components for the Xzibit Apps portfolio. v0.1: TopBar + BackToLauncher + AppsDropdown + XzibitMark + useApps hook. Single source of truth for portfolio chrome — tweak once, every app picks it up on next deploy.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Xzibit Apps",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"prepublishOnly": "npm run build"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"react": "^18.0.0",
|
|
46
|
-
"react-dom": "^18.0.0"
|
|
45
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
46
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"typescript": "^5.0.0",
|