@cat-factory/app 0.131.0 → 0.133.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.
@@ -0,0 +1,93 @@
1
+ import type { AnyModuleDescriptor } from '@modular-vue/core'
2
+ import { createRegistry } from '@modular-vue/runtime'
3
+ import type { ModuleRegistry } from '@modular-vue/runtime'
4
+ import { navigationModule } from '~/modular/nav-contributions'
5
+ import type { AppSlots, NavGates } from '~/modular/nav-contributions'
6
+
7
+ /**
8
+ * modular-vue registry for the `@cat-factory/app` layer (slice 0 of the
9
+ * modular-vue adoption — docs/initiatives/modular-vue-adoption.md).
10
+ *
11
+ * This is the frontend analogue of the backend's public registries
12
+ * (`registerAgentKind`, `registerGate`): a single registry into which the layer
13
+ * registers its own first-party feature modules AND a consumer deployment
14
+ * contributes its own, all through the same seam. The registry is resolved and
15
+ * installed by `app/plugins/modular.client.ts`.
16
+ *
17
+ * Slice 1 registers the first real feature module: `cat-factory:navigation`
18
+ * contributes the whole nav/command catalog to the `nav` slot, gated reactively
19
+ * by a `gates` service + `navSlotFilter` and rendered by `SideBar`, `CommandBar`,
20
+ * and `BoardToolbar` via `useReactiveSlots`. Later slices add result views,
21
+ * wizards, and inspector panels as further modules registered here.
22
+ */
23
+
24
+ /**
25
+ * The layer's shared-dependency shape (grows as later slices wire more deps).
26
+ * A `type` (not `interface`) so it satisfies the registry's
27
+ * `Record<string, any>` dependency constraint — an interface lacks the implicit
28
+ * index signature a type-literal has.
29
+ */
30
+ export type AppDeps = {
31
+ /** Reactive RBAC/availability gates the nav `slotFilter` reads. */
32
+ gates: NavGates
33
+ }
34
+
35
+ /**
36
+ * First-party modules the layer always registers. Real feature modules land
37
+ * here as each area is converted; slice 1 adds the navigation catalog.
38
+ */
39
+ const FIRST_PARTY_MODULES: readonly AnyModuleDescriptor[] = [navigationModule]
40
+
41
+ /**
42
+ * Consumer-contributed modules, collected before the layer resolves its
43
+ * registry. A deployment extending `@cat-factory/app` calls
44
+ * {@link registerAppModule} from its own Nuxt plugin (which runs before the
45
+ * layer's install plugin — see the ordering note in
46
+ * `app/plugins/modular.client.ts`). Module descriptors carry Vue components, so
47
+ * they can't travel through serializable Nuxt config; this in-process seam is
48
+ * how the layer stays unforked while a consumer contributes real components.
49
+ */
50
+ const consumerModules: AnyModuleDescriptor[] = []
51
+
52
+ /**
53
+ * Contribute a module to the app registry from a consumer deployment (or a
54
+ * first-party plugin). Call this at plugin-setup time, before the layer's
55
+ * install plugin resolves the registry. Registering the same id twice makes
56
+ * `resolve()` throw at build time (duplicate-id validation), which is the
57
+ * intended guard.
58
+ */
59
+ export function registerAppModule(module: AnyModuleDescriptor): void {
60
+ consumerModules.push(module)
61
+ }
62
+
63
+ /**
64
+ * Test-only: drop every consumer-registered module so a spec can exercise
65
+ * {@link registerAppModule} without leaking state into the next test. Not part
66
+ * of the runtime seam.
67
+ */
68
+ export function __resetConsumerModulesForTest(): void {
69
+ consumerModules.length = 0
70
+ }
71
+
72
+ /**
73
+ * Build a fresh registry with the first-party modules plus everything a consumer
74
+ * contributed via {@link registerAppModule}. A new registry per call because
75
+ * `resolve()` / `resolveManifest()` are single-commit; the install plugin calls
76
+ * this exactly once at client startup (`ssr: false`, so a singleton app).
77
+ *
78
+ * `deps.gates` is the reactive gate service the nav `slotFilter` reads; it's
79
+ * built in the install plugin (Vue context) and registered as a `service` so
80
+ * `useReactiveSlots` tracks it. The `nav` slot default is seeded empty so the
81
+ * key always exists even before any module (or with only consumer modules)
82
+ * contributes.
83
+ */
84
+ export function createAppRegistry(deps: AppDeps): ModuleRegistry<AppDeps, AppSlots> {
85
+ const registry = createRegistry<AppDeps, AppSlots>({
86
+ services: { gates: deps.gates },
87
+ slots: { nav: [] },
88
+ })
89
+ for (const mod of [...FIRST_PARTY_MODULES, ...consumerModules]) {
90
+ registry.register(mod)
91
+ }
92
+ return registry
93
+ }
@@ -0,0 +1,40 @@
1
+ import { installModularApp } from '@modular-vue/nuxt/runtime'
2
+ import { createAppRegistry } from '~/modular/registry'
3
+ import { navSlotFilter } from '~/modular/nav-contributions'
4
+ import { createNavGates } from '~/modular/nav-gates'
5
+
6
+ /**
7
+ * Wire the modular-vue registry into the Nuxt app (slice 0 of the modular-vue
8
+ * adoption — docs/initiatives/modular-vue-adoption.md).
9
+ *
10
+ * `enforce: 'post'` is load-bearing for the consumer-contribution seam. Nuxt
11
+ * loads layer plugins before the consuming app's plugins within the same enforce
12
+ * bucket, so a consumer that calls `registerAppModule(...)` from a normal plugin
13
+ * would otherwise run AFTER this one and miss the resolve. Putting this plugin in
14
+ * the `post` bucket flips the order: the consumer's default-bucket registration
15
+ * runs first, then this resolves the registry with everything registered. A
16
+ * consumer therefore contributes from a default (or `pre`) plugin — documented
17
+ * in the framework-mode-nuxt guide upstream.
18
+ *
19
+ * `ssr: false`, so this runs once on the client and a singleton registry is
20
+ * fine. `installModularApp` is the router-owning path: it grafts each module's
21
+ * routes onto Nuxt's router (none yet — the nav modules are non-routed) and
22
+ * installs the modular contexts (shared deps, navigation, slots) app-wide.
23
+ *
24
+ * Slice 1 wires the reactive nav gating here: `createNavGates()` builds the
25
+ * reactive `gates` service (Pinia + composables are available in a `post`
26
+ * plugin), which the registry registers as a `service` and `navSlotFilter` reads
27
+ * per item. The shells consume the gated `nav` slot through `useReactiveSlots`,
28
+ * so a permission/connection flip re-gates them with no `recalculateSlots()`.
29
+ */
30
+ export default defineNuxtPlugin({
31
+ name: 'cat-factory:modular',
32
+ enforce: 'post',
33
+ setup(nuxtApp) {
34
+ const registry = createAppRegistry({ gates: createNavGates() })
35
+ const manifest = installModularApp({ vueApp: nuxtApp.vueApp, $router: useRouter() }, registry, {
36
+ slotFilter: navSlotFilter,
37
+ })
38
+ return { provide: { modular: manifest } }
39
+ },
40
+ })
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Auto-imported seam for the modular-vue registry. Re-exported from `app/utils/`
3
+ * (which Nuxt auto-imports across the whole `extends` chain) so a consumer
4
+ * deployment can contribute modules with a bare `registerAppModule(...)` call
5
+ * from its own plugin — no deep import into the layer's internals, the same
6
+ * ergonomics as the layer's auto-imported stores and composables.
7
+ *
8
+ * See `app/modular/registry.ts` for the seam itself and
9
+ * docs/initiatives/modular-vue-adoption.md for the adoption plan.
10
+ */
11
+ export { registerAppModule } from '~/modular/registry'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.131.0",
3
+ "version": "0.133.0",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,6 +18,11 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
+ "@modular-frontend/core": "^0.1.0",
22
+ "@modular-vue/core": "^1.0.1",
23
+ "@modular-vue/nuxt": "^0.1.1",
24
+ "@modular-vue/runtime": "^1.1.0",
25
+ "@modular-vue/vue": "^1.1.0",
21
26
  "@nuxt/ui": "^4.9.0",
22
27
  "@nuxtjs/i18n": "^10.4.1",
23
28
  "@pinia/nuxt": "^0.11.3",
@@ -32,7 +37,7 @@
32
37
  "pinia": "^3.0.4",
33
38
  "pinia-plugin-persistedstate": "^4.7.1",
34
39
  "valibot": "^1.4.2",
35
- "vue": "3.5.39",
40
+ "vue": "3.5.40",
36
41
  "wretch": "^3.0.9",
37
42
  "@cat-factory/contracts": "0.147.1"
38
43
  },