@smartsoft001-mobilems/claude-plugins 2.94.0 → 2.96.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartsoft001-mobilems/claude-plugins",
3
- "version": "2.94.0",
3
+ "version": "2.96.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "mobilems",
3
3
  "description": "MobilEMS-specific Claude Code skills (MuzaCMS / MobileMS indexer API)",
4
- "version": "2.94.0"
4
+ "version": "2.96.0"
5
5
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  An article module wraps `ArticlesSharedModule.forFeature` over the npm `Article` entity and filters
4
4
  its API requests with a `template` value. Thread `<PREFIX>` (the `environment` import) and the chosen
5
- `<name>` / `<entity>` / `<template>`. The `Masonry` factory returns the real library in the browser
5
+ `<name>` / `<entity>` / `<template>`. The `Masonry` factory returns the real library in the browser (`window.Masonry`, set by `bootstrapApplication` from `@smartsoft001-mobilems/angular/bootstrap` in `main.ts`)
6
6
  and a no-op mock during SSR. Copy the file structure from an existing article module in the reference
7
7
  MobilEMS app and substitute the parameters.
8
8
 
@@ -32,10 +32,11 @@ MULTIMEDIA_URL=https://muza-serwer-cdn.<museum-domain>/
32
32
  carried over from another project ships that project's domain in this app's SEO. Pass this project's
33
33
  expected host as the third argument to `verify-devops.sh` to have it checked.
34
34
 
35
- These keys map to the `/env.js` runtime-env template that `mobilems-project-init` sets up in
36
- `server.ts` (`API_URL`/`SITE_URL`/`APP_VERSION` are the base set; `MULTIMEDIA_URL` is the MobilEMS
37
- addition). If a project is missing `MULTIMEDIA_URL` handling in `server.ts`, that's a project-init
38
- gap — flag it, don't fix it here.
35
+ These keys map to the `/env.js` runtime-env endpoint served by `createMobilemsServer`
36
+ (`@smartsoft001-mobilems/angular/ssr`) from `server.ts`: `API_URL` / `SITE_URL` / `MULTIMEDIA_URL` /
37
+ `APP_VERSION` / `USE_LOCAL_TRANSLATIONS` are the built-in set; project-specific keys are declared in
38
+ its `env` option. If a project still hand-rolls `server.ts` (no `createMobilemsServer`) or lacks
39
+ `MULTIMEDIA_URL` handling, that's a project-init gap — flag it, don't fix it here.
39
40
 
40
41
  ## `.eslintignore`
41
42
 
@@ -25,7 +25,7 @@ fail_no_foundation() {
25
25
  [ -f apps/web/src/server.ts ] || fail_no_foundation "apps/web/src/server.ts missing (no SSR)"
26
26
  [ -f package.json ] || fail_no_foundation "package.json missing"
27
27
  grep -q "@smartsoft001-mobilems" package.json || fail_no_foundation "@smartsoft001-mobilems/* not in package.json"
28
- grep -q "/api/translations" apps/web/src/server.ts || fail_no_foundation "/api/translations endpoint missing in server.ts"
28
+ grep -qE "/api/translations|@smartsoft001-mobilems/angular/ssr" apps/web/src/server.ts || fail_no_foundation "server.ts is neither on createMobilemsServer (@smartsoft001-mobilems/angular/ssr) nor serving /api/translations"
29
29
  [ -f apps/web/public/translations.json ] || fail_no_foundation "apps/web/public/translations.json missing"
30
30
 
31
31
  echo "MOBILEMS FOUNDATION OK."
@@ -23,7 +23,7 @@ export class MuseumEventsModule {}
23
23
  ## `events.routes.ts`
24
24
 
25
25
  Every page is loaded lazily with `loadComponent`. The list route additionally provides the `Masonry`
26
- SSR factory (the real library in the browser, a no-op mock during SSR). Copy verbatim — there is no
26
+ SSR factory (the real library in the browser — `window.Masonry`, set by `bootstrapApplication` from `@smartsoft001-mobilems/angular/bootstrap` in `main.ts` — a no-op mock during SSR). Copy verbatim — there is no
27
27
  `<PREFIX>` to thread here either.
28
28
 
29
29
  ```ts
@@ -102,11 +102,16 @@ add `tailwind.config.js` + postcss wiring, and overwrite `apps/web/src/styles.sc
102
102
  global stylesheet (font-fallbacks, `.smart-container`, WCAG, masonry, fullscreen-dialog rules). The
103
103
  stylesheet is pure CSS — copying it verbatim is safe even before the components it targets exist.
104
104
 
105
- ### 5. Runtime env — multimediaUrl + translations
105
+ ### 5. Runtime env + entry files on the framework
106
106
 
107
- Extend the base runtime-env infra per `references/server-env.md`: add `multimediaUrl` to
108
- `environment.ts`/`.prod.ts`, the `/env.js` template, and the SSR allowed-hosts list; and add the
109
- `/api/translations` endpoint to `server.ts`.
107
+ Per `references/server-env.md`: add `multimediaUrl` to `environment.ts`/`.prod.ts` and
108
+ `EnvironmentService`, then **overwrite** the three base entry files with the framework-backed ones —
109
+ `main.ts` (`bootstrapApplication` from `@smartsoft001-mobilems/angular/bootstrap`, registers masonry
110
+ on `window`), `main.server.ts` (`bootstrapServerApplication` from
111
+ `@smartsoft001-mobilems/angular/bootstrap/server`; `./env-init` stays the first import) and
112
+ `server.ts` (`createMobilemsServer` from `@smartsoft001-mobilems/angular/ssr` — allowed hosts,
113
+ `/env.js` incl. `multimediaUrl`, `/api/translations`, `/robots.txt`, `/sitemap.xml`). Never
114
+ hand-roll those endpoints or patch the base `server.ts` line by line.
110
115
 
111
116
  ### 6. app.config.ts → MobilEMS providers
112
117
 
@@ -165,8 +170,9 @@ not re-built.
165
170
  Apply `references/qa-hardening.md`. The copied foundation carries the _source project's_ identity,
166
171
  so this step removes leftovers and closes the recurring SEO/i18n/WCAG gaps:
167
172
 
168
- - dynamic `/sitemap.xml` + `/robots.txt` SSR endpoints driven by `SITE_URL` (never static copies —
169
- they shipped another museum's domain twice),
173
+ - `/sitemap.xml` + `/robots.txt` served by `createMobilemsServer` (`sitemap.routes`,
174
+ `robots.disallow` in `server.ts`), driven by `SITE_URL` — never static copies (they shipped
175
+ another museum's domain twice),
170
176
  - og/twitter images + URLs on the target domain, favicon set, skip links rewritten for this app,
171
177
  - meta description + OpenGraph on home in both languages; `og:locale` follows the active language,
172
178
  - default language = Polish, `<html lang>` synced on switch, zero hardcoded UI strings,
@@ -183,8 +189,9 @@ MobilEMS-specific permissions / MCP allow-lists, and seed `tools/specs` + `tools
183
189
  ### 12. Verify
184
190
 
185
191
  Run `scripts/verify-init.sh <workspace-root> <PREFIX>`. It asserts the delta landed (Tailwind,
186
- multimediaUrl, MobilEMS providers, populated shared-lib barrel), the QA-hardening basics (sitemap +
187
- robots endpoints, favicon, non-empty heading font), AND the foundation is self-contained (no
192
+ multimediaUrl, the three framework entry files, MobilEMS providers, populated shared-lib barrel),
193
+ the QA-hardening basics (sitemap routes configured, no static sitemap/robots, favicon, non-empty
194
+ heading font), AND the foundation is self-contained (no
188
195
  `libs/museum-*`, no feature routes, and the build-safety invariant — no file under
189
196
  `libs/shared/angular/src` imports a feature package). Fix and re-run until it passes. Gate
190
197
  `npm install` behind an explicit ask — skip it during self-validation/eval.
@@ -2,7 +2,8 @@
2
2
 
3
3
  Overwrite `apps/web/src/app/app.config.ts` with the providers below (the base shipped a minimal
4
4
  version). Thread `<PREFIX>`. `app.config.server.ts` and
5
- `app.routes.server.ts` stay as the base emitted them.
5
+ `app.routes.server.ts` stay as the base emitted them; `main.ts`, `main.server.ts` and `server.ts`
6
+ are overwritten in step 5 (`references/server-env.md` §3).
6
7
 
7
8
  The imports from `@<PREFIX>/angular` (`LanguageInterceptor`, `PreviewAuthInterceptor`,
8
9
  `SsrTimeoutInterceptor`, `LocalStorageService`, `EnvironmentService`) come from the shared lib
@@ -25,7 +25,9 @@ time — never hardcode.** Anchor on the installed `@smartsoft001/angular` line
25
25
  **Shared-UI / runtime libraries** (used by **generic** shared-lib components, so they are
26
26
  foundation deps — verified against the reference):
27
27
 
28
- - `@thisissoon/angular-masonry` + `masonry-layout` (masonry grids; `MasonryModule.forRoot()` in app.config)
28
+ - `@thisissoon/angular-masonry` + `masonry-layout` (masonry grids; `MasonryModule.forRoot()` in app.config;
29
+ `masonry-layout` is also a peer of `@smartsoft001-mobilems/angular/bootstrap`, which registers it on
30
+ `window` from `main.ts` — keep `allowedCommonJsDependencies: ["masonry-layout"]` in `apps/web/project.json`)
29
31
  - `swiper` (generic `slider` component)
30
32
  - `leaflet` + `@types/leaflet` (generic `map` component + `styles.scss` `leaflet.css` import)
31
33
  - `openseadragon` + `@types/openseadragon` (generic `gallery/gallery-fullscreen` deep-zoom)
@@ -37,7 +39,8 @@ The runtime libraries go in `dependencies`; their `@types/*` packages (`@types/l
37
39
  (`@angular/core` version). `@angular/forms` is used by the shared filter components — it normally
38
40
  comes with the Angular base; ensure it's present (same Angular line) and add it if the base lacks it.
39
41
 
40
- > `express` + `dotenv` are already present from the base `server.ts` — verify, don't re-add.
42
+ > `express`, `dotenv` and `@angular/ssr` are already present from the base `server.ts` — verify, don't
43
+ > re-add. They are peers of `@smartsoft001-mobilems/angular/ssr` (`createMobilemsServer`).
41
44
 
42
45
  ## Feature packages — added by the module skills, not here
43
46
 
@@ -29,20 +29,19 @@ follows are the SEO-side items:
29
29
 
30
30
  ## 2. Dynamic sitemap + robots endpoints (server.ts)
31
31
 
32
- Put the logic in **pure, testable functions** in
33
- `apps/web/src/seo/seo-endpoints.ts` (`resolveSiteUrl`, `buildSitemapXml`, `buildRobotsTxt`), and
34
- wire thin routes in `server.ts`:
35
-
36
- - Register `GET /sitemap.xml` and `GET /robots.txt` **after** `/api/translations` but **before**
37
- `express.static` and the `'/**'` SSR catch-all — otherwise SSR answers with HTML and status 200
38
- and the endpoints "work" only apparently.
39
- - Normalize `SITE_URL`: strip trailing `/`, prepend `https://` when the schema is missing; fall back
40
- to the request host when the env var is unset.
41
- - Headers: `application/xml; charset=utf-8` / `text/plain; charset=utf-8`,
42
- `Cache-Control: public, max-age=3600`.
43
- - Sitemap content: home + the module list pages (`/strona-glowna`, `/obiekty`, `/kolekcje`, …). Do
44
- **not** include CMS static pages (`/strona/:id`) — their ids are CMS-assigned and go stale.
45
- - `robots.txt` points `Sitemap:` at the same `SITE_URL`.
32
+ Both endpoints come from `createMobilemsServer` (`@smartsoft001-mobilems/angular/ssr`, see
33
+ `references/server-env.md` §3c) — never ship static files and never re-implement them in the app
34
+ (no `seo-endpoints.ts`, no hand-written routes). The framework normalizes `SITE_URL` (trailing `/`
35
+ stripped, `https://` prepended, request host as fallback), sets the content-type and
36
+ `Cache-Control: public, max-age=3600` headers, registers the routes before `express.static` and the
37
+ SSR catch-all, and points `robots.txt` `Sitemap:` at the same origin. What the project configures:
38
+
39
+ - `sitemap.routes`: home + the module list pages (`/strona-glowna`, `/obiekty`, `/kolekcje`, …) with
40
+ `changefreq` / `priority`. Do **not** include CMS static pages (`/strona/:id`) — their ids are
41
+ CMS-assigned and go stale.
42
+ - `sitemap.languages` / `sitemap.defaultLanguage` (defaults `['pl', 'en']` / `'pl'`) when the site is
43
+ not Polish-first with an English alternate.
44
+ - `robots.disallow`: preview and search paths.
46
45
 
47
46
  ## 3. SEO meta (home + every routed page)
48
47
 
@@ -1,9 +1,9 @@
1
- # Runtime env delta — multimediaUrl + /api/translations
1
+ # Runtime env delta — multimediaUrl + framework entry files
2
2
 
3
3
  The base shipped `environment.{ts,prod.ts}`, `EnvironmentService`, a `server.ts` with `/env.js`, and
4
4
  the SSRF allowed-hosts list — all keyed on `API_URL` / `SITE_URL` / `APP_VERSION` only. MobilEMS adds
5
- a **`multimediaUrl`** key (media/CDN host) and an **`/api/translations`** dev endpoint. These are the
6
- only env changes; do not otherwise rewrite the base SSR infra.
5
+ a **`multimediaUrl`** key (media/CDN host), and replaces the three entry files (`main.ts`,
6
+ `main.server.ts`, `server.ts`) with thin wrappers over the framework entry points (section 3).
7
7
 
8
8
  ## 1. `libs/shared/angular/src/lib/environments/environment.ts` + `environment.prod.ts`
9
9
 
@@ -26,72 +26,106 @@ In `EnvironmentService.initFromProcessEnv()` and `init()`, propagate `multimedia
26
26
  transfer-state / `window.env` on the client). In reference mode this comes with the copied service —
27
27
  just confirm it.
28
28
 
29
- ## 3. `apps/web/src/server.ts` — two additions
29
+ ## 3. Entry files — replace the base versions with the framework ones
30
30
 
31
- **a) Allowed-hosts:** after the `SITE_URL` block and before the `API_URL` block, add a
32
- `MULTIMEDIA_URL` host so SSR can fetch media during render:
31
+ The base (`scaffold-nx-workspace`) emits a plain `main.ts`, a `main.server.ts` with `./env-init` and a
32
+ hand-written generic `server.ts`. MobilEMS replaces all three with thin files on top of
33
+ `@smartsoft001-mobilems/angular` entry points — do **not** patch the base files line by line, and
34
+ never hand-roll `/env.js`, `/api/translations`, `/robots.txt` or `/sitemap.xml`.
33
35
 
34
- ```ts
35
- const multimediaUrl = process.env['MULTIMEDIA_URL'];
36
- if (multimediaUrl) {
37
- try {
38
- allowedHosts.push(
39
- new URL(
40
- multimediaUrl.startsWith('http')
41
- ? multimediaUrl
42
- : `https://${multimediaUrl}`,
43
- ).hostname,
44
- );
45
- } catch {
46
- // ignore invalid URL
47
- }
48
- }
49
- ```
36
+ ### a) `apps/web/src/main.ts` (overwrite)
50
37
 
51
- **b) `/env.js` template:** add the `multimediaUrl` line + its replacement:
38
+ `bootstrapApplication` from `@smartsoft001-mobilems/angular/bootstrap` registers `masonry-layout` on
39
+ `window.Masonry` (the module skills' `Masonry` DI factories read it) and then bootstraps. Without
40
+ this the masonry grids silently fall back to the SSR no-op mock in the browser.
52
41
 
53
42
  ```ts
54
- window['env']['multimediaUrl'] = '${MULTIMEDIA_URL}';
43
+ // Browser entry. `@smartsoft001-mobilems/angular/bootstrap` registers the real
44
+ // masonry-layout library on `window.Masonry` before bootstrapping; the SSR entry
45
+ // (`main.server.ts`) keeps using Angular's own `bootstrapApplication`.
46
+ import { bootstrapApplication } from '@smartsoft001-mobilems/angular/bootstrap';
47
+
48
+ import { App } from './app/app';
49
+ import { appConfig } from './app/app.config';
50
+
51
+ bootstrapApplication(App, appConfig).catch((err) => console.error(err));
55
52
  ```
56
53
 
54
+ Keep `"allowedCommonJsDependencies": ["masonry-layout"]` in `apps/web/project.json` build options
55
+ (`masonry-layout` is CommonJS). Delete any local `masonry-layout.d.ts` stub — the entry point ships
56
+ its own types.
57
+
58
+ ### b) `apps/web/src/main.server.ts` (overwrite)
59
+
60
+ `bootstrapServerApplication` from `@smartsoft001-mobilems/angular/bootstrap/server` returns the
61
+ `(context) => Promise<ApplicationRef>` factory Angular SSR expects as the default export. It lives in
62
+ a separate entry point on purpose: `masonry-layout` touches `window` at import time and would crash
63
+ Node. `./env-init` **must stay the first import**.
64
+
57
65
  ```ts
58
- .replace(/\$\{MULTIMEDIA_URL\}/g, process.env['MULTIMEDIA_URL'] || '')
66
+ /**
67
+ * CRITICAL: './env-init' MUST be the first import!
68
+ * It initializes environment variables from process.env BEFORE
69
+ * any modules that depend on them are evaluated.
70
+ */
71
+ import './env-init';
72
+
73
+ import { bootstrapServerApplication } from '@smartsoft001-mobilems/angular/bootstrap/server';
74
+
75
+ import { App } from './app/app';
76
+ import { config } from './app/app.config.server';
77
+
78
+ export default bootstrapServerApplication(App, config);
59
79
  ```
60
80
 
61
- **c) `/api/translations` endpoint** (needs `import * as fs from 'fs'` and `import * as path from 'path'`
62
- at the top): serve the translations JSON from dist, falling back to the public source. Register it
63
- **before** the static-file middleware and the catch-all `app.use('/**', …)`:
81
+ ### c) `apps/web/src/server.ts` (overwrite)
82
+
83
+ `createMobilemsServer` from `@smartsoft001-mobilems/angular/ssr` assembles the whole Express server:
84
+ dotenv, the SSRF allowed-hosts list (hostnames of `SITE_URL` — with and without `www.` —
85
+ `MULTIMEDIA_URL`, `API_URL`, plus `localhost` outside production), `GET /env.js` (`apiUrl`,
86
+ `siteUrl`, `multimediaUrl`, `version`, `useLocalTranslations` — values JSON-encoded),
87
+ `GET /api/translations` (source file first, then the browser dist copy), `GET /robots.txt`,
88
+ `GET /sitemap.xml`, your custom routes, static files, the Angular handler and `app.listen` when the
89
+ module is the entry point. Export its `reqHandler` — the Angular CLI dev server needs it.
64
90
 
65
91
  ```ts
66
- app.get('/api/translations', (req, res) => {
67
- let translationPath = path.join(
68
- process.cwd(),
69
- 'dist/apps/web/browser/translations.json',
70
- );
71
- if (!fs.existsSync(translationPath)) {
72
- translationPath = path.join(
73
- process.cwd(),
74
- 'apps/web/public/translations.json',
75
- );
76
- }
77
- if (fs.existsSync(translationPath)) {
78
- const translations = JSON.parse(fs.readFileSync(translationPath, 'utf8'));
79
- res.json(translations);
80
- } else {
81
- console.error('Translation file not found at:', translationPath);
82
- res.status(404).json({ error: 'Translation file not found' });
83
- }
92
+ import { createMobilemsServer } from '@smartsoft001-mobilems/angular/ssr';
93
+
94
+ export const { reqHandler } = createMobilemsServer({
95
+ moduleUrl: import.meta.url,
96
+ sitemap: {
97
+ routes: [
98
+ { path: '/strona-glowna', changefreq: 'daily', priority: 1 },
99
+ { path: '/obiekty', changefreq: 'daily', priority: 0.9 },
100
+ { path: '/kolekcje', changefreq: 'weekly', priority: 0.9 },
101
+ { path: '/aktualnosci', changefreq: 'daily', priority: 0.8 },
102
+ { path: '/kontakt', changefreq: 'monthly', priority: 0.5 },
103
+ ],
104
+ },
105
+ robots: { disallow: ['/article-preview', '/wyszukaj'] },
84
106
  });
85
107
  ```
86
108
 
109
+ Options you will actually touch (full list: the `ssr` section of the framework
110
+ [`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md#ssr-server-ssr)):
111
+
112
+ | Option | Use |
113
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
114
+ | `sitemap.routes` | home + the module **list** pages. Each module skill appends its own list route here. Never CMS static pages (`/strona/:id` — ids go stale). Omit `sitemap` entirely only for an app that must not be indexed. |
115
+ | `sitemap.languages` / `defaultLanguage` | defaults `['pl', 'en']` / `'pl'` (the default language has no `?lang=`). Override for a non-PL-first site. |
116
+ | `robots.disallow` | preview / search paths (`/article-preview` is added by `mobilems-articles`). |
117
+ | `env` | extra `window.env` keys the project needs: `{ gtmId: 'GTM_ID' }` (env var name) or a resolver `() => string`. Mirror each key in the shared lib's `environment.ts` + `EnvironmentService`. |
118
+ | `configure(app)` | project endpoints (`app.get('/api/...')`) — registered before static files and SSR. |
119
+ | `translations.paths` | only when the app is not at `apps/web`. |
120
+
87
121
  The `app.config.ts` `provideAppInitializer` hits `/api/translations` in dev and `apiUrl + 'setting/theme'`
88
- in prod (see `references/config-providers.md`). Optionally seed an empty
89
- `apps/web/public/translations.json` (`{"en":{},"pl":{}}`) so dev SSR doesn't 404.
122
+ in prod (see `references/config-providers.md`). Seed an empty `apps/web/public/translations.json`
123
+ (`{"en":{},"pl":{}}`) so dev SSR doesn't 404.
90
124
 
91
- ## 4. `/sitemap.xml` + `/robots.txt` — dynamic endpoints (required)
125
+ ## 4. `/sitemap.xml` + `/robots.txt` — configured, never hand-rolled
92
126
 
93
127
  Never ship static `sitemap.xml`/`robots.txt` copied from the reference — two projects went to QA
94
- with another museum's domain in them. Build them as SSR endpoints from `SITE_URL`, with the logic in
95
- pure functions (`apps/web/src/seo/seo-endpoints.ts`) and thin routes in `server.ts`, registered
96
- after `/api/translations` and **before** `express.static` + the `'/**'` catch-all. Full pattern
97
- (normalization, headers, content rules): `references/qa-hardening.md` §2.
128
+ with another museum's domain in them — and never re-implement the endpoints in the app: they come
129
+ from `createMobilemsServer` (`sitemap`, `robots` options above), normalized on `SITE_URL` with the
130
+ request host as fallback, correct headers and caching, registered before static files and the SSR
131
+ catch-all. Content rules: `references/qa-hardening.md` §2.
@@ -25,13 +25,18 @@ grep -q "@smartsoft001-mobilems/angular" package.json && pass "mobilems framewor
25
25
  grep -q "@ngrx/store" package.json && pass "ngrx dep" || bad "@ngrx/store in package.json"
26
26
  grep -q "SharedModule.forRoot" apps/web/src/app/app.config.ts && pass "SharedModule.forRoot in app.config" || bad "SharedModule.forRoot in app.config.ts"
27
27
  grep -q "SEARCH_CONFIG_TOKEN" apps/web/src/app/app.config.ts && pass "SEARCH_CONFIG_TOKEN" || bad "SEARCH_CONFIG_TOKEN in app.config.ts"
28
- grep -q "multimediaUrl" apps/web/src/server.ts && pass "multimediaUrl in server.ts" || bad "multimediaUrl in server.ts"
29
- grep -q "/api/translations" apps/web/src/server.ts && pass "/api/translations endpoint" || bad "/api/translations in server.ts"
28
+ grep -q "multimediaUrl" libs/shared/angular/src/lib/environments/environment.ts && pass "multimediaUrl in environment.ts" || bad "multimediaUrl in libs/shared/angular/src/lib/environments/environment.ts"
29
+ # the three entry files are thin wrappers over the framework entry points (references/server-env.md §3)
30
+ grep -q "@smartsoft001-mobilems/angular/bootstrap'" apps/web/src/main.ts && pass "main.ts uses bootstrapApplication from @smartsoft001-mobilems/angular/bootstrap" || bad "main.ts must import bootstrapApplication from @smartsoft001-mobilems/angular/bootstrap (registers masonry on window)"
31
+ grep -q "@smartsoft001-mobilems/angular/bootstrap/server" apps/web/src/main.server.ts && pass "main.server.ts uses bootstrapServerApplication" || bad "main.server.ts must import bootstrapServerApplication from @smartsoft001-mobilems/angular/bootstrap/server"
32
+ grep -q "@smartsoft001-mobilems/angular/ssr" apps/web/src/server.ts && pass "server.ts uses createMobilemsServer" || bad "server.ts must call createMobilemsServer from @smartsoft001-mobilems/angular/ssr (never a hand-rolled Express server)"
33
+ grep -q "translations: false" apps/web/src/server.ts && bad "/api/translations disabled in server.ts (translations: false)" || pass "/api/translations served by createMobilemsServer"
30
34
 
31
35
  echo "== QA hardening (references/qa-hardening.md) =="
32
- # dynamic sitemap/robots endpoints — a static copy hardcodes whatever domain it was written with
33
- grep -q "sitemap.xml" apps/web/src/server.ts && pass "dynamic /sitemap.xml endpoint" || bad "/sitemap.xml endpoint in server.ts (SITE_URL-driven, never a static copy)"
34
- grep -q "robots.txt" apps/web/src/server.ts && pass "dynamic /robots.txt endpoint" || bad "/robots.txt endpoint in server.ts (SITE_URL-driven, never a static copy)"
36
+ # sitemap/robots are served by createMobilemsServer — a static copy hardcodes whatever domain it was written with
37
+ grep -qE "sitemap:[[:space:]]*\{" apps/web/src/server.ts && pass "sitemap routes configured in createMobilemsServer" || bad "sitemap option in createMobilemsServer (SITE_URL-driven /sitemap.xml, never a static copy)"
38
+ grep -q "robots: false" apps/web/src/server.ts && bad "/robots.txt disabled in server.ts (robots: false)" || pass "dynamic /robots.txt served by createMobilemsServer"
39
+ if ls apps/web/public/sitemap.xml apps/web/public/robots.txt >/dev/null 2>&1; then bad "static sitemap.xml/robots.txt in apps/web/public — delete them, the endpoints are dynamic"; else pass "no static sitemap.xml/robots.txt"; fi
35
40
  if ls apps/web/public/favicon.ico apps/web/src/favicon.ico >/dev/null 2>&1; then pass "favicon present"; else bad "favicon.ico missing"; fi
36
41
  # an empty fontFamily array silently falls back to system sans
37
42
  if grep -qE ":[[:space:]]*\[[[:space:]]*\]" tailwind.config.js 2>/dev/null; then bad "empty fontFamily array in tailwind.config.js (heading font falls back to system sans)"; else pass "no empty fontFamily entries"; fi
@@ -41,13 +41,10 @@ representations of the same object.
41
41
 
42
42
  ## Steps
43
43
 
44
- 1. **Read the mockup — once** in `docs/mockups/` and write
45
- `.ai/tasks/<issue>/mockup-notes.md` (layout, spacing, colours as token names,
46
- typography, field order, states) before styling; work from the note and do
47
- not re-read the image in this step. The searchbar and the result list item
48
- are usually exported as separate design-system components rather than a full
49
- screen, so match the component and keep the page layout consistent with the
50
- rest of the portal.
44
+ 1. **Read the mockup** in `docs/mockups/` — the searchbar and the result list
45
+ item are usually exported as separate design-system components rather than a
46
+ full screen, so match the component and keep the page layout consistent with
47
+ the rest of the portal.
51
48
  2. **Tabs come from modules.** Each feature module registers a child route under
52
49
  `wyszukaj/:text` (its search module) and the page builds its tab list from
53
50
  what is registered. When a tab is missing, fix it in that module's skill
@@ -39,13 +39,9 @@ exists. Re-implementing it is a defect, not a shortcut:
39
39
 
40
40
  ## Steps
41
41
 
42
- 1. **Read the mockup — once.** Exports live in `docs/mockups/`; read them (Read)
43
- rather than guessing from the Figma link, then immediately write
44
- `.ai/tasks/<issue>/mockup-notes.md` (layout order, grid and spacing, colours
45
- as token names, typography, field order, states, header and footer) and work
46
- from the note. Note the container width, heading scale, spacing rhythm and
47
- how the page header/hero differs from the module pages. Do not re-read the
48
- image later in this step; `/flow:impl` reads it once more in its comparison.
42
+ 1. **Read the mockup.** Exports live in `docs/mockups/`; read them (Read) rather
43
+ than guessing from the Figma link. Note the container width, heading scale,
44
+ spacing rhythm and how the page header/hero differs from the module pages.
49
45
  2. **CMS data — render everything the API sends.** The page payload carries more
50
46
  than a title and body: `image`, `backgroundImage`, `links`, `slides`.
51
47
  A field that arrives and is not rendered reads to the client as a missing
@@ -5,7 +5,7 @@
5
5
  Thread `<PREFIX>` (the `environment` + domain imports). This module REUSES `ArticlesSharedModule`
6
6
  (the same shared module the news/articles modules use) but configures it over its OWN
7
7
  `MuseumThematicPath` entity with its OWN `getReducer` and **no `baseQuery`**. The `Masonry` factory
8
- returns the real library in the browser and a no-op mock during SSR (identical to objects).
8
+ returns the real library in the browser (`window.Masonry`, set by `bootstrapApplication` from `@smartsoft001-mobilems/angular/bootstrap` in `main.ts`) and a no-op mock during SSR (identical to objects).
9
9
 
10
10
  ```ts
11
11
  import { NgModule } from '@angular/core';