@dnzn/dxkit 0.1.0
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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/index.cjs +589 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +425 -0
- package/dist/index.d.ts +425 -0
- package/dist/index.global.js +582 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +557 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createEventBus: () => createEventBus,
|
|
24
|
+
createEventRegistry: () => createEventRegistry,
|
|
25
|
+
createLifecycleManager: () => createLifecycleManager,
|
|
26
|
+
createPluginRegistry: () => createPluginRegistry,
|
|
27
|
+
createRouter: () => createRouter,
|
|
28
|
+
createShell: () => createShell
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/events.ts
|
|
33
|
+
var SHELL_EVENTS = /* @__PURE__ */ new Set([
|
|
34
|
+
"dx:ready",
|
|
35
|
+
"dx:route:changed",
|
|
36
|
+
"dx:dapp:mounted",
|
|
37
|
+
"dx:dapp:unmounted",
|
|
38
|
+
"dx:dapp:enabled",
|
|
39
|
+
"dx:dapp:disabled",
|
|
40
|
+
"dx:mount",
|
|
41
|
+
"dx:unmount",
|
|
42
|
+
"dx:error",
|
|
43
|
+
"dx:plugin:registered",
|
|
44
|
+
"dx:event:registered"
|
|
45
|
+
]);
|
|
46
|
+
function createEventBus(target = window) {
|
|
47
|
+
const handlers = {};
|
|
48
|
+
function emit(event, detail) {
|
|
49
|
+
target.dispatchEvent(new CustomEvent(event, { detail }));
|
|
50
|
+
}
|
|
51
|
+
function on(event, handler) {
|
|
52
|
+
let paused = false;
|
|
53
|
+
const wrapper = (e) => {
|
|
54
|
+
if (!paused) handler(e.detail);
|
|
55
|
+
};
|
|
56
|
+
const key = event;
|
|
57
|
+
if (!handlers[key]) {
|
|
58
|
+
handlers[key] = /* @__PURE__ */ new Map();
|
|
59
|
+
}
|
|
60
|
+
handlers[key].set(handler, wrapper);
|
|
61
|
+
target.addEventListener(key, wrapper);
|
|
62
|
+
return {
|
|
63
|
+
off: () => off(event, handler),
|
|
64
|
+
get paused() {
|
|
65
|
+
return paused;
|
|
66
|
+
},
|
|
67
|
+
pause() {
|
|
68
|
+
paused = true;
|
|
69
|
+
},
|
|
70
|
+
resume() {
|
|
71
|
+
paused = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function once(event, handler) {
|
|
76
|
+
const listener = on(event, (detail) => {
|
|
77
|
+
listener.off();
|
|
78
|
+
handler(detail);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function off(event, handler) {
|
|
82
|
+
const map = handlers[event];
|
|
83
|
+
if (!map) return;
|
|
84
|
+
const wrapper = map.get(handler);
|
|
85
|
+
if (wrapper) {
|
|
86
|
+
target.removeEventListener(event, wrapper);
|
|
87
|
+
map.delete(handler);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { emit, on, once, off };
|
|
91
|
+
}
|
|
92
|
+
function createEventRegistry(bus) {
|
|
93
|
+
const registered = /* @__PURE__ */ new Map();
|
|
94
|
+
function registerEvent(source, events) {
|
|
95
|
+
if (!events.length) return;
|
|
96
|
+
const newlyRegistered = [];
|
|
97
|
+
for (const { name, description } of events) {
|
|
98
|
+
if (SHELL_EVENTS.has(name)) {
|
|
99
|
+
throw new Error(`Cannot register built-in shell event: '${name}'`);
|
|
100
|
+
}
|
|
101
|
+
if (name.startsWith("dx:plugin:")) {
|
|
102
|
+
const segments = name.split(":");
|
|
103
|
+
if (segments.length !== 4 || !segments[3]) {
|
|
104
|
+
throw new Error(`Invalid plugin event format: '${name}' \u2014 expected 'dx:plugin:<name>:<action>'`);
|
|
105
|
+
}
|
|
106
|
+
if (segments[2] !== source) {
|
|
107
|
+
throw new Error(`Plugin '${source}' cannot register event '${name}' \u2014 namespace mismatch`);
|
|
108
|
+
}
|
|
109
|
+
} else if (name.startsWith("dx:")) {
|
|
110
|
+
throw new Error(`Event '${name}' uses reserved dx: prefix \u2014 plugins must use 'dx:plugin:${source}:<action>'`);
|
|
111
|
+
}
|
|
112
|
+
const existing = registered.get(name);
|
|
113
|
+
if (existing) {
|
|
114
|
+
if (existing.source === source) continue;
|
|
115
|
+
throw new Error(`Event '${name}' already registered by '${existing.source}'`);
|
|
116
|
+
}
|
|
117
|
+
registered.set(name, { name, source, description });
|
|
118
|
+
newlyRegistered.push(name);
|
|
119
|
+
}
|
|
120
|
+
if (newlyRegistered.length) {
|
|
121
|
+
bus.emit("dx:event:registered", { source, events: newlyRegistered });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function getRegisteredEvents() {
|
|
125
|
+
return Array.from(registered.values());
|
|
126
|
+
}
|
|
127
|
+
function isRegistered(event) {
|
|
128
|
+
return registered.has(event);
|
|
129
|
+
}
|
|
130
|
+
return { registerEvent, getRegisteredEvents, isRegistered };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/lifecycle.ts
|
|
134
|
+
function defaultScriptLoader() {
|
|
135
|
+
const loaded = /* @__PURE__ */ new Set();
|
|
136
|
+
return (src) => {
|
|
137
|
+
if (loaded.has(src)) return Promise.resolve();
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const script = document.createElement("script");
|
|
140
|
+
script.type = "module";
|
|
141
|
+
script.src = src;
|
|
142
|
+
script.onload = () => {
|
|
143
|
+
loaded.add(src);
|
|
144
|
+
resolve();
|
|
145
|
+
};
|
|
146
|
+
script.onerror = () => {
|
|
147
|
+
reject(new Error(`Failed to load dapp script: ${src}`));
|
|
148
|
+
};
|
|
149
|
+
document.head.appendChild(script);
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function defaultStyleLoader() {
|
|
154
|
+
const loaded = /* @__PURE__ */ new Set();
|
|
155
|
+
return (href) => {
|
|
156
|
+
if (loaded.has(href)) return Promise.resolve();
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const link = document.createElement("link");
|
|
159
|
+
link.rel = "stylesheet";
|
|
160
|
+
link.href = href;
|
|
161
|
+
link.onload = () => {
|
|
162
|
+
loaded.add(href);
|
|
163
|
+
resolve();
|
|
164
|
+
};
|
|
165
|
+
link.onerror = () => {
|
|
166
|
+
reject(new Error(`Failed to load dapp styles: ${href}`));
|
|
167
|
+
};
|
|
168
|
+
document.head.appendChild(link);
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function createLifecycleManager(events, options = {}) {
|
|
173
|
+
const loadScript = options.scriptLoader ?? defaultScriptLoader();
|
|
174
|
+
const loadStyle = options.styleLoader ?? defaultStyleLoader();
|
|
175
|
+
const hasPlugin = options.hasPlugin ?? (() => true);
|
|
176
|
+
let currentDappId = null;
|
|
177
|
+
async function mount(manifest, container, path) {
|
|
178
|
+
if (currentDappId) {
|
|
179
|
+
unmount();
|
|
180
|
+
}
|
|
181
|
+
if (manifest.requires?.plugins?.length) {
|
|
182
|
+
const missing = manifest.requires.plugins.filter((p) => !hasPlugin(p));
|
|
183
|
+
if (missing.length > 0) {
|
|
184
|
+
events.emit("dx:error", {
|
|
185
|
+
source: `lifecycle:${manifest.id}`,
|
|
186
|
+
error: new Error(`Missing required plugin(s): ${missing.join(", ")}`)
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (manifest.styles) {
|
|
192
|
+
try {
|
|
193
|
+
await loadStyle(manifest.styles);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
events.emit("dx:error", {
|
|
196
|
+
source: `lifecycle:${manifest.id}:styles`,
|
|
197
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await loadScript(manifest.entry);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
events.emit("dx:error", {
|
|
205
|
+
source: `lifecycle:${manifest.id}`,
|
|
206
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
currentDappId = manifest.id;
|
|
211
|
+
events.emit("dx:mount", { id: manifest.id, container, path: path ?? manifest.route });
|
|
212
|
+
events.emit("dx:dapp:mounted", { id: manifest.id });
|
|
213
|
+
}
|
|
214
|
+
function unmount() {
|
|
215
|
+
if (!currentDappId) return;
|
|
216
|
+
const id = currentDappId;
|
|
217
|
+
events.emit("dx:unmount", { id });
|
|
218
|
+
events.emit("dx:dapp:unmounted", { id });
|
|
219
|
+
currentDappId = null;
|
|
220
|
+
}
|
|
221
|
+
function getCurrentDapp() {
|
|
222
|
+
return currentDappId;
|
|
223
|
+
}
|
|
224
|
+
function destroy() {
|
|
225
|
+
if (currentDappId) unmount();
|
|
226
|
+
}
|
|
227
|
+
return { mount, unmount, getCurrentDapp, destroy };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/registry.ts
|
|
231
|
+
function createPluginRegistry() {
|
|
232
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
233
|
+
function register(name, plugin) {
|
|
234
|
+
plugins.set(name, plugin);
|
|
235
|
+
}
|
|
236
|
+
function get(name) {
|
|
237
|
+
return plugins.get(name);
|
|
238
|
+
}
|
|
239
|
+
function has(name) {
|
|
240
|
+
return plugins.has(name);
|
|
241
|
+
}
|
|
242
|
+
function getAll() {
|
|
243
|
+
const result = {};
|
|
244
|
+
for (const [name, plugin] of plugins) {
|
|
245
|
+
result[name] = plugin;
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
return { register, get, has, getAll };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/router.ts
|
|
253
|
+
function createRouter(config) {
|
|
254
|
+
const { mode, basePath, manifests } = config;
|
|
255
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
256
|
+
function normalizePath(path) {
|
|
257
|
+
let normalized = path;
|
|
258
|
+
if (basePath !== "/" && normalized.startsWith(basePath)) {
|
|
259
|
+
normalized = normalized.slice(basePath.length) || "/";
|
|
260
|
+
}
|
|
261
|
+
if (!normalized.startsWith("/")) normalized = `/${normalized}`;
|
|
262
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
263
|
+
normalized = normalized.slice(0, -1);
|
|
264
|
+
}
|
|
265
|
+
return normalized;
|
|
266
|
+
}
|
|
267
|
+
function resolve(path) {
|
|
268
|
+
const normalized = normalizePath(path);
|
|
269
|
+
const sorted = [...manifests].sort((a, b) => b.route.length - a.route.length);
|
|
270
|
+
for (const manifest of sorted) {
|
|
271
|
+
if (normalized === manifest.route || normalized.startsWith(`${manifest.route}/`)) {
|
|
272
|
+
return manifest;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
function readCurrentPath() {
|
|
278
|
+
if (mode === "hash") {
|
|
279
|
+
const hash = window.location.hash.slice(1);
|
|
280
|
+
return hash || "/";
|
|
281
|
+
}
|
|
282
|
+
return window.location.pathname;
|
|
283
|
+
}
|
|
284
|
+
function getCurrentPath() {
|
|
285
|
+
return normalizePath(readCurrentPath());
|
|
286
|
+
}
|
|
287
|
+
function navigate(path) {
|
|
288
|
+
const fullPath = basePath === "/" ? path : basePath + path;
|
|
289
|
+
if (mode === "hash") {
|
|
290
|
+
window.location.hash = `#${fullPath}`;
|
|
291
|
+
} else {
|
|
292
|
+
window.history.pushState(null, "", fullPath);
|
|
293
|
+
}
|
|
294
|
+
notifyListeners();
|
|
295
|
+
}
|
|
296
|
+
function notifyListeners() {
|
|
297
|
+
const manifest = resolve(readCurrentPath());
|
|
298
|
+
for (const handler of listeners) {
|
|
299
|
+
handler(manifest);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function onRouteChange(handler) {
|
|
303
|
+
listeners.add(handler);
|
|
304
|
+
return () => listeners.delete(handler);
|
|
305
|
+
}
|
|
306
|
+
const onPopState = () => notifyListeners();
|
|
307
|
+
window.addEventListener("popstate", onPopState);
|
|
308
|
+
const onHashChange = mode === "hash" ? () => notifyListeners() : null;
|
|
309
|
+
if (onHashChange) {
|
|
310
|
+
window.addEventListener("hashchange", onHashChange);
|
|
311
|
+
}
|
|
312
|
+
function destroy() {
|
|
313
|
+
window.removeEventListener("popstate", onPopState);
|
|
314
|
+
if (onHashChange) {
|
|
315
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
316
|
+
}
|
|
317
|
+
listeners.clear();
|
|
318
|
+
}
|
|
319
|
+
return { resolve, navigate, getCurrentPath, onRouteChange, destroy };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/utils.ts
|
|
323
|
+
function deepMerge(a, b) {
|
|
324
|
+
const result = { ...a };
|
|
325
|
+
for (const key of Object.keys(b)) {
|
|
326
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
327
|
+
const val = b[key];
|
|
328
|
+
if (val !== void 0 && val !== null && typeof val === "object" && !Array.isArray(val) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
|
|
329
|
+
result[key] = deepMerge(result[key], val);
|
|
330
|
+
} else if (val !== void 0) {
|
|
331
|
+
result[key] = val;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/shell.ts
|
|
338
|
+
function createShell(config = {}) {
|
|
339
|
+
const {
|
|
340
|
+
plugins = {},
|
|
341
|
+
dapps: dappEntries,
|
|
342
|
+
manifests: inlineManifests,
|
|
343
|
+
registryUrl = "/registry.json",
|
|
344
|
+
basePath = "/",
|
|
345
|
+
mode = "history",
|
|
346
|
+
scriptLoader,
|
|
347
|
+
styleLoader
|
|
348
|
+
} = config;
|
|
349
|
+
const events = createEventBus();
|
|
350
|
+
const eventRegistry = createEventRegistry(events);
|
|
351
|
+
const registry = createPluginRegistry();
|
|
352
|
+
const lifecycle = createLifecycleManager(events, {
|
|
353
|
+
hasPlugin: (name) => registry.has(name),
|
|
354
|
+
scriptLoader,
|
|
355
|
+
styleLoader
|
|
356
|
+
});
|
|
357
|
+
let manifests = [];
|
|
358
|
+
let router = createRouter({ mode, basePath, manifests: [] });
|
|
359
|
+
let mountContainer = null;
|
|
360
|
+
let routeUnsub = null;
|
|
361
|
+
let initialized = false;
|
|
362
|
+
const enabledState = /* @__PURE__ */ new Map();
|
|
363
|
+
function getEnabledManifests() {
|
|
364
|
+
return manifests.filter((m) => {
|
|
365
|
+
if (!m.optional) return true;
|
|
366
|
+
return enabledState.get(m.id) ?? true;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function initEnabledState() {
|
|
370
|
+
for (const m of manifests) {
|
|
371
|
+
if (m.optional) {
|
|
372
|
+
enabledState.set(m.id, m.enabled !== false);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const settingsPlugin = registry.get("settings");
|
|
376
|
+
if (!settingsPlugin || !("getSettingsAPI" in settingsPlugin)) return;
|
|
377
|
+
const api = settingsPlugin.getSettingsAPI();
|
|
378
|
+
for (const m of manifests) {
|
|
379
|
+
if (m.optional) {
|
|
380
|
+
const persisted = api.get("_shell", m.id);
|
|
381
|
+
if (persisted !== void 0) {
|
|
382
|
+
enabledState.set(m.id, persisted);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function rebuildRouter() {
|
|
388
|
+
const currentDapp = lifecycle.getCurrentDapp();
|
|
389
|
+
if (routeUnsub) {
|
|
390
|
+
routeUnsub();
|
|
391
|
+
routeUnsub = null;
|
|
392
|
+
}
|
|
393
|
+
router.destroy();
|
|
394
|
+
router = createRouter({ mode, basePath, manifests: getEnabledManifests() });
|
|
395
|
+
routeUnsub = router.onRouteChange(handleRouteChange);
|
|
396
|
+
if (currentDapp) {
|
|
397
|
+
const stillEnabled = getEnabledManifests().some((m) => m.id === currentDapp);
|
|
398
|
+
if (!stillEnabled) {
|
|
399
|
+
lifecycle.unmount();
|
|
400
|
+
router.navigate("/");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function enableDapp(id) {
|
|
405
|
+
const manifest = manifests.find((m) => m.id === id);
|
|
406
|
+
if (!manifest?.optional) return;
|
|
407
|
+
if (enabledState.get(id) === true) return;
|
|
408
|
+
enabledState.set(id, true);
|
|
409
|
+
if (initialized) rebuildRouter();
|
|
410
|
+
events.emit("dx:dapp:enabled", { id });
|
|
411
|
+
}
|
|
412
|
+
function disableDapp(id) {
|
|
413
|
+
const manifest = manifests.find((m) => m.id === id);
|
|
414
|
+
if (!manifest?.optional) return;
|
|
415
|
+
if (enabledState.get(id) === false) return;
|
|
416
|
+
enabledState.set(id, false);
|
|
417
|
+
if (initialized) rebuildRouter();
|
|
418
|
+
events.emit("dx:dapp:disabled", { id });
|
|
419
|
+
}
|
|
420
|
+
function isDappEnabled(id) {
|
|
421
|
+
const manifest = manifests.find((m) => m.id === id);
|
|
422
|
+
if (!manifest) return false;
|
|
423
|
+
if (!manifest.optional) return true;
|
|
424
|
+
return enabledState.get(id) ?? true;
|
|
425
|
+
}
|
|
426
|
+
const context = {
|
|
427
|
+
events,
|
|
428
|
+
eventRegistry,
|
|
429
|
+
router: {
|
|
430
|
+
navigate: (path) => router.navigate(path),
|
|
431
|
+
getCurrentPath: () => router.getCurrentPath()
|
|
432
|
+
},
|
|
433
|
+
getPlugin: (name) => registry.get(name),
|
|
434
|
+
getPlugins: () => registry.getAll(),
|
|
435
|
+
getManifests: () => [...manifests],
|
|
436
|
+
getEnabledManifests: () => getEnabledManifests(),
|
|
437
|
+
enableDapp,
|
|
438
|
+
disableDapp,
|
|
439
|
+
isDappEnabled
|
|
440
|
+
};
|
|
441
|
+
function isValidManifest(m) {
|
|
442
|
+
return m && typeof m.id === "string" && typeof m.route === "string" && typeof m.entry === "string" && m.nav && typeof m.nav.label === "string";
|
|
443
|
+
}
|
|
444
|
+
async function loadDappManifest(entry) {
|
|
445
|
+
try {
|
|
446
|
+
const res = await fetch(entry.manifest);
|
|
447
|
+
if (!res.ok) return null;
|
|
448
|
+
const base = await res.json();
|
|
449
|
+
if (!isValidManifest(base)) {
|
|
450
|
+
events.emit("dx:error", {
|
|
451
|
+
source: "shell:manifest",
|
|
452
|
+
error: new Error(
|
|
453
|
+
`Invalid manifest from ${entry.manifest} \u2014 missing required fields (id, route, entry, nav.label)`
|
|
454
|
+
)
|
|
455
|
+
});
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
if (entry.overrides) {
|
|
459
|
+
return deepMerge(base, entry.overrides);
|
|
460
|
+
}
|
|
461
|
+
return base;
|
|
462
|
+
} catch {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async function loadManifests() {
|
|
467
|
+
if (dappEntries?.length) {
|
|
468
|
+
const results = await Promise.all(dappEntries.map(loadDappManifest));
|
|
469
|
+
return results.filter((m) => m !== null);
|
|
470
|
+
}
|
|
471
|
+
if (inlineManifests) {
|
|
472
|
+
return inlineManifests;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
const res = await fetch(registryUrl);
|
|
476
|
+
if (res.ok) {
|
|
477
|
+
return await res.json();
|
|
478
|
+
}
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
async function init() {
|
|
484
|
+
if (initialized) return;
|
|
485
|
+
for (const [name, plugin] of Object.entries(plugins)) {
|
|
486
|
+
registry.register(name, plugin);
|
|
487
|
+
events.emit("dx:plugin:registered", { name });
|
|
488
|
+
}
|
|
489
|
+
manifests = await loadManifests();
|
|
490
|
+
for (const [name, plugin] of Object.entries(plugins)) {
|
|
491
|
+
if (plugin.init) {
|
|
492
|
+
try {
|
|
493
|
+
await plugin.init(context);
|
|
494
|
+
} catch (err) {
|
|
495
|
+
events.emit("dx:error", {
|
|
496
|
+
source: `plugin:${name}`,
|
|
497
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
initEnabledState();
|
|
503
|
+
router.destroy();
|
|
504
|
+
router = createRouter({ mode, basePath, manifests: getEnabledManifests() });
|
|
505
|
+
routeUnsub = router.onRouteChange(handleRouteChange);
|
|
506
|
+
Object.freeze(context);
|
|
507
|
+
window.__DXKIT__ = context;
|
|
508
|
+
initialized = true;
|
|
509
|
+
const initial = router.resolve(router.getCurrentPath());
|
|
510
|
+
if (initial) {
|
|
511
|
+
await mountDapp(initial);
|
|
512
|
+
}
|
|
513
|
+
events.emit("dx:ready", {});
|
|
514
|
+
}
|
|
515
|
+
async function handleRouteChange(manifest) {
|
|
516
|
+
if (manifest) {
|
|
517
|
+
await mountDapp(manifest);
|
|
518
|
+
} else {
|
|
519
|
+
lifecycle.unmount();
|
|
520
|
+
}
|
|
521
|
+
events.emit("dx:route:changed", {
|
|
522
|
+
path: router.getCurrentPath(),
|
|
523
|
+
manifest: manifest ?? void 0
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
async function mountDapp(manifest) {
|
|
527
|
+
if (lifecycle.getCurrentDapp() === manifest.id) return;
|
|
528
|
+
const container = getMountContainer();
|
|
529
|
+
if (!container) return;
|
|
530
|
+
await lifecycle.mount(manifest, container, router.getCurrentPath());
|
|
531
|
+
}
|
|
532
|
+
function getMountContainer() {
|
|
533
|
+
if (mountContainer) return mountContainer;
|
|
534
|
+
mountContainer = document.getElementById("dx-mount");
|
|
535
|
+
return mountContainer;
|
|
536
|
+
}
|
|
537
|
+
function getPlugin(name) {
|
|
538
|
+
return registry.get(name);
|
|
539
|
+
}
|
|
540
|
+
function getManifests() {
|
|
541
|
+
return [...manifests];
|
|
542
|
+
}
|
|
543
|
+
function navigate(path) {
|
|
544
|
+
router.navigate(path);
|
|
545
|
+
}
|
|
546
|
+
function getCurrentRoute() {
|
|
547
|
+
return router.getCurrentPath();
|
|
548
|
+
}
|
|
549
|
+
function destroy() {
|
|
550
|
+
lifecycle.destroy();
|
|
551
|
+
if (routeUnsub) {
|
|
552
|
+
routeUnsub();
|
|
553
|
+
routeUnsub = null;
|
|
554
|
+
}
|
|
555
|
+
router.destroy();
|
|
556
|
+
for (const plugin of Object.values(registry.getAll())) {
|
|
557
|
+
if (plugin.destroy) {
|
|
558
|
+
plugin.destroy();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (window.__DXKIT__ === context) {
|
|
562
|
+
delete window.__DXKIT__;
|
|
563
|
+
}
|
|
564
|
+
mountContainer = null;
|
|
565
|
+
initialized = false;
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
init,
|
|
569
|
+
getPlugin,
|
|
570
|
+
getManifests,
|
|
571
|
+
getEnabledManifests,
|
|
572
|
+
enableDapp,
|
|
573
|
+
disableDapp,
|
|
574
|
+
isDappEnabled,
|
|
575
|
+
navigate,
|
|
576
|
+
getCurrentRoute,
|
|
577
|
+
destroy
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
581
|
+
0 && (module.exports = {
|
|
582
|
+
createEventBus,
|
|
583
|
+
createEventRegistry,
|
|
584
|
+
createLifecycleManager,
|
|
585
|
+
createPluginRegistry,
|
|
586
|
+
createRouter,
|
|
587
|
+
createShell
|
|
588
|
+
});
|
|
589
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/events.ts","../src/lifecycle.ts","../src/registry.ts","../src/router.ts","../src/utils.ts","../src/shell.ts"],"sourcesContent":["// Types\n\nexport { createEventBus, createEventRegistry } from './events.js';\nexport type { LifecycleManagerOptions, ScriptLoader, StyleLoader } from './lifecycle.js';\nexport { createLifecycleManager } from './lifecycle.js';\nexport { createPluginRegistry } from './registry.js';\nexport { createRouter } from './router.js';\n// Shell\nexport { createShell } from './shell.js';\nexport type {\n Auth,\n AuthState,\n Context,\n DappEntry,\n DappManifest,\n EventBus,\n EventMap,\n EventRegistration,\n EventRegistry,\n Listener,\n Plugin,\n RegisteredEvent,\n SettingDefinition,\n Settings,\n SettingsSection,\n Shell,\n ShellConfig,\n Theme,\n ThemeMode,\n Wallet,\n WalletProvider,\n WalletState,\n} from './types/index.js';\n","import type { EventBus, EventMap, EventRegistration, EventRegistry, Listener, RegisteredEvent } from './types/index.js';\n\n/** Static shell event names — cannot be registered at runtime. */\nconst SHELL_EVENTS = new Set<string>([\n 'dx:ready',\n 'dx:route:changed',\n 'dx:dapp:mounted',\n 'dx:dapp:unmounted',\n 'dx:dapp:enabled',\n 'dx:dapp:disabled',\n 'dx:mount',\n 'dx:unmount',\n 'dx:error',\n 'dx:plugin:registered',\n 'dx:event:registered',\n]);\n\n/**\n * Creates a typed event bus backed by window.CustomEvent.\n *\n * All events are dispatched on the provided target (defaults to window)\n * using the `dx:*` namespace. Handlers receive the typed `detail` payload.\n */\nexport function createEventBus(target: EventTarget = window): EventBus {\n // Maps original handler → wrapper so we can removeEventListener with the same ref\n const handlers: Record<string, Map<(...args: any[]) => any, (e: Event) => void>> = {};\n\n function emit<K extends keyof EventMap>(event: K, detail: EventMap[K]): void {\n target.dispatchEvent(new CustomEvent(event as string, { detail }));\n }\n\n function on<K extends keyof EventMap>(event: K, handler: (detail: EventMap[K]) => void): Listener {\n // Paused listeners stay subscribed but silently drop events\n let paused = false;\n const wrapper = (e: Event) => {\n if (!paused) handler((e as CustomEvent).detail);\n };\n\n const key = event as string;\n if (!handlers[key]) {\n handlers[key] = new Map();\n }\n handlers[key].set(handler, wrapper);\n\n target.addEventListener(key, wrapper);\n return {\n off: () => off(event, handler),\n get paused() {\n return paused;\n },\n pause() {\n paused = true;\n },\n resume() {\n paused = false;\n },\n };\n }\n\n function once<K extends keyof EventMap>(event: K, handler: (detail: EventMap[K]) => void): void {\n const listener = on(event, (detail) => {\n listener.off();\n handler(detail);\n });\n }\n\n function off<K extends keyof EventMap>(event: K, handler: (detail: EventMap[K]) => void): void {\n const map = handlers[event as string];\n if (!map) return;\n\n const wrapper = map.get(handler);\n if (wrapper) {\n target.removeEventListener(event as string, wrapper);\n map.delete(handler);\n }\n }\n\n return { emit, on, once, off };\n}\n\n/**\n * Creates an event registry for runtime event registration and introspection.\n *\n * Namespace rules:\n * - `dx:plugin:<name>:<action>` — plugin events, name must match source\n * - No `dx:` prefix — dapp/developer events, any source\n * - `dx:*` without `dx:plugin:` prefix — reserved, rejected\n */\nexport function createEventRegistry(bus: EventBus): EventRegistry {\n const registered = new Map<string, RegisteredEvent>();\n\n function registerEvent(source: string, events: EventRegistration[]): void {\n if (!events.length) return;\n\n const newlyRegistered: string[] = [];\n\n for (const { name, description } of events) {\n // Reject built-in shell events\n if (SHELL_EVENTS.has(name)) {\n throw new Error(`Cannot register built-in shell event: '${name}'`);\n }\n\n // dx: prefix reserved for shell; plugins must use dx:plugin:<name>:<action>\n if (name.startsWith('dx:plugin:')) {\n const segments = name.split(':');\n if (segments.length !== 4 || !segments[3]) {\n throw new Error(`Invalid plugin event format: '${name}' — expected 'dx:plugin:<name>:<action>'`);\n }\n if (segments[2] !== source) {\n throw new Error(`Plugin '${source}' cannot register event '${name}' — namespace mismatch`);\n }\n } else if (name.startsWith('dx:')) {\n // Reserved dx: namespace (not dx:plugin:)\n throw new Error(`Event '${name}' uses reserved dx: prefix — plugins must use 'dx:plugin:${source}:<action>'`);\n }\n\n // Same source re-registering is a no-op; different source is a conflict\n const existing = registered.get(name);\n if (existing) {\n if (existing.source === source) continue;\n throw new Error(`Event '${name}' already registered by '${existing.source}'`);\n }\n\n registered.set(name, { name, source, description });\n newlyRegistered.push(name);\n }\n\n if (newlyRegistered.length) {\n bus.emit('dx:event:registered', { source, events: newlyRegistered });\n }\n }\n\n function getRegisteredEvents(): RegisteredEvent[] {\n return Array.from(registered.values());\n }\n\n function isRegistered(event: string): boolean {\n return registered.has(event);\n }\n\n return { registerEvent, getRegisteredEvents, isRegistered };\n}\n","import type { DappManifest, EventBus } from './types/index.js';\n\nexport interface LifecycleManager {\n mount(manifest: DappManifest, container: HTMLElement, path?: string): Promise<void>;\n unmount(): void;\n getCurrentDapp(): string | null;\n destroy(): void;\n}\n\nexport type ScriptLoader = (src: string) => Promise<void>;\nexport type StyleLoader = (href: string) => Promise<void>;\n\n/** Default script loader — injects a <script type=\"module\"> into the DOM. */\nfunction defaultScriptLoader(): ScriptLoader {\n const loaded = new Set<string>();\n\n return (src: string) => {\n if (loaded.has(src)) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.type = 'module';\n script.src = src;\n script.onload = () => {\n loaded.add(src);\n resolve();\n };\n script.onerror = () => {\n reject(new Error(`Failed to load dapp script: ${src}`));\n };\n document.head.appendChild(script);\n });\n };\n}\n\n/** Default style loader — injects a <link rel=\"stylesheet\"> into the DOM. */\nfunction defaultStyleLoader(): StyleLoader {\n const loaded = new Set<string>();\n\n return (href: string) => {\n if (loaded.has(href)) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = href;\n link.onload = () => {\n loaded.add(href);\n resolve();\n };\n link.onerror = () => {\n reject(new Error(`Failed to load dapp styles: ${href}`));\n };\n document.head.appendChild(link);\n });\n };\n}\n\nexport interface LifecycleManagerOptions {\n /** Override the script loader (useful for testing). */\n scriptLoader?: ScriptLoader;\n /** Override the style loader (useful for testing). */\n styleLoader?: StyleLoader;\n /** Check if a named plugin is registered. Used for permission enforcement. */\n hasPlugin?: (name: string) => boolean;\n}\n\nexport function createLifecycleManager(events: EventBus, options: LifecycleManagerOptions = {}): LifecycleManager {\n const loadScript = options.scriptLoader ?? defaultScriptLoader();\n const loadStyle = options.styleLoader ?? defaultStyleLoader();\n const hasPlugin = options.hasPlugin ?? (() => true);\n let currentDappId: string | null = null;\n\n async function mount(manifest: DappManifest, container: HTMLElement, path?: string): Promise<void> {\n // Unmount current dapp if any\n if (currentDappId) {\n unmount();\n }\n\n // requires.plugins lists required plugin names — missing plugin → dx:error, skip mount\n if (manifest.requires?.plugins?.length) {\n const missing = manifest.requires.plugins.filter((p) => !hasPlugin(p));\n if (missing.length > 0) {\n events.emit('dx:error', {\n source: `lifecycle:${manifest.id}`,\n error: new Error(`Missing required plugin(s): ${missing.join(', ')}`),\n });\n return;\n }\n }\n\n // CSS failures are non-blocking — emit error but continue mount\n if (manifest.styles) {\n try {\n await loadStyle(manifest.styles);\n } catch (err) {\n events.emit('dx:error', {\n source: `lifecycle:${manifest.id}:styles`,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n // Entry scripts load as ES modules\n try {\n await loadScript(manifest.entry);\n } catch (err) {\n events.emit('dx:error', {\n source: `lifecycle:${manifest.id}`,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n return;\n }\n\n currentDappId = manifest.id;\n\n // Dapp contract: listen for dx:mount on container, render into it, listen for dx:unmount to teardown\n events.emit('dx:mount', { id: manifest.id, container, path: path ?? manifest.route });\n events.emit('dx:dapp:mounted', { id: manifest.id });\n }\n\n function unmount(): void {\n if (!currentDappId) return;\n\n const id = currentDappId;\n events.emit('dx:unmount', { id });\n events.emit('dx:dapp:unmounted', { id });\n currentDappId = null;\n }\n\n function getCurrentDapp(): string | null {\n return currentDappId;\n }\n\n function destroy(): void {\n if (currentDappId) unmount();\n }\n\n return { mount, unmount, getCurrentDapp, destroy };\n}\n","import type { Plugin } from './types/index.js';\n\nexport interface PluginRegistry {\n register(name: string, plugin: Plugin): void;\n get<T extends Plugin>(name: string): T | undefined;\n has(name: string): boolean;\n getAll(): Record<string, Plugin>;\n}\n\nexport function createPluginRegistry(): PluginRegistry {\n const plugins = new Map<string, Plugin>();\n\n function register(name: string, plugin: Plugin): void {\n plugins.set(name, plugin);\n }\n\n function get<T extends Plugin>(name: string): T | undefined {\n return plugins.get(name) as T | undefined;\n }\n\n function has(name: string): boolean {\n return plugins.has(name);\n }\n\n function getAll(): Record<string, Plugin> {\n const result: Record<string, Plugin> = {};\n for (const [name, plugin] of plugins) {\n result[name] = plugin;\n }\n return result;\n }\n\n return { register, get, has, getAll };\n}\n","import type { DappManifest } from './types/index.js';\n\nexport interface Router {\n resolve(path: string): DappManifest | null;\n navigate(path: string): void;\n getCurrentPath(): string;\n onRouteChange(handler: (manifest: DappManifest | null) => void): () => void;\n destroy(): void;\n}\n\nexport interface RouterConfig {\n mode: 'history' | 'hash';\n basePath: string;\n manifests: DappManifest[];\n}\n\nexport function createRouter(config: RouterConfig): Router {\n const { mode, basePath, manifests } = config;\n const listeners = new Set<(manifest: DappManifest | null) => void>();\n\n // Strip basePath prefix, ensure leading slash, remove trailing slash\n function normalizePath(path: string): string {\n let normalized = path;\n if (basePath !== '/' && normalized.startsWith(basePath)) {\n normalized = normalized.slice(basePath.length) || '/';\n }\n // Ensure leading slash\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n // Remove trailing slash (except root)\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n }\n\n function resolve(path: string): DappManifest | null {\n const normalized = normalizePath(path);\n\n // Longest prefix wins — /tools/sender matches before /tools\n const sorted = [...manifests].sort((a, b) => b.route.length - a.route.length);\n\n for (const manifest of sorted) {\n if (normalized === manifest.route || normalized.startsWith(`${manifest.route}/`)) {\n return manifest;\n }\n }\n\n return null;\n }\n\n function readCurrentPath(): string {\n if (mode === 'hash') {\n const hash = window.location.hash.slice(1); // strip '#'\n return hash || '/';\n }\n return window.location.pathname;\n }\n\n function getCurrentPath(): string {\n return normalizePath(readCurrentPath());\n }\n\n function navigate(path: string): void {\n const fullPath = basePath === '/' ? path : basePath + path;\n\n if (mode === 'hash') {\n window.location.hash = `#${fullPath}`;\n } else {\n window.history.pushState(null, '', fullPath);\n }\n\n notifyListeners();\n }\n\n function notifyListeners(): void {\n const manifest = resolve(readCurrentPath());\n for (const handler of listeners) {\n handler(manifest);\n }\n }\n\n function onRouteChange(handler: (manifest: DappManifest | null) => void): () => void {\n listeners.add(handler);\n return () => listeners.delete(handler);\n }\n\n // Listen for browser navigation (back/forward)\n const onPopState = () => notifyListeners();\n window.addEventListener('popstate', onPopState);\n\n // Hash mode also needs hashchange\n const onHashChange = mode === 'hash' ? () => notifyListeners() : null;\n if (onHashChange) {\n window.addEventListener('hashchange', onHashChange);\n }\n\n function destroy(): void {\n window.removeEventListener('popstate', onPopState);\n if (onHashChange) {\n window.removeEventListener('hashchange', onHashChange);\n }\n listeners.clear();\n }\n\n return { resolve, navigate, getCurrentPath, onRouteChange, destroy };\n}\n","/** Deep-merge b into a. Arrays in b replace a (no concatenation). Null/undefined values are skipped. */\nexport function deepMerge<T extends Record<string, any>>(a: T, b: Partial<T>): T {\n const result = { ...a };\n for (const key of Object.keys(b) as (keyof T)[]) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n const val = b[key];\n if (\n val !== undefined &&\n val !== null &&\n typeof val === 'object' &&\n !Array.isArray(val) &&\n typeof result[key] === 'object' &&\n result[key] !== null &&\n !Array.isArray(result[key])\n ) {\n result[key] = deepMerge(result[key] as any, val as any);\n } else if (val !== undefined) {\n result[key] = val as any;\n }\n }\n return result;\n}\n","import { createEventBus, createEventRegistry } from './events.js';\nimport { createLifecycleManager } from './lifecycle.js';\nimport { createPluginRegistry } from './registry.js';\nimport { createRouter } from './router.js';\nimport type { Context, DappEntry, DappManifest, Plugin, Settings, Shell, ShellConfig } from './types/index.js';\nimport { deepMerge } from './utils.js';\n\n/**\n * Creates a shell instance for composable dapp development.\n *\n * The shell manages routing, plugin lifecycle, event bus, and dapp\n * mount/unmount orchestration. It owns zero DOM — the developer provides\n * the layout and mount container.\n */\nexport function createShell(config: ShellConfig = {}): Shell {\n const {\n plugins = {},\n dapps: dappEntries,\n manifests: inlineManifests,\n registryUrl = '/registry.json',\n basePath = '/',\n mode = 'history',\n scriptLoader,\n styleLoader,\n } = config;\n\n const events = createEventBus();\n const eventRegistry = createEventRegistry(events);\n const registry = createPluginRegistry();\n const lifecycle = createLifecycleManager(events, {\n hasPlugin: (name: string) => registry.has(name),\n scriptLoader,\n styleLoader,\n });\n let manifests: DappManifest[] = [];\n let router = createRouter({ mode, basePath, manifests: [] });\n let mountContainer: HTMLElement | null = null;\n let routeUnsub: (() => void) | null = null;\n let initialized = false;\n const enabledState = new Map<string, boolean>();\n\n function getEnabledManifests(): DappManifest[] {\n return manifests.filter((m) => {\n if (!m.optional) return true;\n return enabledState.get(m.id) ?? true;\n });\n }\n\n function initEnabledState(): void {\n // Start with manifest defaults\n for (const m of manifests) {\n if (m.optional) {\n enabledState.set(m.id, m.enabled !== false);\n }\n }\n\n // Override with persisted values from settings plugin (if available)\n // Duck-type check — settings plugin exposes getSettingsAPI() by convention\n const settingsPlugin = registry.get<Plugin>('settings');\n if (!settingsPlugin || !('getSettingsAPI' in settingsPlugin)) return;\n\n const api = (settingsPlugin as any).getSettingsAPI() as Settings;\n // Persisted values override manifest defaults for optional dapps\n for (const m of manifests) {\n if (m.optional) {\n const persisted = api.get<boolean>('_shell', m.id);\n if (persisted !== undefined) {\n enabledState.set(m.id, persisted);\n }\n }\n }\n }\n\n // Full rebuild required — router is immutable once created\n function rebuildRouter(): void {\n const currentDapp = lifecycle.getCurrentDapp();\n\n if (routeUnsub) {\n routeUnsub();\n routeUnsub = null;\n }\n router.destroy();\n\n router = createRouter({ mode, basePath, manifests: getEnabledManifests() });\n routeUnsub = router.onRouteChange(handleRouteChange);\n\n // If the current dapp was disabled, unmount and return to root\n if (currentDapp) {\n const stillEnabled = getEnabledManifests().some((m) => m.id === currentDapp);\n if (!stillEnabled) {\n lifecycle.unmount();\n router.navigate('/');\n }\n }\n }\n\n function enableDapp(id: string): void {\n const manifest = manifests.find((m) => m.id === id);\n if (!manifest?.optional) return;\n if (enabledState.get(id) === true) return;\n\n enabledState.set(id, true);\n if (initialized) rebuildRouter();\n events.emit('dx:dapp:enabled', { id });\n }\n\n function disableDapp(id: string): void {\n const manifest = manifests.find((m) => m.id === id);\n if (!manifest?.optional) return;\n if (enabledState.get(id) === false) return;\n\n enabledState.set(id, false);\n if (initialized) rebuildRouter();\n events.emit('dx:dapp:disabled', { id });\n }\n\n function isDappEnabled(id: string): boolean {\n const manifest = manifests.find((m) => m.id === id);\n if (!manifest) return false;\n if (!manifest.optional) return true;\n return enabledState.get(id) ?? true;\n }\n\n // Build the context that plugins and dapps see\n const context: Context = {\n events,\n eventRegistry,\n router: {\n navigate: (path: string) => router.navigate(path),\n getCurrentPath: () => router.getCurrentPath(),\n },\n getPlugin: <T extends Plugin>(name: string) => registry.get<T>(name),\n getPlugins: () => registry.getAll(),\n getManifests: () => [...manifests],\n getEnabledManifests: () => getEnabledManifests(),\n enableDapp,\n disableDapp,\n isDappEnabled,\n };\n\n /** Rejects manifests missing fields that would cause silent downstream failures. */\n function isValidManifest(m: any): m is DappManifest {\n return (\n m &&\n typeof m.id === 'string' &&\n typeof m.route === 'string' &&\n typeof m.entry === 'string' &&\n m.nav &&\n typeof m.nav.label === 'string'\n );\n }\n\n async function loadDappManifest(entry: DappEntry): Promise<DappManifest | null> {\n try {\n const res = await fetch(entry.manifest);\n if (!res.ok) return null;\n const base = await res.json();\n if (!isValidManifest(base)) {\n events.emit('dx:error', {\n source: 'shell:manifest',\n error: new Error(\n `Invalid manifest from ${entry.manifest} — missing required fields (id, route, entry, nav.label)`,\n ),\n });\n return null;\n }\n if (entry.overrides) {\n return deepMerge(base, entry.overrides);\n }\n return base;\n } catch {\n return null;\n }\n }\n\n // Three-tier fallback: dapp entries → inline manifests → registry.json\n async function loadManifests(): Promise<DappManifest[]> {\n if (dappEntries?.length) {\n const results = await Promise.all(dappEntries.map(loadDappManifest));\n return results.filter((m): m is DappManifest => m !== null);\n }\n\n if (inlineManifests) {\n return inlineManifests;\n }\n\n try {\n const res = await fetch(registryUrl);\n if (res.ok) {\n return await res.json();\n }\n } catch {\n // No registry.json — that's fine\n }\n\n return [];\n }\n\n async function init(): Promise<void> {\n if (initialized) return;\n\n // Register plugins\n for (const [name, plugin] of Object.entries(plugins)) {\n registry.register(name, plugin);\n events.emit('dx:plugin:registered', { name });\n }\n\n // Manifests load before plugin init so plugins can read them during init()\n manifests = await loadManifests();\n\n // Initialize plugins (after manifests are loaded)\n // Failures are contained — a bad plugin emits dx:error but doesn't crash the shell\n for (const [name, plugin] of Object.entries(plugins)) {\n if (plugin.init) {\n try {\n await plugin.init(context);\n } catch (err) {\n events.emit('dx:error', {\n source: `plugin:${name}`,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n }\n\n // Initialize enabled state from manifest defaults + persisted settings\n initEnabledState();\n\n // Rebuild router with enabled manifests only\n router.destroy();\n router = createRouter({ mode, basePath, manifests: getEnabledManifests() });\n\n // Listen for route changes\n routeUnsub = router.onRouteChange(handleRouteChange);\n\n // Global context bridge — dapps access plugins/events via window.__DXKIT__\n // Frozen to prevent mutation by dapp scripts or third-party code\n Object.freeze(context);\n window.__DXKIT__ = context;\n\n initialized = true;\n\n // Resolve initial route\n const initial = router.resolve(router.getCurrentPath());\n if (initial) {\n await mountDapp(initial);\n }\n\n events.emit('dx:ready', {});\n }\n\n async function handleRouteChange(manifest: DappManifest | null): Promise<void> {\n if (manifest) {\n await mountDapp(manifest);\n } else {\n lifecycle.unmount();\n }\n events.emit('dx:route:changed', {\n path: router.getCurrentPath(),\n manifest: manifest ?? undefined,\n });\n }\n\n async function mountDapp(manifest: DappManifest): Promise<void> {\n // Skip if already mounted\n if (lifecycle.getCurrentDapp() === manifest.id) return;\n\n const container = getMountContainer();\n if (!container) return;\n\n await lifecycle.mount(manifest, container, router.getCurrentPath());\n }\n\n // Lazily resolved — developer must provide <div id=\"dx-mount\"> in their layout\n function getMountContainer(): HTMLElement | null {\n if (mountContainer) return mountContainer;\n mountContainer = document.getElementById('dx-mount');\n return mountContainer;\n }\n\n function getPlugin<T extends Plugin>(name: string): T | undefined {\n return registry.get<T>(name);\n }\n\n function getManifests(): DappManifest[] {\n return [...manifests];\n }\n\n function navigate(path: string): void {\n router.navigate(path);\n }\n\n function getCurrentRoute(): string {\n return router.getCurrentPath();\n }\n\n function destroy(): void {\n lifecycle.destroy();\n\n if (routeUnsub) {\n routeUnsub();\n routeUnsub = null;\n }\n\n router.destroy();\n\n for (const plugin of Object.values(registry.getAll())) {\n if (plugin.destroy) {\n plugin.destroy();\n }\n }\n\n if (window.__DXKIT__ === context) {\n delete window.__DXKIT__;\n }\n\n mountContainer = null;\n initialized = false;\n }\n\n return {\n init,\n getPlugin,\n getManifests,\n getEnabledManifests,\n enableDapp,\n disableDapp,\n isDappEnabled,\n navigate,\n getCurrentRoute,\n destroy,\n };\n}\n"],"mappings":"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","names":[]}
|