@cat-factory/app 0.131.0 → 0.132.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,31 @@
1
+ import { defineModule } from '@modular-vue/core'
2
+ import { afterEach, describe, expect, it } from 'vitest'
3
+ import { __resetConsumerModulesForTest, createAppRegistry, registerAppModule } from './registry'
4
+
5
+ describe('app modular registry', () => {
6
+ afterEach(() => {
7
+ __resetConsumerModulesForTest()
8
+ })
9
+
10
+ it('registers the first-party core module', () => {
11
+ const manifest = createAppRegistry().resolveManifest()
12
+ expect(manifest.modules.map((m) => m.id)).toContain('cat-factory:core')
13
+ })
14
+
15
+ it('includes consumer modules contributed via registerAppModule', () => {
16
+ registerAppModule(defineModule({ id: 'consumer:example', version: '1.0.0' }))
17
+
18
+ const ids = createAppRegistry()
19
+ .resolveManifest()
20
+ .modules.map((m) => m.id)
21
+
22
+ expect(ids).toContain('cat-factory:core')
23
+ expect(ids).toContain('consumer:example')
24
+ })
25
+
26
+ it('rejects a module whose id collides with an already-registered one', () => {
27
+ registerAppModule(defineModule({ id: 'cat-factory:core', version: '2.0.0' }))
28
+
29
+ expect(() => createAppRegistry().resolveManifest()).toThrow(/duplicate/i)
30
+ })
31
+ })
@@ -0,0 +1,75 @@
1
+ import { defineModule } from '@modular-vue/core'
2
+ import type { AnyModuleDescriptor } from '@modular-vue/core'
3
+ import { createRegistry } from '@modular-vue/runtime'
4
+ import type { ModuleRegistry } from '@modular-vue/runtime'
5
+
6
+ /**
7
+ * modular-vue registry for the `@cat-factory/app` layer (slice 0 of the
8
+ * modular-vue adoption — docs/initiatives/modular-vue-adoption.md).
9
+ *
10
+ * This is the frontend analogue of the backend's public registries
11
+ * (`registerAgentKind`, `registerGate`): a single registry into which the layer
12
+ * registers its own first-party feature modules AND a consumer deployment
13
+ * contributes its own, all through the same seam. The registry is resolved and
14
+ * installed by `app/plugins/modular.client.ts`.
15
+ *
16
+ * Slice 0 wires the plumbing behind ZERO behaviour change: the one first-party
17
+ * module carries no navigation / slots / component, so nothing new renders.
18
+ * Later slices convert real areas (navigation, result views, wizards, inspector
19
+ * panels) into modules registered here.
20
+ */
21
+
22
+ /**
23
+ * First-party modules the layer always registers. Kept tiny on purpose for
24
+ * slice 0 — a single descriptor with no contributions, present only to prove the
25
+ * registration pipeline end to end and give the seam a stable anchor. Real
26
+ * feature modules land here as later slices convert each area.
27
+ */
28
+ const FIRST_PARTY_MODULES: readonly AnyModuleDescriptor[] = [
29
+ defineModule({ id: 'cat-factory:core', version: '1.0.0' }),
30
+ ]
31
+
32
+ /**
33
+ * Consumer-contributed modules, collected before the layer resolves its
34
+ * registry. A deployment extending `@cat-factory/app` calls
35
+ * {@link registerAppModule} from its own Nuxt plugin (which runs before the
36
+ * layer's install plugin — see the ordering note in
37
+ * `app/plugins/modular.client.ts`). Module descriptors carry Vue components, so
38
+ * they can't travel through serializable Nuxt config; this in-process seam is
39
+ * how the layer stays unforked while a consumer contributes real components.
40
+ */
41
+ const consumerModules: AnyModuleDescriptor[] = []
42
+
43
+ /**
44
+ * Contribute a module to the app registry from a consumer deployment (or a
45
+ * first-party plugin). Call this at plugin-setup time, before the layer's
46
+ * install plugin resolves the registry. Registering the same id twice makes
47
+ * `resolve()` throw at build time (duplicate-id validation), which is the
48
+ * intended guard.
49
+ */
50
+ export function registerAppModule(module: AnyModuleDescriptor): void {
51
+ consumerModules.push(module)
52
+ }
53
+
54
+ /**
55
+ * Test-only: drop every consumer-registered module so a spec can exercise
56
+ * {@link registerAppModule} without leaking state into the next test. Not part
57
+ * of the runtime seam.
58
+ */
59
+ export function __resetConsumerModulesForTest(): void {
60
+ consumerModules.length = 0
61
+ }
62
+
63
+ /**
64
+ * Build a fresh registry with the first-party modules plus everything a consumer
65
+ * contributed via {@link registerAppModule}. A new registry per call because
66
+ * `resolve()` / `resolveManifest()` are single-commit; the install plugin calls
67
+ * this exactly once at client startup (`ssr: false`, so a singleton app).
68
+ */
69
+ export function createAppRegistry(): ModuleRegistry<Record<string, unknown>> {
70
+ const registry = createRegistry<Record<string, unknown>>({})
71
+ for (const mod of [...FIRST_PARTY_MODULES, ...consumerModules]) {
72
+ registry.register(mod)
73
+ }
74
+ return registry
75
+ }
@@ -0,0 +1,32 @@
1
+ import { installModularApp } from '@modular-vue/nuxt/runtime'
2
+ import { createAppRegistry } from '~/modular/registry'
3
+
4
+ /**
5
+ * Wire the modular-vue registry into the Nuxt app (slice 0 of the modular-vue
6
+ * adoption — docs/initiatives/modular-vue-adoption.md).
7
+ *
8
+ * `enforce: 'post'` is load-bearing for the consumer-contribution seam. Nuxt
9
+ * loads layer plugins before the consuming app's plugins within the same enforce
10
+ * bucket, so a consumer that calls `registerAppModule(...)` from a normal plugin
11
+ * would otherwise run AFTER this one and miss the resolve. Putting this plugin in
12
+ * the `post` bucket flips the order: the consumer's default-bucket registration
13
+ * runs first, then this resolves the registry with everything registered. A
14
+ * consumer therefore contributes from a default (or `pre`) plugin — documented
15
+ * in the framework-mode-nuxt guide upstream.
16
+ *
17
+ * `ssr: false`, so this runs once on the client and a singleton registry is
18
+ * fine. `installModularApp` is the router-owning path: it grafts each module's
19
+ * routes onto Nuxt's router (none yet — slice 0 modules are non-routed) and
20
+ * installs the modular contexts (shared deps, navigation, slots) app-wide. The
21
+ * resolved manifest is exposed as `$modular` for later slices; nothing reads it
22
+ * yet, which is what keeps this behaviour-neutral.
23
+ */
24
+ export default defineNuxtPlugin({
25
+ name: 'cat-factory:modular',
26
+ enforce: 'post',
27
+ setup(nuxtApp) {
28
+ const registry = createAppRegistry()
29
+ const manifest = installModularApp({ vueApp: nuxtApp.vueApp, $router: useRouter() }, registry)
30
+ return { provide: { modular: manifest } }
31
+ },
32
+ })
@@ -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.132.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.0.1",
25
+ "@modular-vue/vue": "^1.0.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
  },