@herdingbits/trailhead-core 0.0.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/README.md +30 -0
- package/dist/adapters/public-api.d.ts +5 -0
- package/dist/adapters/public-api.d.ts.map +1 -0
- package/dist/adapters/public-api.js +4 -0
- package/dist/adapters/types.d.ts +62 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +6 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/feedback.d.ts +58 -0
- package/dist/lib/feedback.d.ts.map +1 -0
- package/dist/lib/feedback.js +145 -0
- package/dist/lib/http.d.ts +26 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +98 -0
- package/dist/lib/i18n.d.ts +7 -0
- package/dist/lib/i18n.d.ts.map +1 -0
- package/dist/lib/i18n.js +6 -0
- package/dist/lib/requestManager.d.ts +33 -0
- package/dist/lib/requestManager.d.ts.map +1 -0
- package/dist/lib/requestManager.js +72 -0
- package/dist/shell.d.ts +54 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +236 -0
- package/dist/types/public-api.d.ts +6 -0
- package/dist/types/public-api.d.ts.map +1 -0
- package/dist/types/public-api.js +5 -0
- package/dist/types/shell-api.d.ts +97 -0
- package/dist/types/shell-api.d.ts.map +1 -0
- package/dist/types/shell-api.js +5 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @herdingbits/trailhead-core
|
|
2
|
+
|
|
3
|
+
Core shell orchestration for the Trailhead micro-frontend framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @herdingbits/trailhead-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Trailhead } from '@herdingbits/trailhead-core';
|
|
15
|
+
import { YourAdapter } from '@herdingbits/trailhead-your-design-system';
|
|
16
|
+
|
|
17
|
+
const shell = new Trailhead({
|
|
18
|
+
adapter: new YourAdapter(),
|
|
19
|
+
basePath: '/app',
|
|
20
|
+
apiUrl: 'https://api.example.com'
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
See the [main Trailhead documentation](https://github.com/herdingbits/trailhead) for more information.
|
|
27
|
+
|
|
28
|
+
## License
|
|
29
|
+
|
|
30
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../src/adapters/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design System Adapter Interfaces
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the contract between Trailhead core and design system implementations.
|
|
5
|
+
*/
|
|
6
|
+
export type ToastVariant = "success" | "error" | "warning" | "info";
|
|
7
|
+
export interface DialogButton<T extends string = string> {
|
|
8
|
+
label: string;
|
|
9
|
+
value: T;
|
|
10
|
+
variant?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DialogConfig<T extends string = string> {
|
|
13
|
+
message: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
buttons: DialogButton<T>[];
|
|
16
|
+
}
|
|
17
|
+
export interface DialogResult<T extends string = string> {
|
|
18
|
+
value: T | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Feedback Adapter - Handles user feedback (toasts, dialogs, busy states)
|
|
22
|
+
*/
|
|
23
|
+
export interface FeedbackAdapter {
|
|
24
|
+
/**
|
|
25
|
+
* Show a busy overlay with a message
|
|
26
|
+
*/
|
|
27
|
+
showBusy(message: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Clear the busy overlay
|
|
30
|
+
*/
|
|
31
|
+
clearBusy(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Show a toast notification
|
|
34
|
+
*/
|
|
35
|
+
showToast(message: string, variant: ToastVariant, duration?: number): void;
|
|
36
|
+
/**
|
|
37
|
+
* Show a dialog with custom buttons
|
|
38
|
+
*/
|
|
39
|
+
showDialog<T extends string>(config: DialogConfig<T>): Promise<DialogResult<T>>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Design System Adapter - Main adapter interface
|
|
43
|
+
*/
|
|
44
|
+
export interface DesignSystemAdapter {
|
|
45
|
+
/**
|
|
46
|
+
* Adapter name (e.g., "shoelace", "cloudscape")
|
|
47
|
+
*/
|
|
48
|
+
name: string;
|
|
49
|
+
/**
|
|
50
|
+
* Adapter version
|
|
51
|
+
*/
|
|
52
|
+
version: string;
|
|
53
|
+
/**
|
|
54
|
+
* Initialize the design system (load assets, set base paths, etc.)
|
|
55
|
+
*/
|
|
56
|
+
init(basePath: string): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Feedback adapter for user notifications
|
|
59
|
+
*/
|
|
60
|
+
feedback: FeedbackAdapter;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,SAAS,IAAI,IAAI,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3E;;OAEG;IACH,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CACjF;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;CAC3B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback system - imperative API for user feedback
|
|
3
|
+
* No React, pure DOM manipulation
|
|
4
|
+
*/
|
|
5
|
+
import type { AlertVariant } from "../types/shell-api";
|
|
6
|
+
/**
|
|
7
|
+
* Show blocking busy overlay with spinner
|
|
8
|
+
*/
|
|
9
|
+
export declare function busy(message: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Clear busy overlay
|
|
12
|
+
*/
|
|
13
|
+
export declare function clear(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Show toast notification
|
|
16
|
+
*/
|
|
17
|
+
export declare function alert(message: string, variant?: AlertVariant, duration?: number): void;
|
|
18
|
+
/**
|
|
19
|
+
* Show success toast
|
|
20
|
+
*/
|
|
21
|
+
export declare function success(message: string, duration?: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Show error toast
|
|
24
|
+
*/
|
|
25
|
+
export declare function error(message: string, duration?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Show warning toast
|
|
28
|
+
*/
|
|
29
|
+
export declare function warning(message: string, duration?: number): void;
|
|
30
|
+
/**
|
|
31
|
+
* Show info toast
|
|
32
|
+
*/
|
|
33
|
+
export declare function info(message: string, duration?: number): void;
|
|
34
|
+
/**
|
|
35
|
+
* Show dialog with custom buttons
|
|
36
|
+
*/
|
|
37
|
+
export declare function custom<T extends string>(message: string, title: string, buttons: Array<{
|
|
38
|
+
label: string;
|
|
39
|
+
value: T;
|
|
40
|
+
variant?: string;
|
|
41
|
+
}>): Promise<T | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Show confirm dialog (Cancel/Confirm)
|
|
44
|
+
*/
|
|
45
|
+
export declare function confirm(message: string, title?: string): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Show OK dialog
|
|
48
|
+
*/
|
|
49
|
+
export declare function ok(message: string, title?: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Show Yes/No dialog
|
|
52
|
+
*/
|
|
53
|
+
export declare function yesNo(message: string, title?: string): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Show Yes/No/Cancel dialog
|
|
56
|
+
*/
|
|
57
|
+
export declare function yesNoCancel(message: string, title?: string): Promise<"yes" | "no" | "cancel">;
|
|
58
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/lib/feedback.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKvD;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAkB1C;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,IAAI,CAI5B;AAED;;GAEG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAqB,EAC9B,QAAQ,GAAE,MAAa,GACtB,IAAI,CAiBN;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAEhE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAE9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAEhE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAE7D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5D,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAqCnB;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKpF;AAED;;GAEG;AACH,wBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKlF;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAkB,GACxB,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC,CAMlC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
let busyOverlay = null;
|
|
2
|
+
let toastContainer = null;
|
|
3
|
+
/**
|
|
4
|
+
* Show blocking busy overlay with spinner
|
|
5
|
+
*/
|
|
6
|
+
export function busy(message) {
|
|
7
|
+
if (!busyOverlay) {
|
|
8
|
+
busyOverlay = document.createElement("div");
|
|
9
|
+
busyOverlay.id = "shell-busy-overlay";
|
|
10
|
+
busyOverlay.innerHTML = `
|
|
11
|
+
<div class="shell-busy-content">
|
|
12
|
+
<div class="shell-spinner"></div>
|
|
13
|
+
<div class="shell-busy-message"></div>
|
|
14
|
+
</div>
|
|
15
|
+
`;
|
|
16
|
+
document.body.appendChild(busyOverlay);
|
|
17
|
+
}
|
|
18
|
+
const messageEl = busyOverlay.querySelector(".shell-busy-message");
|
|
19
|
+
if (messageEl) {
|
|
20
|
+
messageEl.textContent = message;
|
|
21
|
+
}
|
|
22
|
+
busyOverlay.style.display = "flex";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Clear busy overlay
|
|
26
|
+
*/
|
|
27
|
+
export function clear() {
|
|
28
|
+
if (busyOverlay) {
|
|
29
|
+
busyOverlay.style.display = "none";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Show toast notification
|
|
34
|
+
*/
|
|
35
|
+
export function alert(message, variant = "info", duration = 3000) {
|
|
36
|
+
if (!toastContainer) {
|
|
37
|
+
toastContainer = document.createElement("div");
|
|
38
|
+
toastContainer.id = "shell-toast-container";
|
|
39
|
+
document.body.appendChild(toastContainer);
|
|
40
|
+
}
|
|
41
|
+
const toast = document.createElement("div");
|
|
42
|
+
toast.className = `shell-toast shell-toast-${variant}`;
|
|
43
|
+
toast.textContent = message;
|
|
44
|
+
toastContainer.appendChild(toast);
|
|
45
|
+
setTimeout(() => toast.classList.add("shell-toast-show"), 10);
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
toast.classList.remove("shell-toast-show");
|
|
48
|
+
setTimeout(() => toast.remove(), 300);
|
|
49
|
+
}, duration);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Show success toast
|
|
53
|
+
*/
|
|
54
|
+
export function success(message, duration) {
|
|
55
|
+
alert(message, "success", duration);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Show error toast
|
|
59
|
+
*/
|
|
60
|
+
export function error(message, duration) {
|
|
61
|
+
alert(message, "error", duration || 5000);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Show warning toast
|
|
65
|
+
*/
|
|
66
|
+
export function warning(message, duration) {
|
|
67
|
+
alert(message, "warning", duration || 4000);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Show info toast
|
|
71
|
+
*/
|
|
72
|
+
export function info(message, duration) {
|
|
73
|
+
alert(message, "info", duration);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Show dialog with custom buttons
|
|
77
|
+
*/
|
|
78
|
+
export function custom(message, title, buttons) {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const dialog = document.createElement("div");
|
|
81
|
+
dialog.className = "shell-dialog-overlay";
|
|
82
|
+
dialog.innerHTML = `
|
|
83
|
+
<div class="shell-dialog">
|
|
84
|
+
<div class="shell-dialog-title">${title}</div>
|
|
85
|
+
<div class="shell-dialog-message">${message}</div>
|
|
86
|
+
<div class="shell-dialog-buttons">
|
|
87
|
+
${buttons
|
|
88
|
+
.map((btn) => `<button class="shell-btn shell-btn-${btn.variant || "default"}" data-value="${btn.value}">${btn.label}</button>`)
|
|
89
|
+
.join("")}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
`;
|
|
93
|
+
dialog.querySelectorAll("button").forEach((btn) => {
|
|
94
|
+
btn.onclick = () => {
|
|
95
|
+
const value = btn.getAttribute("data-value");
|
|
96
|
+
dialog.remove();
|
|
97
|
+
resolve(value);
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
// Click outside to cancel
|
|
101
|
+
dialog.onclick = (e) => {
|
|
102
|
+
if (e.target === dialog) {
|
|
103
|
+
dialog.remove();
|
|
104
|
+
resolve(null);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
document.body.appendChild(dialog);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Show confirm dialog (Cancel/Confirm)
|
|
112
|
+
*/
|
|
113
|
+
export function confirm(message, title = "Confirm") {
|
|
114
|
+
return custom(message, title, [
|
|
115
|
+
{ label: "Cancel", value: "cancel", variant: "secondary" },
|
|
116
|
+
{ label: "Confirm", value: "confirm", variant: "primary" },
|
|
117
|
+
]).then((result) => result === "confirm");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Show OK dialog
|
|
121
|
+
*/
|
|
122
|
+
export function ok(message, title = "Information") {
|
|
123
|
+
return custom(message, title, [
|
|
124
|
+
{ label: "OK", value: "ok", variant: "primary" },
|
|
125
|
+
]).then(() => undefined);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Show Yes/No dialog
|
|
129
|
+
*/
|
|
130
|
+
export function yesNo(message, title = "Confirm") {
|
|
131
|
+
return custom(message, title, [
|
|
132
|
+
{ label: "No", value: "no", variant: "secondary" },
|
|
133
|
+
{ label: "Yes", value: "yes", variant: "primary" },
|
|
134
|
+
]).then((result) => result === "yes");
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Show Yes/No/Cancel dialog
|
|
138
|
+
*/
|
|
139
|
+
export function yesNoCancel(message, title = "Confirm") {
|
|
140
|
+
return custom(message, title, [
|
|
141
|
+
{ label: "Cancel", value: "cancel", variant: "secondary" },
|
|
142
|
+
{ label: "No", value: "no", variant: "secondary" },
|
|
143
|
+
{ label: "Yes", value: "yes", variant: "primary" },
|
|
144
|
+
]).then((result) => result || "cancel");
|
|
145
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RequestOptions, Result } from "../types/shell-api";
|
|
2
|
+
/**
|
|
3
|
+
* Initialize HTTP client
|
|
4
|
+
*/
|
|
5
|
+
export declare function init(baseUrl?: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* GET request
|
|
8
|
+
*/
|
|
9
|
+
export declare function get<T>(url: string, options?: RequestOptions): Promise<Result<T>>;
|
|
10
|
+
/**
|
|
11
|
+
* POST request
|
|
12
|
+
*/
|
|
13
|
+
export declare function post<T>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
14
|
+
/**
|
|
15
|
+
* PUT request
|
|
16
|
+
*/
|
|
17
|
+
export declare function put<T>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
18
|
+
/**
|
|
19
|
+
* PATCH request
|
|
20
|
+
*/
|
|
21
|
+
export declare function patch<T>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
22
|
+
/**
|
|
23
|
+
* DELETE request
|
|
24
|
+
*/
|
|
25
|
+
export declare function del<T>(url: string, options?: RequestOptions): Promise<Result<T>>;
|
|
26
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/lib/http.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAyC,MAAM,oBAAoB,CAAC;AAKxG;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,MAAW,GAAG,IAAI,CAM/C;AA4ED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEhF;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEpB;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,EACnB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEpB;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,CAAC,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEpB;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEhF"}
|
package/dist/lib/http.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client using ky with automatic feedback orchestration
|
|
3
|
+
*/
|
|
4
|
+
import ky from "ky";
|
|
5
|
+
import * as requestManager from "./requestManager";
|
|
6
|
+
let kyInstance;
|
|
7
|
+
/**
|
|
8
|
+
* Initialize HTTP client
|
|
9
|
+
*/
|
|
10
|
+
export function init(baseUrl = "") {
|
|
11
|
+
kyInstance = ky.create({
|
|
12
|
+
prefixUrl: baseUrl,
|
|
13
|
+
timeout: 30000,
|
|
14
|
+
retry: 0,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Make HTTP request with feedback orchestration
|
|
19
|
+
*/
|
|
20
|
+
async function request(method, url, data, options = {}) {
|
|
21
|
+
const { requestKey, busyMessage, successMessage, showSuccess = false, noFeedback = false, headers = {}, } = options;
|
|
22
|
+
try {
|
|
23
|
+
requestManager.startRequest(requestKey, busyMessage, noFeedback);
|
|
24
|
+
const kyOptions = {
|
|
25
|
+
method,
|
|
26
|
+
headers,
|
|
27
|
+
};
|
|
28
|
+
if (data && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
29
|
+
kyOptions.json = data;
|
|
30
|
+
}
|
|
31
|
+
const response = await kyInstance(url, kyOptions);
|
|
32
|
+
const result = await response.json();
|
|
33
|
+
requestManager.endRequest(requestKey, noFeedback);
|
|
34
|
+
if (!noFeedback && showSuccess && successMessage) {
|
|
35
|
+
requestManager.showSuccess(successMessage);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
data: result,
|
|
40
|
+
requestKey,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
requestManager.endRequest(requestKey, noFeedback);
|
|
45
|
+
const error = {
|
|
46
|
+
name: err.name || "HttpError",
|
|
47
|
+
message: err.message || "Request failed",
|
|
48
|
+
status: err.response?.status,
|
|
49
|
+
};
|
|
50
|
+
if (err.response) {
|
|
51
|
+
try {
|
|
52
|
+
error.data = await err.response.json();
|
|
53
|
+
error.message = error.data.message || error.message;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Response not JSON
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!noFeedback) {
|
|
60
|
+
requestManager.showError(error.message);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error,
|
|
65
|
+
requestKey,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* GET request
|
|
71
|
+
*/
|
|
72
|
+
export function get(url, options) {
|
|
73
|
+
return request("GET", url, undefined, options);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* POST request
|
|
77
|
+
*/
|
|
78
|
+
export function post(url, data, options) {
|
|
79
|
+
return request("POST", url, data, options);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* PUT request
|
|
83
|
+
*/
|
|
84
|
+
export function put(url, data, options) {
|
|
85
|
+
return request("PUT", url, data, options);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* PATCH request
|
|
89
|
+
*/
|
|
90
|
+
export function patch(url, data, options) {
|
|
91
|
+
return request("PATCH", url, data, options);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* DELETE request
|
|
95
|
+
*/
|
|
96
|
+
export function del(url, options) {
|
|
97
|
+
return request("DELETE", url, undefined, options);
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/lib/i18n.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,CAAC,GAAI,KAAK,MAAM,KAAG,MAAa,CAAC"}
|
package/dist/lib/i18n.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request manager - orchestrates HTTP requests with feedback
|
|
3
|
+
*/
|
|
4
|
+
import type { FeedbackAdapter } from "../adapters/types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Initialize request manager with feedback adapter
|
|
7
|
+
*/
|
|
8
|
+
export declare function init(adapter: FeedbackAdapter): void;
|
|
9
|
+
/**
|
|
10
|
+
* Track request start
|
|
11
|
+
*/
|
|
12
|
+
export declare function startRequest(requestKey?: string, busyMessage?: string, noFeedback?: boolean): void;
|
|
13
|
+
/**
|
|
14
|
+
* Track request end
|
|
15
|
+
*/
|
|
16
|
+
export declare function endRequest(requestKey?: string, noFeedback?: boolean): void;
|
|
17
|
+
/**
|
|
18
|
+
* Show success feedback
|
|
19
|
+
*/
|
|
20
|
+
export declare function showSuccess(message: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Show error feedback
|
|
23
|
+
*/
|
|
24
|
+
export declare function showError(message: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get active request count
|
|
27
|
+
*/
|
|
28
|
+
export declare function getActiveRequestCount(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Check if specific request is active
|
|
31
|
+
*/
|
|
32
|
+
export declare function isRequestActive(requestKey: string): boolean;
|
|
33
|
+
//# sourceMappingURL=requestManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestManager.d.ts","sourceRoot":"","sources":["../../src/lib/requestManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAM5D;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEnD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,OAAO,GACnB,IAAI,CAYN;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,OAAO,GACnB,IAAI,CAgBN;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAIjD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
let activeRequests = 0;
|
|
2
|
+
const requestKeys = new Map();
|
|
3
|
+
let feedbackAdapter = null;
|
|
4
|
+
/**
|
|
5
|
+
* Initialize request manager with feedback adapter
|
|
6
|
+
*/
|
|
7
|
+
export function init(adapter) {
|
|
8
|
+
feedbackAdapter = adapter;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Track request start
|
|
12
|
+
*/
|
|
13
|
+
export function startRequest(requestKey, busyMessage, noFeedback) {
|
|
14
|
+
if (noFeedback || !feedbackAdapter)
|
|
15
|
+
return;
|
|
16
|
+
if (requestKey) {
|
|
17
|
+
const count = requestKeys.get(requestKey) || 0;
|
|
18
|
+
requestKeys.set(requestKey, count + 1);
|
|
19
|
+
}
|
|
20
|
+
activeRequests++;
|
|
21
|
+
if (activeRequests === 1) {
|
|
22
|
+
feedbackAdapter.showBusy(busyMessage || "Loading...");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Track request end
|
|
27
|
+
*/
|
|
28
|
+
export function endRequest(requestKey, noFeedback) {
|
|
29
|
+
if (noFeedback || !feedbackAdapter)
|
|
30
|
+
return;
|
|
31
|
+
if (requestKey) {
|
|
32
|
+
const count = requestKeys.get(requestKey) || 0;
|
|
33
|
+
if (count > 1) {
|
|
34
|
+
requestKeys.set(requestKey, count - 1);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
requestKeys.delete(requestKey);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
activeRequests--;
|
|
41
|
+
if (activeRequests === 0) {
|
|
42
|
+
feedbackAdapter.clearBusy();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Show success feedback
|
|
47
|
+
*/
|
|
48
|
+
export function showSuccess(message) {
|
|
49
|
+
if (feedbackAdapter) {
|
|
50
|
+
feedbackAdapter.showToast(message, "success");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Show error feedback
|
|
55
|
+
*/
|
|
56
|
+
export function showError(message) {
|
|
57
|
+
if (feedbackAdapter) {
|
|
58
|
+
feedbackAdapter.showToast(message, "error", 5000);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get active request count
|
|
63
|
+
*/
|
|
64
|
+
export function getActiveRequestCount() {
|
|
65
|
+
return activeRequests;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if specific request is active
|
|
69
|
+
*/
|
|
70
|
+
export function isRequestActive(requestKey) {
|
|
71
|
+
return requestKeys.has(requestKey);
|
|
72
|
+
}
|
package/dist/shell.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DesignSystemAdapter } from "./adapters/types.js";
|
|
2
|
+
export interface ShellConfig {
|
|
3
|
+
adapter: DesignSystemAdapter;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class Trailhead {
|
|
8
|
+
private navigation;
|
|
9
|
+
private routeChangeCallbacks;
|
|
10
|
+
private basePath;
|
|
11
|
+
private adapter;
|
|
12
|
+
constructor(config: ShellConfig);
|
|
13
|
+
/**
|
|
14
|
+
* Initialize shell
|
|
15
|
+
*/
|
|
16
|
+
private init;
|
|
17
|
+
/**
|
|
18
|
+
* Initialize design system adapter
|
|
19
|
+
*/
|
|
20
|
+
private initAdapter;
|
|
21
|
+
/**
|
|
22
|
+
* Create shell API
|
|
23
|
+
*/
|
|
24
|
+
private createAPI;
|
|
25
|
+
/**
|
|
26
|
+
* Load navigation configuration
|
|
27
|
+
*/
|
|
28
|
+
private loadNavigation;
|
|
29
|
+
/**
|
|
30
|
+
* Render navigation menu
|
|
31
|
+
*/
|
|
32
|
+
private renderNavigation;
|
|
33
|
+
/**
|
|
34
|
+
* Setup routing
|
|
35
|
+
*/
|
|
36
|
+
private setupRouting;
|
|
37
|
+
/**
|
|
38
|
+
* Navigate to path
|
|
39
|
+
*/
|
|
40
|
+
private navigate;
|
|
41
|
+
/**
|
|
42
|
+
* Handle route change
|
|
43
|
+
*/
|
|
44
|
+
private handleRoute;
|
|
45
|
+
/**
|
|
46
|
+
* Update active navigation item
|
|
47
|
+
*/
|
|
48
|
+
private updateActiveNav;
|
|
49
|
+
/**
|
|
50
|
+
* Load plugin application
|
|
51
|
+
*/
|
|
52
|
+
private loadPlugin;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAsB;gBAEzB,MAAM,EAAE,WAAW;IAM/B;;OAEG;YACW,IAAI;IAwBlB;;OAEG;YACW,WAAW;IAUzB;;OAEG;IACH,OAAO,CAAC,SAAS;IAmFjB;;OAEG;YACW,cAAc;IAU5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA6BxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;YACW,UAAU;CAsCzB"}
|
package/dist/shell.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import * as http from "./lib/http.js";
|
|
2
|
+
import * as requestManager from "./lib/requestManager.js";
|
|
3
|
+
import { t } from "./lib/i18n.js";
|
|
4
|
+
export class Trailhead {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.navigation = [];
|
|
7
|
+
this.routeChangeCallbacks = [];
|
|
8
|
+
this.basePath = config.basePath || "";
|
|
9
|
+
this.adapter = config.adapter;
|
|
10
|
+
this.init(config.apiUrl);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Initialize shell
|
|
14
|
+
*/
|
|
15
|
+
async init(apiUrl) {
|
|
16
|
+
// Initialize design system adapter
|
|
17
|
+
await this.initAdapter();
|
|
18
|
+
// Initialize request manager and HTTP client with adapter
|
|
19
|
+
requestManager.init(this.adapter.feedback);
|
|
20
|
+
http.init(apiUrl || "");
|
|
21
|
+
// Expose shell API globally
|
|
22
|
+
window.shell = this.createAPI();
|
|
23
|
+
// Load navigation
|
|
24
|
+
await this.loadNavigation();
|
|
25
|
+
// Setup routing
|
|
26
|
+
this.setupRouting();
|
|
27
|
+
// Render navigation
|
|
28
|
+
this.renderNavigation();
|
|
29
|
+
// Load initial route
|
|
30
|
+
this.handleRoute();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initialize design system adapter
|
|
34
|
+
*/
|
|
35
|
+
async initAdapter() {
|
|
36
|
+
try {
|
|
37
|
+
await this.adapter.init(this.basePath);
|
|
38
|
+
console.log(`[Trailhead] Initialized ${this.adapter.name} adapter v${this.adapter.version}`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error("Failed to initialize design system adapter:", error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create shell API
|
|
47
|
+
*/
|
|
48
|
+
createAPI() {
|
|
49
|
+
const adapter = this.adapter;
|
|
50
|
+
return {
|
|
51
|
+
version: "1.0.0",
|
|
52
|
+
feedback: {
|
|
53
|
+
busy: (message) => adapter.feedback.showBusy(message),
|
|
54
|
+
clear: () => adapter.feedback.clearBusy(),
|
|
55
|
+
success: (message, duration) => adapter.feedback.showToast(message, "success", duration),
|
|
56
|
+
error: (message, duration) => adapter.feedback.showToast(message, "error", duration || 5000),
|
|
57
|
+
warning: (message, duration) => adapter.feedback.showToast(message, "warning", duration || 4000),
|
|
58
|
+
info: (message, duration) => adapter.feedback.showToast(message, "info", duration),
|
|
59
|
+
alert: (message, variant = "info", duration) => adapter.feedback.showToast(message, variant, duration),
|
|
60
|
+
confirm: (message, title = "Confirm") => adapter.feedback.showDialog({
|
|
61
|
+
message,
|
|
62
|
+
title,
|
|
63
|
+
buttons: [
|
|
64
|
+
{ label: "Cancel", value: "cancel", variant: "secondary" },
|
|
65
|
+
{ label: "Confirm", value: "confirm", variant: "primary" },
|
|
66
|
+
],
|
|
67
|
+
}).then((result) => result.value === "confirm"),
|
|
68
|
+
ok: (message, title = "Information") => adapter.feedback.showDialog({
|
|
69
|
+
message,
|
|
70
|
+
title,
|
|
71
|
+
buttons: [{ label: "OK", value: "ok", variant: "primary" }],
|
|
72
|
+
}).then(() => undefined),
|
|
73
|
+
yesNo: (message, title = "Confirm") => adapter.feedback.showDialog({
|
|
74
|
+
message,
|
|
75
|
+
title,
|
|
76
|
+
buttons: [
|
|
77
|
+
{ label: "No", value: "no", variant: "secondary" },
|
|
78
|
+
{ label: "Yes", value: "yes", variant: "primary" },
|
|
79
|
+
],
|
|
80
|
+
}).then((result) => result.value === "yes"),
|
|
81
|
+
yesNoCancel: (message, title = "Confirm") => adapter.feedback.showDialog({
|
|
82
|
+
message,
|
|
83
|
+
title,
|
|
84
|
+
buttons: [
|
|
85
|
+
{ label: "Cancel", value: "cancel", variant: "secondary" },
|
|
86
|
+
{ label: "No", value: "no", variant: "secondary" },
|
|
87
|
+
{ label: "Yes", value: "yes", variant: "primary" },
|
|
88
|
+
],
|
|
89
|
+
}).then((result) => result.value || "cancel"),
|
|
90
|
+
custom: (message, title, buttons) => adapter.feedback.showDialog({ message, title, buttons })
|
|
91
|
+
.then((result) => result.value),
|
|
92
|
+
},
|
|
93
|
+
http: {
|
|
94
|
+
get: http.get,
|
|
95
|
+
post: http.post,
|
|
96
|
+
put: http.put,
|
|
97
|
+
patch: http.patch,
|
|
98
|
+
delete: http.del,
|
|
99
|
+
},
|
|
100
|
+
navigation: {
|
|
101
|
+
navigate: (path) => this.navigate(path),
|
|
102
|
+
getCurrentPath: () => window.location.pathname,
|
|
103
|
+
onRouteChange: (callback) => {
|
|
104
|
+
this.routeChangeCallbacks.push(callback);
|
|
105
|
+
return () => {
|
|
106
|
+
const index = this.routeChangeCallbacks.indexOf(callback);
|
|
107
|
+
if (index > -1) {
|
|
108
|
+
this.routeChangeCallbacks.splice(index, 1);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load navigation configuration
|
|
117
|
+
*/
|
|
118
|
+
async loadNavigation() {
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch(`${this.basePath}/navigation.json`);
|
|
121
|
+
this.navigation = await response.json();
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.error("Failed to load navigation:", error);
|
|
125
|
+
this.navigation = [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Render navigation menu
|
|
130
|
+
*/
|
|
131
|
+
renderNavigation() {
|
|
132
|
+
const nav = document.getElementById("shell-navigation");
|
|
133
|
+
if (!nav)
|
|
134
|
+
return;
|
|
135
|
+
nav.innerHTML = this.navigation
|
|
136
|
+
.map((item) => `
|
|
137
|
+
<a href="${this.basePath}${item.path}"
|
|
138
|
+
class="shell-nav-item"
|
|
139
|
+
data-path="${item.path}"
|
|
140
|
+
data-app="${item.app}">
|
|
141
|
+
<i class="shell-icon shell-icon-${item.icon}"></i>
|
|
142
|
+
<span class="shell-nav-label">${item.label}</span>
|
|
143
|
+
</a>
|
|
144
|
+
`)
|
|
145
|
+
.join("");
|
|
146
|
+
nav.querySelectorAll("a").forEach((link) => {
|
|
147
|
+
link.addEventListener("click", (e) => {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
const path = link.getAttribute("data-path");
|
|
150
|
+
if (path) {
|
|
151
|
+
this.navigate(path);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Setup routing
|
|
158
|
+
*/
|
|
159
|
+
setupRouting() {
|
|
160
|
+
window.addEventListener("popstate", () => {
|
|
161
|
+
this.handleRoute();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Navigate to path
|
|
166
|
+
*/
|
|
167
|
+
navigate(path) {
|
|
168
|
+
window.location.href = this.basePath + path;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Handle route change
|
|
172
|
+
*/
|
|
173
|
+
handleRoute() {
|
|
174
|
+
let path = window.location.pathname;
|
|
175
|
+
if (this.basePath && path.startsWith(this.basePath)) {
|
|
176
|
+
path = path.substring(this.basePath.length) || "/";
|
|
177
|
+
}
|
|
178
|
+
const navItem = this.navigation.find((item) => path.startsWith(item.path));
|
|
179
|
+
if (navItem) {
|
|
180
|
+
this.loadPlugin(navItem.app, navItem.path);
|
|
181
|
+
this.updateActiveNav(navItem.path);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Update active navigation item
|
|
186
|
+
*/
|
|
187
|
+
updateActiveNav(path) {
|
|
188
|
+
const nav = document.getElementById("shell-navigation");
|
|
189
|
+
if (!nav)
|
|
190
|
+
return;
|
|
191
|
+
nav.querySelectorAll("a").forEach((link) => {
|
|
192
|
+
if (link.getAttribute("data-path") === path) {
|
|
193
|
+
link.classList.add("shell-nav-item-active");
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
link.classList.remove("shell-nav-item-active");
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Load plugin application
|
|
202
|
+
*/
|
|
203
|
+
async loadPlugin(appName, appPath) {
|
|
204
|
+
const root = document.getElementById("shell-content");
|
|
205
|
+
if (!root)
|
|
206
|
+
return;
|
|
207
|
+
root.innerHTML = `<div class="shell-loading">${t("Loading...")}</div>`;
|
|
208
|
+
try {
|
|
209
|
+
const pluginUrl = `${this.basePath}${appPath}/app.js`;
|
|
210
|
+
const pluginCss = `${this.basePath}${appPath}/${appName}.css`;
|
|
211
|
+
// Load CSS
|
|
212
|
+
const link = document.createElement("link");
|
|
213
|
+
link.rel = "stylesheet";
|
|
214
|
+
link.href = pluginCss;
|
|
215
|
+
document.head.appendChild(link);
|
|
216
|
+
// Load JS
|
|
217
|
+
const script = document.createElement("script");
|
|
218
|
+
script.src = pluginUrl;
|
|
219
|
+
script.type = "module";
|
|
220
|
+
script.onload = () => {
|
|
221
|
+
root.innerHTML = "";
|
|
222
|
+
if (window.AppMount) {
|
|
223
|
+
window.AppMount(root);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
script.onerror = () => {
|
|
227
|
+
root.innerHTML = `<div class="shell-error">${t("Failed to load application")}: ${appName}</div>`;
|
|
228
|
+
};
|
|
229
|
+
document.body.appendChild(script);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.error("Failed to load plugin:", error);
|
|
233
|
+
root.innerHTML = `<div class="shell-error">${t("Failed to load application")}</div>`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Shell API - Only these types are exposed to plugin applications
|
|
3
|
+
* This file explicitly defines what's public vs internal
|
|
4
|
+
*/
|
|
5
|
+
export type { ShellAPI, FeedbackAPI, AlertVariant, HttpAPI, RequestOptions, Result, SuccessResult, ErrorResult, HttpError, NavigationAPI, NavItem, } from './shell-api.js';
|
|
6
|
+
//# sourceMappingURL=public-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../src/types/public-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EACV,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,OAAO,EACP,cAAc,EACd,MAAM,EACN,aAAa,EACb,WAAW,EACX,SAAS,EACT,aAAa,EACb,OAAO,GACR,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell API Interface
|
|
3
|
+
* Exposed to plugin applications via window.shell
|
|
4
|
+
*/
|
|
5
|
+
export interface ShellAPI {
|
|
6
|
+
version: string;
|
|
7
|
+
feedback: FeedbackAPI;
|
|
8
|
+
http: HttpAPI;
|
|
9
|
+
navigation: NavigationAPI;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Feedback system API
|
|
13
|
+
*/
|
|
14
|
+
export interface FeedbackAPI {
|
|
15
|
+
busy(message: string): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
success(message: string, duration?: number): void;
|
|
18
|
+
error(message: string, duration?: number): void;
|
|
19
|
+
warning(message: string, duration?: number): void;
|
|
20
|
+
info(message: string, duration?: number): void;
|
|
21
|
+
alert(message: string, variant?: AlertVariant, duration?: number): void;
|
|
22
|
+
confirm(message: string, title?: string): Promise<boolean>;
|
|
23
|
+
ok(message: string, title?: string): Promise<void>;
|
|
24
|
+
yesNo(message: string, title?: string): Promise<boolean>;
|
|
25
|
+
yesNoCancel(message: string, title?: string): Promise<"yes" | "no" | "cancel">;
|
|
26
|
+
custom<T extends string>(message: string, title: string, buttons: Array<{
|
|
27
|
+
label: string;
|
|
28
|
+
value: T;
|
|
29
|
+
variant?: string;
|
|
30
|
+
}>): Promise<T | null>;
|
|
31
|
+
}
|
|
32
|
+
export type AlertVariant = "success" | "error" | "warning" | "info";
|
|
33
|
+
/**
|
|
34
|
+
* HTTP client API with automatic feedback orchestration
|
|
35
|
+
*/
|
|
36
|
+
export interface HttpAPI {
|
|
37
|
+
get<T = any>(url: string, options?: RequestOptions): Promise<Result<T>>;
|
|
38
|
+
post<T = any>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
39
|
+
put<T = any>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
40
|
+
patch<T = any>(url: string, data?: any, options?: RequestOptions): Promise<Result<T>>;
|
|
41
|
+
delete<T = any>(url: string, options?: RequestOptions): Promise<Result<T>>;
|
|
42
|
+
}
|
|
43
|
+
export interface RequestOptions {
|
|
44
|
+
requestKey?: string;
|
|
45
|
+
busyMessage?: string;
|
|
46
|
+
successMessage?: string;
|
|
47
|
+
showSuccess?: boolean;
|
|
48
|
+
noFeedback?: boolean;
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
export interface SuccessResult<T> {
|
|
52
|
+
success: true;
|
|
53
|
+
data: T;
|
|
54
|
+
requestKey?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ErrorResult {
|
|
57
|
+
success: false;
|
|
58
|
+
error: HttpError;
|
|
59
|
+
requestKey?: string;
|
|
60
|
+
}
|
|
61
|
+
export type Result<T> = SuccessResult<T> | ErrorResult;
|
|
62
|
+
export interface HttpError {
|
|
63
|
+
name: string;
|
|
64
|
+
message: string;
|
|
65
|
+
status?: number;
|
|
66
|
+
data?: any;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Navigation API
|
|
70
|
+
*/
|
|
71
|
+
export interface NavigationAPI {
|
|
72
|
+
navigate(path: string): void;
|
|
73
|
+
getCurrentPath(): string;
|
|
74
|
+
onRouteChange(callback: (path: string) => void): () => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Navigation configuration
|
|
78
|
+
*/
|
|
79
|
+
export interface NavItem {
|
|
80
|
+
id: string;
|
|
81
|
+
path: string;
|
|
82
|
+
app: string;
|
|
83
|
+
icon: string;
|
|
84
|
+
label: string;
|
|
85
|
+
order: number;
|
|
86
|
+
badge?: () => number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Global window extension
|
|
90
|
+
*/
|
|
91
|
+
declare global {
|
|
92
|
+
interface Window {
|
|
93
|
+
shell: ShellAPI;
|
|
94
|
+
AppMount?: (container: HTMLElement) => void;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=shell-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-api.d.ts","sourceRoot":"","sources":["../../src/types/shell-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC,CAAC;IAC/E,MAAM,CAAC,CAAC,SAAS,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAC5D,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,CAAC,CAAC;IACR,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,IAAI,MAAM,CAAC;IACzB,aAAa,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7D;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,KAAK,EAAE,QAAQ,CAAC;QAChB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,KAAK,IAAI,CAAC;KAC7C;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@herdingbits/trailhead-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Core shell orchestration for Trailhead micro-frontend framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clean": "rm -rf dist ../types/*.d.ts ../types/adapters",
|
|
20
|
+
"build": "npm run clean && tsc && npm run build:types",
|
|
21
|
+
"build:types": "tsc src/types/public-api.ts --declaration --emitDeclarationOnly --outDir ../types --skipLibCheck && tsc src/adapters/public-api.ts --declaration --emitDeclarationOnly --outDir ../types/adapters --skipLibCheck",
|
|
22
|
+
"test": "vitest"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"ky": "^1.14.3"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"typescript": "^5.9.3",
|
|
29
|
+
"vitest": "^4.0.18"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"micro-frontend",
|
|
33
|
+
"shell",
|
|
34
|
+
"trailhead"
|
|
35
|
+
],
|
|
36
|
+
"author": "HerdingBits",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/herdingbits/trailhead.git",
|
|
44
|
+
"directory": "packages/core"
|
|
45
|
+
}
|
|
46
|
+
}
|