@antelopejs/dms-frontend 0.3.2 → 0.3.4

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.
@@ -299,6 +299,24 @@ interface DmsPluginRegistration {
299
299
  clientOnly: boolean;
300
300
  }
301
301
 
302
+ /**
303
+ * What the frontend modules registered through their SDK. Modules are set up
304
+ * by descending priority, so the first one to claim a name keeps it.
305
+ */
306
+ interface DmsFrontendRegistry {
307
+ components: Map<string, DmsComponentRegistration>;
308
+ pages: Map<string, DmsFrontendEntry>;
309
+ dynamicPages: Map<string, DmsFrontendEntry>;
310
+ layouts: Map<string, DmsFrontendEntry>;
311
+ errorPage?: DmsFrontendEntry;
312
+ plugins: Plugin[];
313
+ pluginSetups: DmsPluginRegistration[];
314
+ middleware: DmsMiddleware[];
315
+ namedMiddleware: Map<string, DmsMiddleware>;
316
+ accessRedirects: Map<string, string>;
317
+ injections: Map<string | symbol, unknown>;
318
+ }
319
+
302
320
  export interface DmsFrontendRuntime {
303
321
  sharedState: Map<string, Ref<unknown>>;
304
322
  asyncData: Map<string, DmsAsyncData<unknown>>;
@@ -324,17 +342,25 @@ export interface DmsFrontendRuntime {
324
342
  scope: EffectScope;
325
343
  }
326
344
 
327
- const components = new Map<string, DmsComponentRegistration>();
328
- const pages = new Map<string, DmsFrontendEntry>();
329
- const dynamicPages = new Map<string, DmsFrontendEntry>();
330
- const layouts = new Map<string, DmsFrontendEntry>();
331
- let errorPage: DmsFrontendEntry | undefined;
332
- const plugins: Plugin[] = [];
333
- const pluginSetups: DmsPluginRegistration[] = [];
334
- const middleware: DmsMiddleware[] = [];
335
- const namedMiddleware = new Map<string, DmsMiddleware>();
336
- const accessRedirects = new Map<string, string>();
337
- const injections = new Map<string | symbol, unknown>();
345
+ /** Creates the empty registry one setup of the frontend modules fills. */
346
+ function createDmsFrontendRegistry(): DmsFrontendRegistry {
347
+ return {
348
+ components: new Map(),
349
+ pages: new Map(),
350
+ dynamicPages: new Map(),
351
+ layouts: new Map(),
352
+ plugins: [],
353
+ pluginSetups: [],
354
+ middleware: [],
355
+ namedMiddleware: new Map(),
356
+ accessRedirects: new Map(),
357
+ injections: new Map(),
358
+ };
359
+ }
360
+
361
+ // Replaced, never refilled, by each setup of the modules: see
362
+ // `setupFrontendModules`.
363
+ let registry = createDmsFrontendRegistry();
338
364
  const runtimeConfig = ref<DmsRuntimeConfig>({
339
365
  public: {},
340
366
  } as DmsRuntimeConfig);
@@ -506,9 +532,9 @@ async function runMiddleware(
506
532
  const declared = Array.isArray(to.meta.middleware) ? to.meta.middleware : [];
507
533
  const metadataNames = Object.keys(to.meta).filter((name) => to.meta[name]);
508
534
  const declaredHandlers = [...declared, ...metadataNames]
509
- .map((name) => namedMiddleware.get(String(name)))
535
+ .map((name) => registry.namedMiddleware.get(String(name)))
510
536
  .filter((handler): handler is DmsMiddleware => !!handler);
511
- for (const handler of [...middleware, ...declaredHandlers]) {
537
+ for (const handler of [...registry.middleware, ...declaredHandlers]) {
512
538
  const runWithContext = runtimeContexts.get(runtime);
513
539
  const result = await (runWithContext
514
540
  ? runWithContext(() => handler(to, runtime.route))
@@ -913,8 +939,8 @@ export function addDmsMiddleware(
913
939
  name?: string,
914
940
  isGlobal = false,
915
941
  ): void {
916
- if (isGlobal) middleware.push(handler);
917
- else if (name) namedMiddleware.set(name, handler);
942
+ if (isGlobal) registry.middleware.push(handler);
943
+ else if (name) registry.namedMiddleware.set(name, handler);
918
944
  }
919
945
  export const useHead = useUnhead;
920
946
  export const useSeoMeta = useUnheadSeoMeta;
@@ -1158,13 +1184,20 @@ export function useUserSession<
1158
1184
  };
1159
1185
  }
1160
1186
 
1187
+ function registerDmsComponentEntry(
1188
+ entries: Map<string, DmsComponentRegistration>,
1189
+ name: string,
1190
+ component: Component,
1191
+ ): void {
1192
+ const key = normalizeDmsName(name);
1193
+ if (!entries.has(key)) entries.set(key, { name, component });
1194
+ }
1161
1195
  /** Registers a component unless a higher-priority module already owns its name. */
1162
1196
  export function registerDmsComponent(name: string, component: Component): void {
1163
- const key = normalizeDmsName(name);
1164
- if (!components.has(key)) components.set(key, { name, component });
1197
+ registerDmsComponentEntry(registry.components, name, component);
1165
1198
  }
1166
1199
  export function resolveDmsComponent(name: string): Component | undefined {
1167
- return components.get(normalizeDmsName(name))?.component;
1200
+ return registry.components.get(normalizeDmsName(name))?.component;
1168
1201
  }
1169
1202
  export const getDmsComponent = resolveDmsComponent;
1170
1203
 
@@ -1177,7 +1210,7 @@ export const getDmsComponent = resolveDmsComponent;
1177
1210
  */
1178
1211
  export function trackDmsAsyncComponents(app: App): Set<string> {
1179
1212
  const names = new Map<Component, string>();
1180
- components.forEach(({ component, name }) => {
1213
+ registry.components.forEach(({ component, name }) => {
1181
1214
  if ((component as DmsAsyncComponent).__asyncLoader)
1182
1215
  names.set(component, name);
1183
1216
  });
@@ -1293,30 +1326,35 @@ function findDmsPageEntry(props: DmsPageProps): DmsFrontendEntry | undefined {
1293
1326
  props.page.componentName,
1294
1327
  props.page.route?.fullSlug,
1295
1328
  props.path,
1296
- ].find((candidate) => candidate && pages.has(normalizeDmsPageKey(candidate)));
1329
+ ].find(
1330
+ (candidate) =>
1331
+ candidate && registry.pages.has(normalizeDmsPageKey(candidate)),
1332
+ );
1297
1333
  return name
1298
- ? pages.get(normalizeDmsPageKey(name))
1299
- : dynamicPages.get(CATCH_ALL_PAGE_KEY);
1334
+ ? registry.pages.get(normalizeDmsPageKey(name))
1335
+ : registry.dynamicPages.get(CATCH_ALL_PAGE_KEY);
1300
1336
  }
1301
1337
 
1302
1338
  /** The path a frontend module registered for a typed backend refusal. */
1303
1339
  export function resolveDmsAccessRedirect(code: string): string | undefined {
1304
- return accessRedirects.get(code);
1340
+ return registry.accessRedirects.get(code);
1305
1341
  }
1306
1342
  export function hasDmsPage(name: string): boolean {
1307
- return pages.has(normalizeDmsPageKey(name));
1343
+ return registry.pages.has(normalizeDmsPageKey(name));
1308
1344
  }
1309
1345
  export function getDmsPage(props: DmsPageProps): Component | undefined {
1310
- if (props.error) return errorPage?.component;
1346
+ if (props.error) return registry.errorPage?.component;
1311
1347
  return findDmsPageEntry(props)?.component;
1312
1348
  }
1313
1349
  export function getDmsDynamicPage(name = "default"): Component | undefined {
1314
- return dynamicPages.get(name)?.component;
1350
+ return registry.dynamicPages.get(name)?.component;
1315
1351
  }
1316
1352
  export function getDmsLayout(props: DmsPageProps): Component | undefined {
1317
1353
  const layout = props.page.layout;
1318
1354
  const name = layout?.layout?.componentName ?? layout?.componentName;
1319
- return name ? layouts.get(normalizeDmsName(name))?.component : undefined;
1355
+ return name
1356
+ ? registry.layouts.get(normalizeDmsName(name))?.component
1357
+ : undefined;
1320
1358
  }
1321
1359
  export function getDmsLayoutProps(
1322
1360
  props: DmsPageProps,
@@ -1362,18 +1400,18 @@ export function hydrateDmsPageProps(props: DmsPageProps, url?: string): void {
1362
1400
  layoutUrl && props.page.layout ? { [layoutUrl]: props.page.layout } : {};
1363
1401
  }
1364
1402
  export function getDmsErrorPage(): Component | undefined {
1365
- return errorPage?.component;
1403
+ return registry.errorPage?.component;
1366
1404
  }
1367
1405
 
1368
1406
  export async function preloadDmsPage(props: DmsPageProps): Promise<void> {
1369
1407
  const layoutName =
1370
1408
  props.page.layout?.layout?.componentName ??
1371
1409
  props.page.layout?.componentName;
1372
- const page = findDmsPageEntry(props) ?? dynamicPages.get("default");
1410
+ const page = findDmsPageEntry(props) ?? registry.dynamicPages.get("default");
1373
1411
  const layout = layoutName
1374
- ? layouts.get(normalizeDmsName(layoutName))
1412
+ ? registry.layouts.get(normalizeDmsName(layoutName))
1375
1413
  : undefined;
1376
- const entries = props.error ? [errorPage] : [page, layout];
1414
+ const entries = props.error ? [registry.errorPage] : [page, layout];
1377
1415
  // `preload` only warms the module; the async wrapper stays unresolved until
1378
1416
  // its own loader runs. Hydrating an unresolved wrapper defers it, and the
1379
1417
  // first parent render (Inertia's initial swap) then makes Vue drop the
@@ -1394,58 +1432,85 @@ export function useDmsInjection<T>(
1394
1432
  }
1395
1433
 
1396
1434
  function registerDmsFrontendEntry(
1397
- registry: Map<string, DmsFrontendEntry>,
1435
+ entries: Map<string, DmsFrontendEntry>,
1398
1436
  name: string,
1399
1437
  component: Component,
1400
1438
  preload?: DmsComponentPreloader,
1401
1439
  ): void {
1402
- if (!registry.has(name)) registry.set(name, { component, preload });
1440
+ if (!entries.has(name)) entries.set(name, { component, preload });
1403
1441
  }
1404
1442
 
1405
- function createSdk(options: DmsModuleOptions): DmsFrontendSdk {
1443
+ function createSdk(
1444
+ target: DmsFrontendRegistry,
1445
+ options: DmsModuleOptions,
1446
+ ): DmsFrontendSdk {
1406
1447
  return {
1407
1448
  options,
1408
- registerComponent: registerDmsComponent,
1449
+ registerComponent: (name, component) =>
1450
+ registerDmsComponentEntry(target.components, name, component),
1409
1451
  registerPage: (name, component, preload) => {
1410
1452
  const key = normalizeDmsPageKey(name);
1411
- registerDmsFrontendEntry(pages, key, component, preload);
1453
+ registerDmsFrontendEntry(target.pages, key, component, preload);
1412
1454
  },
1413
1455
  registerDynamicPage: (name, component, preload) =>
1414
- registerDmsFrontendEntry(dynamicPages, name, component, preload),
1456
+ registerDmsFrontendEntry(target.dynamicPages, name, component, preload),
1415
1457
  registerLayout: (name, component, preload) => {
1416
1458
  const key = normalizeDmsName(name);
1417
- registerDmsFrontendEntry(layouts, key, component, preload);
1459
+ registerDmsFrontendEntry(target.layouts, key, component, preload);
1418
1460
  },
1419
1461
  registerErrorPage: (component, preload) => {
1420
- errorPage ??= { component, preload };
1462
+ target.errorPage ??= { component, preload };
1421
1463
  },
1422
1464
  registerPlugin: (setup, options = {}) =>
1423
- pluginSetups.push({ setup, clientOnly: options.clientOnly ?? false }),
1465
+ target.pluginSetups.push({
1466
+ setup,
1467
+ clientOnly: options.clientOnly ?? false,
1468
+ }),
1424
1469
  registerMiddleware: (name, handler, options = {}) => {
1425
- if (!namedMiddleware.has(name)) namedMiddleware.set(name, handler);
1426
- if (options.global) middleware.push(handler);
1470
+ if (!target.namedMiddleware.has(name))
1471
+ target.namedMiddleware.set(name, handler);
1472
+ if (options.global) target.middleware.push(handler);
1427
1473
  },
1428
1474
  registerAccessRedirect: (code, path) => {
1429
- if (!accessRedirects.has(code)) accessRedirects.set(code, path);
1475
+ if (!target.accessRedirects.has(code))
1476
+ target.accessRedirects.set(code, path);
1430
1477
  },
1431
1478
  provide: (key, value) => {
1432
- if (!injections.has(key)) injections.set(key, value);
1479
+ if (!target.injections.has(key)) target.injections.set(key, value);
1433
1480
  },
1434
- use: (plugin) => plugins.push(plugin),
1481
+ use: (plugin) => target.plugins.push(plugin),
1435
1482
  };
1436
1483
  }
1437
1484
 
1438
- /** Initializes frontend adapters in their generated priority order. */
1485
+ /**
1486
+ * Sets the frontend modules up in their generated priority order, into a
1487
+ * registry of their own that replaces the previous one once complete.
1488
+ *
1489
+ * The browser and a production server set the modules up once. A development
1490
+ * server sets them up again each time Vite evaluates the server renderer anew
1491
+ * after a layer changes, but keeps this module and its state: registering into
1492
+ * the previous registry, the new components lost their names to the ones they
1493
+ * replace, so the server kept rendering the old ones, and every list gained the
1494
+ * same entries once more. A render still in progress keeps reading a complete
1495
+ * registry: the new one is only published once every module is set up.
1496
+ */
1439
1497
  export async function setupFrontendModules(
1440
1498
  registrations: DmsFrontendModuleRegistration[],
1441
1499
  ): Promise<void> {
1500
+ const next = createDmsFrontendRegistry();
1501
+ // Modules read and extend these two while they are set up (the DMS layout
1502
+ // merges its app config into `useDmsAppConfig()`), so they restart empty
1503
+ // here rather than being published with the registry.
1504
+ runtimeConfig.value.public = {} as PublicRuntimeConfig;
1505
+ appConfig.value = {};
1442
1506
  for (const registration of registrations) {
1443
1507
  runtimeConfig.value.public = defu(
1444
1508
  runtimeConfig.value.public,
1445
1509
  registration.options.public,
1446
1510
  );
1447
- await registration.module.setup(createSdk(registration.options));
1511
+ await registration.module.setup(createSdk(next, registration.options));
1448
1512
  }
1513
+ registry = next;
1449
1514
  }
1450
1515
 
1451
1516
  export async function installDmsPlugins(
@@ -1502,16 +1567,16 @@ export async function installDmsPlugins(
1502
1567
  );
1503
1568
  app.component("DmsLink", DmsLink);
1504
1569
  app.component("DmsClientOnly", DmsClientOnly);
1505
- components.forEach(({ component, name }) => {
1570
+ registry.components.forEach(({ component, name }) => {
1506
1571
  app.component(name, component);
1507
1572
  });
1508
- injections.forEach((value, key) => {
1573
+ registry.injections.forEach((value, key) => {
1509
1574
  app.provide(key, value);
1510
1575
  });
1511
- plugins.forEach((plugin) => {
1576
+ registry.plugins.forEach((plugin) => {
1512
1577
  app.use(plugin);
1513
1578
  });
1514
- for (const registration of pluginSetups) {
1579
+ for (const registration of registry.pluginSetups) {
1515
1580
  if (registration.clientOnly && typeof window === "undefined") continue;
1516
1581
  await runtime.scope.run(() =>
1517
1582
  app.runWithContext(() => registration.setup(appContext)),