@antelopejs/dms 0.9.0 → 0.9.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/data/module-store/catalog.json +71 -0
  3. package/dist/config.d.ts +7 -0
  4. package/dist/config.js +8 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/implementations/dms/frontend-modules.d.ts +2 -0
  7. package/dist/implementations/dms/frontend-modules.js +29 -1
  8. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  9. package/dist/implementations/dms/frontend-ready.d.ts +8 -0
  10. package/dist/implementations/dms/frontend-ready.js +34 -0
  11. package/dist/implementations/dms/frontend-ready.js.map +1 -0
  12. package/dist/implementations/dms/manifest.d.ts +1 -0
  13. package/dist/implementations/dms/manifest.js.map +1 -1
  14. package/dist/implementations/dms/page.d.ts +2 -0
  15. package/dist/implementations/dms/page.js +16 -0
  16. package/dist/implementations/dms/page.js.map +1 -1
  17. package/dist/routes/index.d.ts +1 -0
  18. package/dist/routes/index.js +1 -0
  19. package/dist/routes/index.js.map +1 -1
  20. package/dist/routes/store.d.ts +21 -0
  21. package/dist/routes/store.js +130 -0
  22. package/dist/routes/store.js.map +1 -0
  23. package/dist/store/activity.d.ts +2 -0
  24. package/dist/store/activity.js +12 -0
  25. package/dist/store/activity.js.map +1 -0
  26. package/dist/store/cache.d.ts +8 -0
  27. package/dist/store/cache.js +29 -0
  28. package/dist/store/cache.js.map +1 -0
  29. package/dist/store/catalog.d.ts +4 -0
  30. package/dist/store/catalog.js +69 -0
  31. package/dist/store/catalog.js.map +1 -0
  32. package/dist/store/compatibility.d.ts +3 -0
  33. package/dist/store/compatibility.js +45 -0
  34. package/dist/store/compatibility.js.map +1 -0
  35. package/dist/store/declarations.d.ts +18 -0
  36. package/dist/store/declarations.js +116 -0
  37. package/dist/store/declarations.js.map +1 -0
  38. package/dist/store/frontend-compatibility.d.ts +18 -0
  39. package/dist/store/frontend-compatibility.js +93 -0
  40. package/dist/store/frontend-compatibility.js.map +1 -0
  41. package/dist/store/interfaces.d.ts +6 -0
  42. package/dist/store/interfaces.js +126 -0
  43. package/dist/store/interfaces.js.map +1 -0
  44. package/dist/store/listing.d.ts +2 -0
  45. package/dist/store/listing.js +180 -0
  46. package/dist/store/listing.js.map +1 -0
  47. package/dist/store/operations.d.ts +8 -0
  48. package/dist/store/operations.js +254 -0
  49. package/dist/store/operations.js.map +1 -0
  50. package/dist/store/registry.d.ts +4 -0
  51. package/dist/store/registry.js +61 -0
  52. package/dist/store/registry.js.map +1 -0
  53. package/dist/store/requirements.d.ts +14 -0
  54. package/dist/store/requirements.js +81 -0
  55. package/dist/store/requirements.js.map +1 -0
  56. package/dist/store/runtime.d.ts +11 -0
  57. package/dist/store/runtime.js +78 -0
  58. package/dist/store/runtime.js.map +1 -0
  59. package/dist/store/tarball.d.ts +2 -0
  60. package/dist/store/tarball.js +80 -0
  61. package/dist/store/tarball.js.map +1 -0
  62. package/dist/store/types.d.ts +98 -0
  63. package/dist/store/types.js +3 -0
  64. package/dist/store/types.js.map +1 -0
  65. package/dist/test/unit/store/store.test.d.ts +1 -0
  66. package/dist/test/unit/store/store.test.js +127 -0
  67. package/dist/test/unit/store/store.test.js.map +1 -0
  68. package/dist/validation/store.schema.d.ts +39 -0
  69. package/dist/validation/store.schema.js +27 -0
  70. package/dist/validation/store.schema.js.map +1 -0
  71. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreDependencyPicker.vue +136 -0
  72. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +98 -0
  73. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +247 -29
  74. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +19 -2
  75. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +186 -120
  76. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-install.ts +146 -0
  77. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +109 -0
  78. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +335 -34
  79. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +134 -0
  80. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +107 -9
  81. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +107 -9
  82. package/frontend-vue/package.json +3 -3
  83. package/frontend-vue/pnpm-lock.yaml +16 -22
  84. package/frontend-vue/tests/module-store-catalog.test.ts +270 -51
  85. package/frontend-vue/tests/top-list-card-period-badge-ssr.test.ts +212 -0
  86. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import type { Ref, VNode } from "vue";
2
3
  import { MONO_CHIP_CLASS } from "#dms-ui/app/build/utils/monoChip";
3
4
  import UButton from "@nuxt/ui/components/Button.vue";
4
5
  import UIcon from "@nuxt/ui/runtime/vue/components/Icon.vue";
@@ -13,6 +14,7 @@ import { useKeyboardPlatform } from "#dms-ui/app/composables/global/keyboardPlat
13
14
  import DmsChipGroup from "#dms-ui/app/build/components/form/ChipGroup.vue";
14
15
  import ModuleCatalogTile from "../build/components/pages/modules/ModuleCatalogTile.vue";
15
16
  import ModuleStoreTile from "../build/components/pages/modules/ModuleStoreTile.vue";
17
+ import ModuleStoreDependencyPicker from "../build/components/pages/modules/ModuleStoreDependencyPicker.vue";
16
18
  import { usePageHeaderActions } from "../composables/layout/usePageHeaderActions";
17
19
  import {
18
20
  ENDPOINT_MODULES_LISTING,
@@ -30,9 +32,30 @@ import {
30
32
  type ModuleSortMode,
31
33
  } from "../build/utils/modules-catalog";
32
34
  import {
33
- MODULE_STORE_CATALOG,
34
- availableStoreModules,
35
+ type ModuleStoreEntry,
36
+ type StoreScope,
37
+ FRONTEND_BUILD_COMMAND,
38
+ filterByScope,
39
+ isAwaitingFrontend,
40
+ needsDependencyChoice,
41
+ sortStoreModules,
42
+ toModuleStoreEntry,
35
43
  } from "../build/utils/module-store-catalog";
44
+ import {
45
+ type DependencyDraft,
46
+ areDependencyDraftsValid,
47
+ buildInstallBody,
48
+ createDependencyDrafts,
49
+ } from "../build/utils/module-store-install";
50
+ import { useModuleStore } from "../composables/page/useModuleStore";
51
+ import {
52
+ ConfirmActionError,
53
+ useConfirm,
54
+ } from "#dms-ui/app/composables/confirm";
55
+ import type {
56
+ StoreActionBody,
57
+ StoreOperationKind,
58
+ } from "../types/module-store";
36
59
  import DmsSearchInput from "#dms-ui/app/build/components/form/SearchInput.vue";
37
60
 
38
61
  const SKELETON_PLACEHOLDER_COUNT = 6;
@@ -197,26 +220,49 @@ function clearFilters(): void {
197
220
  category.value = MODULE_CATEGORY_ALL;
198
221
  }
199
222
 
200
- // ── Module store (preview) ─────────────────────────────────────────
201
- // A static list of the official modules not installed here: nothing installs
202
- // yet, only the search and the category chips work, locally.
223
+ // ── Module store ───────────────────────────────────────────────────
224
+ const moduleStore = useModuleStore();
225
+ const toast = useToast();
226
+ const { confirm } = useConfirm();
227
+
228
+ onMounted(() => {
229
+ if (isOwner.value) void moduleStore.load();
230
+ });
231
+ onBeforeUnmount(() => moduleStore.stop());
232
+
233
+ const storeListing = computed(() => moduleStore.listing.value);
234
+ const isStoreLoading = computed(
235
+ () => storeListing.value === undefined && !moduleStore.loadingError.value,
236
+ );
203
237
  const storeModules = computed(() =>
204
- availableStoreModules(
205
- MODULE_STORE_CATALOG,
206
- new Set((modulesListing.modules.value ?? []).map((entry) => entry.id)),
238
+ sortStoreModules(
239
+ (storeListing.value?.modules ?? []).map(toModuleStoreEntry),
207
240
  processI18n,
208
241
  ),
209
242
  );
243
+ const storeScope = ref<StoreScope>("all");
210
244
  const storeQuery = ref("");
211
245
  const storeCategory = ref<string>(MODULE_CATEGORY_ALL);
212
246
 
247
+ const storeScopeItems = computed(() =>
248
+ (["all", "available", "installed"] as const).map((scope) => ({
249
+ value: scope,
250
+ label: t(`modules.store.scope.${scope}`, {
251
+ count: filterByScope(storeModules.value, scope).length,
252
+ }),
253
+ })),
254
+ );
255
+ const scopedStoreModules = computed(() =>
256
+ filterByScope(storeModules.value, storeScope.value),
257
+ );
258
+
213
259
  const storeCategoryOptions = computed(() => [
214
260
  {
215
261
  value: MODULE_CATEGORY_ALL,
216
262
  label: t("modules.category.all"),
217
- count: storeModules.value.length,
263
+ count: scopedStoreModules.value.length,
218
264
  },
219
- ...moduleCategoryOptions(storeModules.value, processI18n),
265
+ ...moduleCategoryOptions(scopedStoreModules.value, processI18n),
220
266
  ]);
221
267
 
222
268
  watch(storeCategoryOptions, (options) => {
@@ -226,7 +272,7 @@ watch(storeCategoryOptions, (options) => {
226
272
  });
227
273
 
228
274
  const visibleStoreModules = computed(() =>
229
- filterModules(storeModules.value, {
275
+ filterModules(scopedStoreModules.value, {
230
276
  query: storeQuery.value,
231
277
  category: storeCategory.value,
232
278
  resolve: processI18n,
@@ -239,6 +285,153 @@ function clearStoreFilters(): void {
239
285
  storeCategory.value = MODULE_CATEGORY_ALL;
240
286
  }
241
287
 
288
+ /** Store modules whose interface the running frontend does not match yet. */
289
+ const frontendLagging = computed(() =>
290
+ storeModules.value.filter(
291
+ (entry) =>
292
+ isAwaitingFrontend(entry) ||
293
+ (entry.frontend === "unknown" &&
294
+ moduleStore.awaitingReload.value.has(entry.id)),
295
+ ),
296
+ );
297
+ const frontendNotice = computed(() => {
298
+ const lagging = frontendLagging.value;
299
+ if (lagging.length === 0) return null;
300
+ const names = lagging.map((entry) => processI18n(entry.title)).join(", ");
301
+ const hasReported = lagging.every((entry) => entry.frontend !== "unknown");
302
+ const key = !hasReported
303
+ ? "unknown"
304
+ : storeListing.value?.dev
305
+ ? "dev"
306
+ : "production";
307
+ return { key, names };
308
+ });
309
+
310
+ /**
311
+ * Runs `kind` from a dialog: a refusal is worded as the toast words it and
312
+ * shown in the dialog, which stays open.
313
+ */
314
+ async function runFromDialog(
315
+ kind: StoreOperationKind,
316
+ entry: ModuleStoreEntry,
317
+ body?: StoreActionBody,
318
+ ): Promise<void> {
319
+ try {
320
+ await moduleStore.run(kind, entry, body);
321
+ } catch (error) {
322
+ throw new ConfirmActionError({
323
+ title: t("modules.store.action_failed", {
324
+ module: processI18n(entry.title),
325
+ }),
326
+ description: resolveStoreError(error),
327
+ });
328
+ }
329
+ }
330
+
331
+ /** What the owner last picked in a module's install dialog, for a retry. */
332
+ const lastDependencyDrafts = new Map<string, DependencyDraft[]>();
333
+
334
+ function renderDependencyPicker(
335
+ entry: ModuleStoreEntry,
336
+ drafts: Ref<DependencyDraft[]>,
337
+ showErrors: boolean,
338
+ ): VNode {
339
+ return h(ModuleStoreDependencyPicker, {
340
+ missing: entry.missingInterfaces,
341
+ drafts: drafts.value,
342
+ showErrors,
343
+ onUpdate: (index: number, patch: Partial<DependencyDraft>) => {
344
+ drafts.value = drafts.value.map((draft, position) =>
345
+ position === index ? { ...draft, ...patch } : draft,
346
+ );
347
+ },
348
+ });
349
+ }
350
+
351
+ /**
352
+ * Asks which modules implement the interfaces the release misses, then
353
+ * installs them with it. A refusal shows in the dialog, kept open.
354
+ */
355
+ async function installWithDependencies(entry: ModuleStoreEntry): Promise<void> {
356
+ const title = processI18n(entry.title);
357
+ const drafts = ref(
358
+ createDependencyDrafts(
359
+ entry.missingInterfaces,
360
+ lastDependencyDrafts.get(entry.id),
361
+ ),
362
+ );
363
+ const showErrors = ref(false);
364
+ await confirm({
365
+ title: t("modules.store.install_dialog.title", { module: title }),
366
+ description: t("modules.store.install_dialog.description", {
367
+ module: title,
368
+ }),
369
+ confirmLabel: t("modules.store.actions.install"),
370
+ confirmIcon: "i-ph-download-simple",
371
+ icon: "i-ph-puzzle-piece",
372
+ body: () => renderDependencyPicker(entry, drafts, showErrors.value),
373
+ validate: () => {
374
+ showErrors.value = true;
375
+ return areDependencyDraftsValid(drafts.value);
376
+ },
377
+ onConfirm: async () => {
378
+ lastDependencyDrafts.set(entry.id, drafts.value);
379
+ await runFromDialog(
380
+ "install",
381
+ entry,
382
+ buildInstallBody(entry.id, drafts.value),
383
+ );
384
+ },
385
+ });
386
+ }
387
+
388
+ async function runStoreAction(
389
+ kind: StoreOperationKind,
390
+ entry: ModuleStoreEntry,
391
+ ): Promise<void> {
392
+ const title = processI18n(entry.title);
393
+ if (needsDependencyChoice(entry, kind)) {
394
+ await installWithDependencies(entry);
395
+ return;
396
+ }
397
+ if (kind === "uninstall") {
398
+ await confirm({
399
+ title: t("modules.store.uninstall_confirm.title", { module: title }),
400
+ description: t("modules.store.uninstall_confirm.description", {
401
+ module: title,
402
+ }),
403
+ confirmLabel: t("modules.store.actions.uninstall"),
404
+ color: "error",
405
+ icon: "i-ph-trash",
406
+ onConfirm: () => runFromDialog(kind, entry),
407
+ });
408
+ return;
409
+ }
410
+ try {
411
+ await moduleStore.run(kind, entry);
412
+ } catch (error) {
413
+ toast.add({
414
+ color: "error",
415
+ icon: "i-ph-warning-circle",
416
+ title: t("modules.store.action_failed", { module: title }),
417
+ description: resolveStoreError(error),
418
+ });
419
+ }
420
+ }
421
+
422
+ /** What a refused store request carries: the server's error key, if any. */
423
+ interface StoreRequestError {
424
+ data?: unknown;
425
+ message?: string;
426
+ }
427
+
428
+ function resolveStoreError(error: unknown): string {
429
+ const details = error as StoreRequestError | undefined;
430
+ const key = typeof details?.data === "string" ? details.data : undefined;
431
+ if (!key) return details?.message ?? "";
432
+ return t(`modules.store.errors.${key.replace("error.", "")}`);
433
+ }
434
+
242
435
  // ── Actions ────────────────────────────────────────────────────────
243
436
  async function retry(): Promise<void> {
244
437
  if (isRetrying.value) return;
@@ -506,49 +699,141 @@ const summaryCards = computed<StatGroupItem[]>(() =>
506
699
  </div>
507
700
  </section>
508
701
 
509
- <!-- Module store: a preview, nothing installs from here yet. -->
702
+ <!-- Module store: the official modules, installed or not. -->
510
703
  <section class="mt-10" aria-labelledby="module-store-title">
511
704
  <DmsSectionHeader
512
705
  class="mb-3"
513
- :count="isInitiallyLoading ? undefined : storeModules.length"
706
+ :count="isStoreLoading ? undefined : storeModules.length"
514
707
  :description="t('modules.store.description')"
515
708
  >
516
709
  <template #title>
517
710
  <span id="module-store-title">{{ t("modules.store.title") }}</span>
518
711
  </template>
519
- <template #badge>
520
- <DmsStatusPill
521
- tone="info"
522
- :label="t('modules.store.soon')"
712
+ <template #trailing>
713
+ <span
714
+ v-if="storeListing?.frontendVersion"
715
+ :class="[
716
+ MONO_CHIP_CLASS,
717
+ 'text-dimmed border border-(--ui-border) text-[11px] max-sm:hidden',
718
+ ]"
719
+ :title="
720
+ storeListing.frontendVersionSource
721
+ ? t(
722
+ `modules.store.frontend_version.${storeListing.frontendVersionSource}`,
723
+ )
724
+ : undefined
725
+ "
726
+ >
727
+ {{
728
+ t("modules.store.frontend_version.label", {
729
+ version: storeListing.frontendVersion,
730
+ })
731
+ }}
732
+ </span>
733
+ <DmsSegmented
734
+ v-model="storeScope"
735
+ :items="storeScopeItems"
523
736
  size="sm"
524
- uppercase
737
+ :aria-label="t('modules.store.scope.label')"
525
738
  />
526
739
  </template>
527
740
  </DmsSectionHeader>
528
741
 
529
742
  <DmsBanner
743
+ v-if="storeListing?.configError"
744
+ class="mb-4"
745
+ tone="error"
746
+ size="sm"
747
+ icon="i-ph-file-x"
748
+ :title="t('modules.store.config_error')"
749
+ :description="storeListing.configError"
750
+ />
751
+ <DmsBanner
752
+ v-else-if="storeListing && !storeListing.enabled"
530
753
  class="mb-4"
531
754
  tone="info"
532
755
  size="sm"
533
- icon="i-ph-storefront"
534
- :title="t('modules.store.banner.title')"
535
- :description="t('modules.store.banner.description')"
756
+ icon="i-ph-lock-simple"
757
+ :title="t('modules.store.disabled.title')"
758
+ :description="t('modules.store.disabled.description')"
536
759
  />
537
760
 
538
- <!-- Which modules are installed is unknown until the listing loads:
539
- the store list waits for it. -->
540
- <DmsCard
541
- v-if="!isInitiallyLoading && storeModules.length === 0"
542
- :padded="false"
761
+ <!-- The interface lags behind the backend: say what follows. -->
762
+ <DmsBanner
763
+ v-if="frontendNotice"
764
+ class="mb-4"
765
+ :tone="frontendNotice.key === 'dev' ? 'info' : 'warning'"
766
+ size="sm"
767
+ :icon="
768
+ frontendNotice.key === 'dev'
769
+ ? 'i-ph-circle-notch'
770
+ : 'i-ph-arrows-clockwise'
771
+ "
772
+ :title="
773
+ t(`modules.store.frontend.${frontendNotice.key}.title`, {
774
+ modules: frontendNotice.names,
775
+ })
776
+ "
543
777
  >
544
- <DmsEmptyState
545
- icon="i-ph-check-circle"
546
- :title="t('modules.store.all_installed.title')"
547
- :description="t('modules.store.all_installed.description')"
548
- />
549
- </DmsCard>
778
+ <template #description>
779
+ <i18n-t
780
+ :keypath="`modules.store.frontend.${frontendNotice.key}.description`"
781
+ scope="global"
782
+ tag="span"
783
+ >
784
+ <template #command>
785
+ <code
786
+ :class="[
787
+ MONO_CHIP_CLASS,
788
+ 'bg-elevated text-highlighted text-xs',
789
+ ]"
790
+ >
791
+ {{ FRONTEND_BUILD_COMMAND }}
792
+ </code>
793
+ </template>
794
+ </i18n-t>
795
+ </template>
796
+ </DmsBanner>
550
797
 
551
- <template v-else-if="!isInitiallyLoading">
798
+ <div v-if="isStoreLoading" :class="TILE_GRID_CLASS" aria-busy="true">
799
+ <DmsCard
800
+ v-for="placeholder in skeletonItems"
801
+ :key="placeholder"
802
+ :padded="false"
803
+ class="flex min-h-[228px] flex-col gap-3 p-4"
804
+ >
805
+ <USkeleton
806
+ :aria-label="t('dms.a11y.loading')"
807
+ class="size-9 rounded-[10px]"
808
+ />
809
+ <USkeleton
810
+ :aria-label="t('dms.a11y.loading')"
811
+ class="mt-auto h-2.5 w-3/5"
812
+ />
813
+ </DmsCard>
814
+ </div>
815
+
816
+ <!-- Only when nothing is on screen yet: a follow-up read that fails
817
+ while the frontend restarts keeps the tiles and tries again. -->
818
+ <DmsBanner
819
+ v-else-if="moduleStore.loadingError.value && !storeListing"
820
+ tone="error"
821
+ size="sm"
822
+ icon="i-ph-plugs"
823
+ :title="t('modules.store.load_error')"
824
+ >
825
+ <template #actions>
826
+ <UButton
827
+ color="error"
828
+ size="sm"
829
+ icon="i-ph-arrows-clockwise"
830
+ :label="t('modules.error.retry')"
831
+ @click="moduleStore.load()"
832
+ />
833
+ </template>
834
+ </DmsBanner>
835
+
836
+ <template v-else>
552
837
  <div :class="TOOLBAR_CLASS">
553
838
  <DmsSearchInput
554
839
  v-model="storeQuery"
@@ -564,7 +849,18 @@ const summaryCards = computed<StatGroupItem[]>(() =>
564
849
  />
565
850
  </div>
566
851
 
567
- <DmsCard v-if="visibleStoreModules.length === 0" :padded="false">
852
+ <DmsCard
853
+ v-if="scopedStoreModules.length === 0 && storeScope === 'available'"
854
+ :padded="false"
855
+ >
856
+ <DmsEmptyState
857
+ icon="i-ph-check-circle"
858
+ :title="t('modules.store.all_installed.title')"
859
+ :description="t('modules.store.all_installed.description')"
860
+ />
861
+ </DmsCard>
862
+
863
+ <DmsCard v-else-if="visibleStoreModules.length === 0" :padded="false">
568
864
  <DmsEmptyState
569
865
  variant="no-result"
570
866
  :title="
@@ -592,6 +888,11 @@ const summaryCards = computed<StatGroupItem[]>(() =>
592
888
  v-for="entry in visibleStoreModules"
593
889
  :key="entry.id"
594
890
  :entry="entry"
891
+ :is-locked="
892
+ !storeListing?.enabled ||
893
+ (storeListing?.busy === true && entry.state !== 'busy')
894
+ "
895
+ @action="runStoreAction($event, entry)"
595
896
  />
596
897
  </div>
597
898
  </template>
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Mirror of the backend's module store listing (`GET /dms/store/listing`,
3
+ * `src/store/types.ts` in `@antelopejs/dms`).
4
+ */
5
+ export type StoreModuleState =
6
+ | "available"
7
+ | "busy"
8
+ | "active"
9
+ | "disabled"
10
+ | "error"
11
+ | "pending";
12
+
13
+ export type StoreFrontendState = "none" | "synced" | "pending" | "unknown";
14
+
15
+ export type StoreOperationKind =
16
+ | "install"
17
+ | "update"
18
+ | "enable"
19
+ | "disable"
20
+ | "uninstall"
21
+ | "apply";
22
+
23
+ export type StoreOperationStep =
24
+ | "resolve"
25
+ | "dependencies"
26
+ | "unload"
27
+ | "load"
28
+ | "save";
29
+
30
+ export interface StoreOperation {
31
+ moduleId: string;
32
+ kind: StoreOperationKind;
33
+ step: StoreOperationStep;
34
+ targetVersion?: string;
35
+ startedAt: number;
36
+ }
37
+
38
+ /** A module of the AntelopeJS interfaces registry that implements an interface. */
39
+ export interface InterfaceImplementation {
40
+ /** Name the registry gives it, such as `redis`. */
41
+ name: string;
42
+ /** npm package it is installed from. */
43
+ package: string;
44
+ }
45
+
46
+ /** An interface the release to install needs and no running module implements. */
47
+ export interface MissingInterface {
48
+ interfacePackage: string;
49
+ /** Registry modules that implement it, npm packages only. */
50
+ candidates: InterfaceImplementation[];
51
+ }
52
+
53
+ /** A frontend layer that would stop loading on another dms-frontend. */
54
+ export interface FrontendBreak {
55
+ name: string;
56
+ /** The dms-frontend range the layer supports. */
57
+ range: string;
58
+ }
59
+
60
+ /** Why a release cannot load on the running dms-frontend, and what moving it would take. */
61
+ export interface FrontendRequirement {
62
+ /** The dms-frontend range the release supports. */
63
+ range: string;
64
+ /** The dms-frontend version that runs. */
65
+ running: string;
66
+ /** Latest published dms-frontend in the range, when there is one. */
67
+ target?: string;
68
+ /** Command that moves the project to `target`. */
69
+ command?: string;
70
+ /** Layers already loaded that `target` would refuse. */
71
+ breaks: FrontendBreak[];
72
+ }
73
+
74
+ /** Where the store read the running dms-frontend version. */
75
+ export type FrontendVersionSource = "reported" | "project";
76
+
77
+ export interface StoreListingEntry {
78
+ id: string;
79
+ package: string;
80
+ icon: string;
81
+ /** Store category key, under `modules.store.category`. */
82
+ category: string;
83
+ /** `$i18n.key` */
84
+ title: string;
85
+ /** `$i18n.key` */
86
+ description: string;
87
+ state: StoreModuleState;
88
+ installedVersion?: string;
89
+ /** The version range antelope.config.ts declares. */
90
+ declaredRange?: string;
91
+ latestVersion?: string;
92
+ updateAvailable: boolean;
93
+ compatible: boolean;
94
+ requiredInterfaceRange?: string;
95
+ error?: string;
96
+ operation?: StoreOperation;
97
+ frontend: StoreFrontendState;
98
+ /** The dms-frontend range the release to install declares, when it does. */
99
+ frontendRange?: string;
100
+ /** Set when that range excludes the running dms-frontend. */
101
+ frontendRequirement?: FrontendRequirement;
102
+ /** Interfaces the release to install needs and nothing implements. */
103
+ missingInterfaces: MissingInterface[];
104
+ /** Running modules that need an interface only this one implements. */
105
+ dependents: string[];
106
+ }
107
+
108
+ export interface StoreListing {
109
+ enabled: boolean;
110
+ modules: StoreListingEntry[];
111
+ busy: boolean;
112
+ interfaceVersion: string;
113
+ dev: boolean;
114
+ /** Why antelope.config.ts cannot be read or edited, when it cannot. */
115
+ configError?: string;
116
+ /** The dms-frontend version that runs, when the store can tell. */
117
+ frontendVersion?: string;
118
+ frontendVersionSource?: FrontendVersionSource;
119
+ }
120
+
121
+ /** A module the owner picked to implement a missing interface. */
122
+ export interface StoreDependencyChoice {
123
+ interfacePackage: string;
124
+ /** npm package, never a git or local source. */
125
+ package: string;
126
+ /** Config written to its antelope.config.ts entry. */
127
+ config?: Record<string, unknown>;
128
+ }
129
+
130
+ /** Body of `POST /dms/store/<kind>`; only an install carries dependencies. */
131
+ export interface StoreActionBody {
132
+ id: string;
133
+ dependencies?: StoreDependencyChoice[];
134
+ }