@starci/hfs 4.2.12 → 4.2.14

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 (203) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +7 -3
  3. package/emit/db-types.mjs +2 -1
  4. package/package.json +1 -1
  5. package/runtime/knowledge/hfs/canon-pins.yaml +207 -15
  6. package/runtime/knowledge/hfs/fe-stack.yaml +218 -0
  7. package/runtime/knowledge/hfs/rules.yaml +51 -48
  8. package/runtime/knowledge/hfs/slots.yaml +319 -179
  9. package/runtime/knowledge/patterns/be/event-bus.yaml +15 -0
  10. package/runtime/knowledge/patterns/be/jobs.yaml +10 -0
  11. package/runtime/knowledge/patterns/be/projections.yaml +10 -0
  12. package/runtime/knowledge/patterns/be/queues.yaml +15 -0
  13. package/runtime/knowledge/patterns/be/reactors.yaml +8 -0
  14. package/runtime/knowledge/patterns/be/realtime.yaml +17 -0
  15. package/runtime/knowledge/patterns/be/saga.yaml +13 -0
  16. package/runtime/knowledge/patterns/fe/folder.yaml +24 -18
  17. package/runtime/knowledge/sonar-gate.yaml +10 -2
  18. package/runtime/modules/kernel/failure-codes.yaml +40 -60
  19. package/runtime/modules/kernel/removed-vocabulary.yaml +48 -0
  20. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +0 -2
  21. package/runtime/scripts/hfs/architecture/config.mjs +3 -2
  22. package/runtime/scripts/hfs/architecture/dead-exports.mjs +6 -2
  23. package/runtime/scripts/hfs/architecture/error-masked.mjs +10 -1
  24. package/runtime/scripts/hfs/architecture/frontend.mjs +10 -3
  25. package/runtime/scripts/hfs/architecture/hfs.mjs +6 -3
  26. package/runtime/scripts/hfs/architecture/index.mjs +2 -0
  27. package/runtime/scripts/hfs/architecture/owners.mjs +19 -16
  28. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +4 -1
  29. package/runtime/scripts/hfs/architecture/server-action.mjs +75 -20
  30. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +2 -2
  31. package/runtime/scripts/hfs/architecture/supabase.mjs +36 -210
  32. package/runtime/scripts/hfs/architecture/tiers.mjs +1 -33
  33. package/runtime/scripts/hfs/architecture/transport-owner.mjs +110 -54
  34. package/runtime/scripts/hfs/architecture/type-context.mjs +4 -1
  35. package/runtime/scripts/hfs/check.mjs +10 -6
  36. package/runtime/scripts/hfs/edition-slots.mjs +13 -4
  37. package/runtime/scripts/hfs/manifest-shape.mjs +3 -3
  38. package/runtime/scripts/hfs/rule-catalog.mjs +21 -1
  39. package/runtime/scripts/hfs/rules/database-constants.mjs +4 -0
  40. package/runtime/scripts/hfs/rules/database-migrations.mjs +12 -7
  41. package/runtime/scripts/hfs/rules/database.mjs +4 -3
  42. package/runtime/scripts/hfs/rules/edition.mjs +49 -37
  43. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +4 -5
  44. package/runtime/scripts/hfs/rules/fe-stack.mjs +71 -0
  45. package/scaffold/add-app.mjs +24 -18
  46. package/scaffold/add-cli-lite.mjs +33 -3
  47. package/scaffold/add-inbox-lite.mjs +24 -8
  48. package/scaffold/add-table.mjs +222 -74
  49. package/scaffold/add.mjs +85 -35
  50. package/scaffold/app.mjs +146 -38
  51. package/scaffold/edition-gate.mjs +21 -2
  52. package/scaffold/lite-exports.mjs +30 -1
  53. package/scaffold/service.mjs +5 -8
  54. package/src/main.mjs +2 -2
  55. package/sync/index.mjs +19 -15
  56. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +14 -0
  57. package/templates/app/codegen-graphql.mjs.partial +34 -0
  58. package/templates/app/codegen-lite/scripts/codegen.mjs +18 -13
  59. package/templates/app/package-scripts/core.json +1 -0
  60. package/templates/app/package-scripts-lite/package.json +4 -0
  61. package/templates/app/skeleton/README.md +1 -1
  62. package/templates/app/skeleton/scripts/codegen.mjs +4 -1
  63. package/templates/app/skeleton-lite/README.md +7 -4
  64. package/templates/app/skeleton-lite/supabase/config.toml +1 -1
  65. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +13 -14
  66. package/templates/be/inbox/database-client.spec.ts.tpl +47 -0
  67. package/templates/be/inbox/service.spec.ts.tpl +46 -0
  68. package/templates/be/inbox/webhook-door.spec.ts.tpl +76 -0
  69. package/templates/be/patterns/api/graphql.module.ts.tpl +2 -2
  70. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +6 -3
  71. package/templates/be/platform/errors-graphql/graphql-error.mapper.spec.ts +52 -0
  72. package/templates/be/platform/errors-graphql/graphql-error.mapper.ts +31 -0
  73. package/templates/be/platform/graphql/graphql-depth.policy.spec.ts +43 -0
  74. package/templates/be/platform/graphql/graphql-depth.policy.ts +21 -0
  75. package/templates/be/platform/graphql/graphql.contracts.ts +12 -0
  76. package/templates/be/platform/graphql/graphql.module-definition.ts +7 -0
  77. package/templates/be/platform/graphql/graphql.module.ts +49 -0
  78. package/templates/be/platform/graphql/graphql.options.ts +2 -0
  79. package/templates/be/platform/graphql/index.ts +1 -0
  80. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +114 -0
  81. package/templates/be/platform/graphql/localize-errors.mapper.ts +51 -0
  82. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +11 -0
  83. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.ts +8 -3
  84. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +13 -1
  85. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.ts +11 -2
  86. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +21 -0
  87. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +94 -0
  88. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +2 -4
  89. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +10 -0
  90. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +2 -2
  91. package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +2 -6
  92. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +15 -0
  93. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
  94. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +1 -1
  95. package/templates/be/table/api/create.command.ts.tpl +10 -0
  96. package/templates/be/table/api/create.contracts.ts.tpl +8 -0
  97. package/templates/be/table/api/create.handler.ts.tpl +22 -0
  98. package/templates/be/table/api/create.mapper.ts.tpl +5 -0
  99. package/templates/be/table/api/create.resolver.ts.tpl +21 -0
  100. package/templates/be/table/api/create.type.ts.tpl +9 -0
  101. package/templates/be/table/api/feature.index.ts.tpl +1 -0
  102. package/templates/be/table/api/feature.module.ts.tpl +7 -0
  103. package/templates/be/table/api/get.contracts.ts.tpl +11 -0
  104. package/templates/be/table/api/get.handler.ts.tpl +22 -0
  105. package/templates/be/table/api/get.input.ts.tpl +12 -0
  106. package/templates/be/table/api/get.mapper.ts.tpl +9 -0
  107. package/templates/be/table/api/get.query.ts.tpl +10 -0
  108. package/templates/be/table/api/get.resolver.ts.tpl +27 -0
  109. package/templates/be/table/api/get.type.ts.tpl +9 -0
  110. package/templates/be/table/api/graphql.module.ts.tpl +8 -0
  111. package/templates/be/table/error.api.ts.tpl +19 -0
  112. package/templates/be/table/migration.sql.tpl +13 -21
  113. package/templates/be/table/service.api.ts.tpl +30 -0
  114. package/templates/be/table/sql.api.ts.tpl +7 -0
  115. package/templates/fe/{skeleton-lite/apps/web → skeleton/apps/__app__}/src/components/blocks/SignInForm/index.tsx +16 -18
  116. package/templates/fe/{skeleton-lite/apps/web → skeleton/apps/__app__}/src/components/blocks/SignOutButton/component.tsx +7 -3
  117. package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignOutButton/index.tsx +23 -0
  118. package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +49 -0
  119. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +4 -0
  120. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -0
  121. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignIn.ts +29 -0
  122. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +27 -0
  123. package/templates/fe/skeleton/apps/__app__/src/hooks/effects/index.ts +1 -0
  124. package/templates/fe/skeleton/apps/__app__/src/hooks/effects/useAuthSessionEffect.ts +14 -0
  125. package/templates/fe/skeleton/apps/__app__/src/hooks/swr/SwrSideEffects.tsx +8 -0
  126. package/templates/fe/skeleton/apps/__app__/src/hooks/swr/index.ts +1 -0
  127. package/templates/fe/skeleton/apps/__app__/src/modules/api/graphql/index.ts +1 -0
  128. package/templates/fe/skeleton/apps/__app__/src/modules/api/graphql/types.ts +26 -0
  129. package/templates/fe/skeleton/apps/__app__/src/modules/api/index.ts +1 -0
  130. package/templates/fe/skeleton/apps/__app__/src/modules/auth/client.ts +21 -0
  131. package/templates/fe/skeleton/apps/__app__/src/modules/auth/contracts.ts +27 -0
  132. package/templates/fe/skeleton/apps/__app__/src/modules/auth/index.ts +2 -0
  133. package/templates/fe/skeleton/apps/__app__/src/modules/toast/api.ts +52 -0
  134. package/templates/fe/skeleton/apps/__app__/src/modules/toast/hooks.ts +33 -0
  135. package/templates/fe/skeleton/apps/__app__/src/modules/toast/index.ts +1 -0
  136. package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +27 -0
  137. package/templates/fe/skeleton/apps/__app__/src/modules/types/ambient/styles.d.ts +2 -0
  138. package/templates/fe/skeleton/apps/__app__/src/redux/ReduxProvider.tsx +11 -0
  139. package/templates/fe/skeleton/apps/__app__/src/redux/hooks.ts +8 -0
  140. package/templates/fe/skeleton/apps/__app__/src/redux/index.ts +3 -0
  141. package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +26 -0
  142. package/templates/fe/skeleton/apps/__app__/src/redux/store.ts +14 -0
  143. package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +5 -2
  144. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -2
  145. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +1 -1
  146. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +23 -0
  147. package/templates/fe/skeleton/apps/landing/src/modules/types/ambient/styles.d.ts +2 -0
  148. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +51 -0
  149. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +10 -13
  150. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +8 -5
  151. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +2 -17
  152. package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +3 -10
  153. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +0 -6
  154. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +11 -8
  155. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +0 -3
  156. package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +9 -25
  157. package/templates/fe/table/graphql/mutation.ts.tpl +25 -0
  158. package/templates/fe/table/graphql/mutation.types.ts.tpl +7 -0
  159. package/templates/fe/table/graphql/query.ts.tpl +25 -0
  160. package/templates/fe/table/graphql/query.types.ts.tpl +7 -0
  161. package/templates/fe/table/graphql/useMutateSwr.ts.tpl +21 -0
  162. package/templates/fe/table/graphql/useQuerySwr.ts.tpl +17 -0
  163. package/templates/fe/transport/graphql/clients/clients/auth.ts +39 -0
  164. package/templates/fe/transport/graphql/clients/clients/index.ts +2 -0
  165. package/templates/fe/transport/graphql/clients/clients/no-auth.ts +33 -0
  166. package/templates/fe/transport/graphql/clients/index.ts +2 -0
  167. package/templates/fe/transport/graphql/clients/links/attach-access-token.ts +9 -0
  168. package/templates/fe/transport/graphql/clients/links/error.ts +30 -0
  169. package/templates/fe/transport/graphql/clients/links/http.ts +16 -0
  170. package/templates/fe/transport/graphql/clients/links/index.ts +8 -0
  171. package/templates/fe/transport/graphql/clients/links/locale.ts +13 -0
  172. package/templates/fe/transport/graphql/clients/links/options.ts +17 -0
  173. package/templates/fe/transport/graphql/clients/links/refresh-token.ts +36 -0
  174. package/templates/fe/transport/graphql/clients/links/retry.ts +16 -0
  175. package/templates/fe/transport/graphql/clients/links/timeout.ts +28 -0
  176. package/templates/fe/transport/graphql/clients/response.ts +43 -0
  177. package/upgrade/check.mjs +1 -1
  178. package/upgrade/index.mjs +3 -2
  179. package/runtime/scripts/hfs/architecture/supabase-results.mjs +0 -182
  180. package/templates/app/quality-config-lite/sonar-project.properties +0 -5
  181. package/templates/be/patterns/api/feature.index.http.ts.tpl +0 -1
  182. package/templates/be/patterns/api/http.controller.ts.tpl +0 -27
  183. package/templates/be/patterns/api/http.mapper.ts.tpl +0 -9
  184. package/templates/be/patterns/api/http.module.ts.tpl +0 -7
  185. package/templates/be/patterns/api/http.request.ts.tpl +0 -10
  186. package/templates/be/patterns/api/http.response.ts.tpl +0 -5
  187. package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +0 -24
  188. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +0 -38
  189. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +0 -25
  190. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +0 -25
  191. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +0 -17
  192. package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +0 -14
  193. package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +0 -3
  194. package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +0 -59
  195. package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +0 -29
  196. package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +0 -70
  197. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +0 -58
  198. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +0 -26
  199. package/templates/fe/table/read.ts.tpl +0 -16
  200. package/templates/fe/table/write.ts.tpl +0 -16
  201. /package/templates/be/patterns/api/{application.command.http.ts.tpl → application.command.principal.ts.tpl} +0 -0
  202. /package/templates/be/patterns/api/{application.handler.http.ts.tpl → application.handler.principal.ts.tpl} +0 -0
  203. /package/templates/fe/{skeleton-lite/apps/web → skeleton/apps/__app__}/src/components/blocks/SignInForm/component.tsx +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.2.14 - 2026-10-13
4
+
5
+ - Breaking: HFS manifest 3.0.0 (schema `starci/hfs-slots@3`, rule catalog `starci/hfs-rules@3` 3.0.0). Removing the front-end database slots and the Supabase realtime feature slot and narrowing `fe.route` change answers for existing apps, so the manifest takes a major: every app pins `"hfs": 3` (scaffold writes it), and an app still pinned to 2 is refused with `HFS_MANIFEST_MAJOR_MISMATCH`.
6
+ - Changed: HFS manifest 3.0.0 re-rules the lite edition (owner ruling 2026-10-12, R212): lite is the full architecture deployed without self-hosted infrastructure. Lite regains the worker app, queue, event bus, saga, reactors, fenced jobs, projections, realtime and event contracts through their Supabase adapters, and full's unit world; only the e2e and contract layers, the browser journey, UAT records, the socket and graphql-ws doors and the self-hosted stack stay forbidden. The lite-only `be.feature.realtime.supabase` door is added and `repo.tests-forbidden` is removed.
7
+ - Changed: lite sync, scaffold and `add` render full's unit run (test scripts, jest config, the tests tsconfig, coverage and the CI unit step with the Codecov upload) and the lite unit specs; the lite identity module owns its access-token verifier token.
8
+ - Changed: carry the canon pins for Grammar 0.9.14, the BE/FE lint canons 3.2.15/8.2.15, jest-preset 2.2.7 and CLI 1.2.14.
9
+ - Changed: one front-end skeleton for lite and full (owner ruling 2026-10-13, knowledge/hfs/fe-stack.yaml). Every product Next app (full `app`, lite `web`; not the marketing `landing`) is scaffolded with the shared app core `templates/fe/skeleton/apps/__app__`: `modules/api/graphql` (the GraphQLResponse union), `modules/toast`, `modules/auth` (one `AuthClient` interface: a full IdP stub, the lite overlay a browser Supabase Auth client with client-side PKCE), `redux/` (store, typed hooks, ReduxProvider, the session slice), `components/providers` (one AppProviders owner composing the theme, redux, SWR config, app effects and the HeroUI toast region), `components/blocks` (SignInForm, SignOutButton, drawn by the home page), `hooks/auth` (session, sign-in, sign-out with its toast), `hooks/swr` (SwrSideEffects) and `hooks/effects` (the session kept in redux by the auth change subscription, no load in an effect). A fresh scaffold is lint-clean in both editions: it holds no code nothing calls, so the Apollo clients and their links arrive with the first GraphQL operation. The lite web app loses `modules/db`, its Server Actions, the `auth/callback` route and the session-refreshing proxy; health/live is its one route handler, and Supabase's redirect URL is the site root.
10
+ - Changed: the scaffold installs every `tier: core` package of fe-stack.yaml at its canon pin in both editions, read from the catalog (product app workspaces take the runtime packages, the app root the codegen, lint and compiler tools), and both editions add the back end's GraphQL stack (@nestjs/graphql, @nestjs/apollo, @apollo/server, graphql).
11
+ - Added: `npm run codegen:graphql`, and `scripts/codegen.mjs` in both editions generates each product app's `modules/api/graphql/__generated__` from `be/contracts/<app>/schema.graphql` with the GraphQL codegen client preset; both editions now run `codegen` before dev, build, typecheck and lint.
12
+ - Changed: the GraphQL door is the same in both editions. The shared be skeleton's `requestOf` reads a resolver's request through the GraphQL context and the errors filter hands GraphQL failures to the formatter; the GraphQL server capability (`modules/platform/graphql`: Apollo driver, error formatter, depth limit, localize plugin over `x-locale`) is written by the first GraphQL feature. Lite `add api` now writes full's GraphQL transport (principal-aware command and handler) and wires it with the server into the api app; the lite HTTP-door templates are removed.
13
+ - Changed: `add table --fe` emits the table's api feature over GraphQL (get and create, with the app_be insert policy and grant) and per product app the query/mutation operation files and the useQuery*Swr / useMutate*Swr hooks with their barrels, never Server Actions or Supabase readers; the first operation of an app also writes its GraphQL transport (`templates/fe/transport/graphql/clients`: the auth and no-auth Apollo clients with the retry, error, timeout, UNAUTHENTICATED refresh, locale, access-token and http links) and the `graphqlUrl` reader of its modules/config, as platform/graphql is written by the first GraphQL feature on the back end. `add app` is no longer refused in lite and renders partials and the shared core in both editions.
14
+ - Fixed: `starci app scaffold --edition lite` loads the installed jest preset's Sonar exclusions like full, so a fresh lite app's sonar-project.properties is the render its own `starci app lint` expects.
15
+ - Fixed: the full api `graphql.module.ts.tpl` imported a `<Feature>Module` the feature module never exported; it imports `<Feature>ApiModule`.
16
+
17
+ ## 4.2.13 - 2026-10-12
18
+
19
+ - Added: HFS manifest 2.2.0 owns app compiler declarations under the declaration-only types/ambient slot, without a runtime entry; runtime source files remain refused there.
20
+ - Fixed: the derived Grammar contract selects all three required peers, react, @heroui/react and @xyflow/react. Full and lite FE scaffolds and the shared UI workspace declare @xyflow/react at the canonical 12.11.6 pin.
21
+ - Fixed: full and lite Next app scaffolds and the four Next app examples declare CSS imports through global declarations in their ambient owner, while missing non-CSS side-effect imports remain errors under TypeScript 6.
22
+ - Changed: carry the merged runtime mirror and canon pins for Grammar 0.9.13 and Stylelint canon 2.0.9, retaining the no-frontend-tests policy.
23
+
3
24
  ## 4.2.12 - 2026-10-12
4
25
 
5
26
  - Changed: bundled canon pins carry @starci/grammar 0.9.12 (the secondary tab track keeps its padding).
@@ -12,9 +33,13 @@
12
33
 
13
34
  - Changed: bundled canon pins carry @starci/grammar 0.9.10 (the grammar ships without spec suites).
14
35
 
36
+ ## 4.2.9 - 2026-10-12
37
+
38
+ - Changed: refresh the bundled canon pins for CLI 1.2.9 and the BE/FE lint canons 3.2.10/8.2.10 after correcting the CLI runtime install pin; app command implementations are unchanged.
39
+
15
40
  ## 4.2.8 - 2026-10-12
16
41
 
17
- - Changed: refresh the bundled canon pins for grammar 0.9.9; behaviour is unchanged.
42
+ - Changed: refresh the bundled runtime and canon pins for grammar 0.9.9, the BE/FE lint canons 3.2.9/8.2.9, stylelint-canon 2.0.8 and CLI 1.2.8; app command implementations are unchanged.
18
43
 
19
44
  ## 4.2.7 - 2026-10-12
20
45
 
package/README.md CHANGED
@@ -36,11 +36,15 @@ the app it began instead of leaving a partial tree.
36
36
 
37
37
  The lite additions are deliberately narrow:
38
38
 
39
- - `starci app add table <name> [--fe] [--no-types]` adds the migration and matching back-end capability. `--fe` also adds typed readers
40
- and writers through the front-end database owner; types regenerate by default, while `--no-types` is the explicit opt-out.
39
+ - `starci app add table <name> [--fe] [--no-types]` adds the migration and matching back-end capability. `--fe` also adds the table's
40
+ api feature over GraphQL (get-<singular> query, create-<singular> mutation, the GraphQL server capability on first use) and, in
41
+ every product Next app, one operation file per operation under `modules/api/graphql` and its `useQuery*Swr` / `useMutate*Swr`
42
+ hook under `hooks/swr/api/graphql`; never a Server Action or a Supabase reader. Types regenerate by default, while `--no-types` is
43
+ the explicit opt-out.
41
44
  - `starci app add cli <group>` creates the optional cli app on first use, including its built-in migration and seed groups, then adds
42
45
  the requested group without full-edition specs.
43
- - `starci app add app <name>` refuses in lite because lite has exactly one front-end app. Upgrade before adding another.
46
+ - `starci app add app <name>` adds another Next app in either edition: the edition's app skeleton plus the shared app core
47
+ (modules/api, auth, toast, redux, the providers and the hooks), one fe structure for lite and full (owner ruling 2026-10-13).
44
48
  - An `add` or `new` whose target slot is unavailable in lite stops before writing with `<verb> <noun>: full edition only`.
45
49
 
46
50
  `starci app emit` continues to emit the enabled back-end snapshots and also writes `supabase/types/database.types.ts` when
package/emit/db-types.mjs CHANGED
@@ -109,7 +109,8 @@ function emitDbTypesWithRunner({ root, run }) {
109
109
  const cause = firstLine(stderr) ?? firstLine(result?.error?.message ?? result?.error) ?? 'no output';
110
110
  throw new DbTypesError(`\`${command}\` failed${result?.status !== undefined && result?.status !== null ? ` (exit ${result.status})` : ''}: ${cause}; the local Supabase stack must be up (supabase start)`, { file: dbTypesPath, stderr });
111
111
  }
112
- return `${TYPES_STAMP}${migrationsDigest(root)}\n${stdout}`;
112
+ // The CLI pads blank lines with spaces; the committed file carries none (git diff --check refuses trailing whitespace).
113
+ return `${TYPES_STAMP}${migrationsDigest(root)}\n${stdout.replace(/[ \t]+$/gm, '')}`;
113
114
  }
114
115
 
115
116
  export function emitDbTypes({ root, run = spawn, platform = process.platform } = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/hfs",
3
- "version": "4.2.12",
3
+ "version": "4.2.14",
4
4
  "description": "The StarCi app command implementation for scaffolding, linting, checking and synchronizing one product repository.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,43 +14,38 @@ purpose: >-
14
14
  pins:
15
15
  # --- @starci packages. Every @starci package is a PUBLISHED npm package (install: registry): a product repository
16
16
  # installs the exact pinned version from the npm registry in the root and every workspace package.json, never `file:`.
17
- '@starci/heroicons':
18
- version: 0.3.3
19
- group: starci
20
- install: registry
21
- side: fe
22
- source: packages/heroicons/package.json
23
- why: 'StarCi custom Heroicons-compatible cuts; eslint-canon-fe (icon.mjs) admits @starci/heroicons/24/outline and /16/solid as icon sources, so an app that uses them gets this exact version.'
17
+ # @starci/heroicons has no pin since 1.0.0-alpha.13: the application icon set is @phosphor-icons/react (fe-stack.yaml
18
+ # icons) and eslint-canon-fe one-icon-set refuses every other glyph catalogue, so no product repository installs it.
24
19
  '@starci/grammar':
25
- version: 0.9.12
20
+ version: 0.9.14
26
21
  group: starci
27
22
  install: registry
28
23
  side: fe
29
24
  source: packages/grammar/package.json
30
25
  why: 'the grammar front ends read: the brand layer sets `--font-sans` and `--font-mono` and the grammar reads them; it carries the Input tel kind, the copyable and read-only Input, the IconButton disclosure props and count badge, ChatBubble, FillViewport, Textarea mentions and Enter-to-send, the leading PrimaryRailLayout rail, the plate-less Image, ListRow, the soft Alert, Progress steps, the floating primary IconButton, FlowGraph, the STATE-8 loading views and the bordered selectable TagGroup chips. The runtime source version wins for a @starci package.'
31
26
  '@starci/eslint-canon-be':
32
- version: 3.2.13
27
+ version: 3.2.16
33
28
  group: starci
34
29
  install: registry
35
30
  side: be
36
31
  source: packages/eslint/be/package.json
37
32
  why: 'the back-end ESLint canon: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; `starciBeConfig({ hfs: loadHfs(import.meta.url) })` is the typed factory and the BE-CONVENTION laws are its rules.'
38
33
  '@starci/eslint-canon-fe':
39
- version: 8.2.13
34
+ version: 8.2.15
40
35
  group: starci
41
36
  install: registry
42
37
  side: fe
43
38
  source: packages/eslint/fe/package.json
44
39
  why: 'the front-end ESLint canon: `loadHfs(import.meta.url)` of fe/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the fe side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; the front end has no tests (FE_NO_TESTS R97 in hfs) and no-vietnamese-in-source (R91) applies.'
45
40
  '@starci/stylelint-canon':
46
- version: 2.0.7
41
+ version: 2.0.9
47
42
  group: starci
48
43
  install: registry
49
44
  side: fe
50
45
  source: packages/stylelint/package.json
51
46
  why: 'the front-end stylelint canon: no-class-selector refuses a compound class selector (`div.card`); the brand layer accepts `--font-sans` and `--font-mono`; status-contrast measures the HeroUI soft pairs per theme; brand-layer-shape reads the shared :root,.light,.dark block and the info soft pair; appTokens come from loadAppTokens(import.meta.url) for the managed one-line stylelint.config.mjs.'
52
47
  '@starci/tsconfig':
53
- version: 2.0.3
48
+ version: 2.0.4
54
49
  group: starci
55
50
  install: registry
56
51
  side: both
@@ -62,7 +57,7 @@ pins:
62
57
  side: both
63
58
  source: packages/prettier-config/package.json
64
59
  '@starci/jest-preset':
65
- version: 2.2.6
60
+ version: 2.2.7
66
61
  group: starci
67
62
  install: registry
68
63
  side: be
@@ -76,14 +71,14 @@ pins:
76
71
  source: packages/test-world/package.json
77
72
  why: 'the shared e2e library of every back end (R47, R48), a devDependency of every back end: the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs and the outage lock. Kafka is real own infra (the one digest-pinned apache/kafka KRaft image, a broker listener and proxy per slot, slot-prefixed topics, consumer groups and client ids) and Postgres is schema-per-context (a schema and a login role per context in a shared database). Each worker has its own data slot (<ns>_w<k>) with its own databases, Keycloak realm, leased Redis DB, bucket/collection/topic prefixes, fakes host, toxiproxy proxies and outage lock; a run provisions min(jest workers, workers cap, default 2) slots, and state file protocol 2 pairs exactly with @starci/jest-preset (TEST_WORLD_PAIR_MISMATCH otherwise). A modules world boots real peer apps beside its modules, world.apps.<name>.during(fn) is the outage of a peer app, w.keycloak.clientSecret(client) answers the run-generated secret of a confidential realm client, world.keycloak.events/sessions read the realm user events and live sessions, world.infra.postgresql.connection(name) takes one database connection down while the others serve, and world.resolve and scope.resolve take any Nest token. Every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json); the globalSetup registers the path aliases as TypeScript resolves them. Its CLI main is exported at ./cli for starci app stack.'
78
73
  '@starci/cli':
79
- version: 1.2.12
74
+ version: 1.2.14
80
75
  group: starci
81
76
  install: registry
82
77
  side: both
83
78
  source: packages/cli/package.json
84
79
  why: 'the only StarCi command package; product repositories install it for starci app, while @starci/hfs remains its pinned transitive implementation.'
85
80
  '@starci/hfs':
86
- version: 4.2.12
81
+ version: 4.2.14
87
82
  group: starci
88
83
  install: registry
89
84
  side: both
@@ -274,6 +269,11 @@ pins:
274
269
  side: be
275
270
  why: the e2e world of a back end calls the app over HTTP (/health/live in the skeleton); two product back ends resolve 7.3.0, one 7.2.2.
276
271
  # --- ui
272
+ '@xyflow/react':
273
+ version: 12.11.6
274
+ group: ui
275
+ side: fe
276
+ why: 'Grammar FlowGraph imports React Flow from @xyflow/react; every Grammar consumer declares this required peer at the version used by the Grammar package.'
277
277
  '@heroui/react':
278
278
  version: 3.2.6
279
279
  group: ui
@@ -294,6 +294,198 @@ pins:
294
294
  group: ui
295
295
  side: fe
296
296
  why: 'the PostCSS plugin tailwindcss 4 runs through; the reference app declares ^4 and resolves 4.3.3.'
297
+ # --- front-end stack (knowledge/hfs/fe-stack.yaml; owner ruling 2026-10-13: lite = full)
298
+ '@apollo/client':
299
+ version: 4.1.6
300
+ group: framework
301
+ side: fe
302
+ why: 'the GraphQL client of every front end (fe-stack graphql): auth and no-auth clients with the link chain in modules/api/graphql; version read from the starci-academy package-lock on 2026-10-13.'
303
+ '@graphql-codegen/cli':
304
+ version: 7.4.6
305
+ group: tooling
306
+ side: fe
307
+ why: 'generates the front-end wire types from be/contracts/<app>/schema.graphql into modules/api/graphql/__generated__ (fe-stack codegen); the latest registry version on 2026-10-13, academy has none.'
308
+ '@graphql-codegen/client-preset':
309
+ version: 6.2.3
310
+ group: tooling
311
+ side: fe
312
+ why: 'the codegen preset of the front-end wire types (fe-stack codegen); the latest registry version on 2026-10-13.'
313
+ 'axios':
314
+ version: 1.15.2
315
+ group: framework
316
+ side: fe
317
+ why: 'the REST client of the front end for uploads, presigned URLs and auth endpoints (fe-stack rest); version read from the starci-academy package-lock on 2026-10-13.'
318
+ 'swr':
319
+ version: 2.4.2
320
+ group: framework
321
+ side: fe
322
+ why: 'the data hooks and cache of every front end, hooks/swr (fe-stack data); version read from the starci-academy package-lock on 2026-10-13.'
323
+ 'react-hook-form':
324
+ version: 7.77.0
325
+ group: framework
326
+ side: fe
327
+ why: 'the form library, hooks/rhf (fe-stack forms); version read from the starci-academy package-lock on 2026-10-13.'
328
+ '@hookform/resolvers':
329
+ version: 5.4.0
330
+ group: framework
331
+ side: fe
332
+ why: 'the zod resolver of react-hook-form (fe-stack forms); version read from the starci-academy package-lock on 2026-10-13.'
333
+ 'zod':
334
+ version: 4.4.3
335
+ group: framework
336
+ side: fe
337
+ why: 'the schema library of front-end forms and boundary parsing (fe-stack forms); version read from the starci-academy package-lock on 2026-10-13.'
338
+ '@reduxjs/toolkit':
339
+ version: 2.11.2
340
+ group: framework
341
+ side: fe
342
+ why: 'app-wide shared state, redux/ (fe-stack state-global); version read from the starci-academy package-lock on 2026-10-13.'
343
+ 'react-redux':
344
+ version: 9.2.0
345
+ group: framework
346
+ side: fe
347
+ why: 'the React binding of the app-wide store (fe-stack state-global); version read from the starci-academy package-lock on 2026-10-13.'
348
+ 'zustand':
349
+ version: 5.0.12
350
+ group: framework
351
+ side: fe
352
+ why: 'feature-local client state, hooks/zustand (fe-stack state-local); version read from the starci-academy package-lock on 2026-10-13.'
353
+ 'socket.io-client':
354
+ version: 4.8.3
355
+ group: framework
356
+ side: fe
357
+ why: 'the realtime client, hooks/socketio (fe-stack realtime); version read from the starci-academy package-lock on 2026-10-13.'
358
+ 'eventemitter2':
359
+ version: 6.4.9
360
+ group: framework
361
+ side: fe
362
+ why: 'the fan-out emitter behind the socket side-effect component (fe-stack realtime); version read from the starci-academy package-lock on 2026-10-13.'
363
+ '@phosphor-icons/react':
364
+ version: 2.1.10
365
+ group: ui
366
+ side: fe
367
+ why: 'the one application icon set (fe-stack icons); version read from the starci-academy package-lock on 2026-10-13.'
368
+ 'next-themes':
369
+ version: 0.4.6
370
+ group: ui
371
+ side: fe
372
+ why: 'light and dark theme switching in components/providers (fe-stack theme); version read from the starci-academy package-lock on 2026-10-13.'
373
+ 'framer-motion':
374
+ version: 12.42.2
375
+ group: ui
376
+ side: fe
377
+ why: 'motion (fe-stack motion); version read from the starci-academy package-lock on 2026-10-13.'
378
+ 'dayjs':
379
+ version: 1.11.21
380
+ group: framework
381
+ side: fe
382
+ why: 'date math and parsing, modules/dayjs (fe-stack dates); display goes through the next-intl formatter; version read from the starci-academy package-lock on 2026-10-13.'
383
+ 'decimal.js':
384
+ version: 10.6.0
385
+ group: framework
386
+ side: fe
387
+ why: 'money and decimal arithmetic, modules/utils (fe-stack numbers); version read from the starci-academy package-lock on 2026-10-13.'
388
+ 'superjson':
389
+ version: 2.2.6
390
+ group: framework
391
+ side: fe
392
+ why: 'serialization of stored values, modules/superjson and modules/storage (fe-stack storage); version read from the starci-academy package-lock on 2026-10-13.'
393
+ 'lodash':
394
+ version: 4.17.23
395
+ group: framework
396
+ side: fe
397
+ why: 'per-method utilities, modules/utils (fe-stack utils); version read from the starci-academy package-lock on 2026-10-13.'
398
+ 'usehooks-ts':
399
+ version: 3.1.1
400
+ group: framework
401
+ side: fe
402
+ why: 'reusable React hooks, hooks/reuseables (fe-stack utils); version read from the starci-academy package-lock on 2026-10-13.'
403
+ 'recharts':
404
+ version: 3.8.1
405
+ group: ui
406
+ side: fe
407
+ why: 'charts (fe-stack charts); version read from the starci-academy package-lock on 2026-10-13.'
408
+ 'react-markdown':
409
+ version: 10.1.0
410
+ group: ui
411
+ side: fe
412
+ why: 'markdown rendering (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
413
+ 'remark-gfm':
414
+ version: 4.0.1
415
+ group: ui
416
+ side: fe
417
+ why: 'GitHub-flavoured markdown for react-markdown (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
418
+ 'remark-directive':
419
+ version: 4.0.0
420
+ group: ui
421
+ side: fe
422
+ why: 'markdown directives for react-markdown (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
423
+ 'react-shiki':
424
+ version: 0.9.2
425
+ group: ui
426
+ side: fe
427
+ why: 'code highlighting (fe-stack code); version read from the starci-academy package-lock on 2026-10-13.'
428
+ '@monaco-editor/react':
429
+ version: 4.7.0
430
+ group: ui
431
+ side: fe
432
+ why: 'the code editor (fe-stack code); version read from the starci-academy package-lock on 2026-10-13.'
433
+ 'mermaid':
434
+ version: 11.16.0
435
+ group: ui
436
+ side: fe
437
+ why: 'diagrams (fe-stack diagrams); version read from the starci-academy package-lock on 2026-10-13.'
438
+ 'react-dropzone':
439
+ version: 15.0.0
440
+ group: ui
441
+ side: fe
442
+ why: 'file drop zones of uploads (fe-stack upload); version read from the starci-academy package-lock on 2026-10-13.'
443
+ 'react-pdf':
444
+ version: 10.4.1
445
+ group: ui
446
+ side: fe
447
+ why: 'PDF viewing (fe-stack pdf); version read from the starci-academy package-lock on 2026-10-13.'
448
+ '@fingerprintjs/fingerprintjs':
449
+ version: 5.2.0
450
+ group: framework
451
+ side: fe
452
+ why: 'the device id of the GraphQL fingerprint link (fe-stack device-id); version read from the starci-academy package-lock on 2026-10-13.'
453
+ '@sentry/nextjs':
454
+ version: 11.7.0
455
+ group: framework
456
+ side: fe
457
+ why: 'error reporting through sentry.*.config.ts and instrumentation.ts (fe-stack observability); the latest registry version on 2026-10-13, academy has none.'
458
+ # --- back-end transports the front end calls, and the realtime gateway
459
+ '@nestjs/graphql':
460
+ version: 13.4.5
461
+ group: framework
462
+ side: be
463
+ why: 'the GraphQL transport of every back end (be.transport.graphql) that the front end calls; the ecommerce-app resolution on 2026-10-13.'
464
+ '@nestjs/apollo':
465
+ version: 13.4.5
466
+ group: framework
467
+ side: be
468
+ why: 'the Apollo driver of @nestjs/graphql; the ecommerce-app resolution on 2026-10-13.'
469
+ '@apollo/server':
470
+ version: 5.5.1
471
+ group: framework
472
+ side: be
473
+ why: 'the Apollo server behind @nestjs/apollo; the ecommerce-app resolution on 2026-10-13.'
474
+ '@nestjs/websockets':
475
+ version: 11.2.5
476
+ group: framework
477
+ side: be
478
+ why: 'the socket.io gateway of a realtime back end (lite runs it in the api process, owner ruling 2026-10-13); the @nestjs/core pin line.'
479
+ '@nestjs/platform-socket.io':
480
+ version: 11.2.5
481
+ group: framework
482
+ side: be
483
+ why: 'the socket.io platform of @nestjs/websockets; the @nestjs/core pin line.'
484
+ '@as-integrations/express5':
485
+ version: 1.1.2
486
+ group: framework
487
+ side: be
488
+ why: 'the Express 5 integration @nestjs/apollo loads at boot (R111 nestjs-apollo-express5, knowledge/hfs/peer-integrations.yaml); every api app that serves GraphQL installs it; the latest registry version on 2026-10-13.'
297
489
  # --- Supabase lite
298
490
  '@supabase/ssr':
299
491
  version: 0.12.7
@@ -0,0 +1,218 @@
1
+ schema: starci/fe-stack@1
2
+ purpose: >-
3
+ The front-end stack of every app, lite and full alike (owner ruling 2026-10-13, R212 case-2): for each common front-end concern the
4
+ ONE canonical library, its HFS home (slot ids of knowledge/hfs/slots.yaml), the pattern id that teaches it
5
+ (knowledge/patterns/fe/*.yaml) and the alternatives a front end never depends on or imports. Versions live in
6
+ knowledge/hfs/canon-pins.yaml only. `tier: core` packages are installed by every scaffold; `opt` packages are added when a feature
7
+ needs the concern. Source example: starci-lab/starci-academy (origin/main, 2026-10-13); it illustrates, this file decides.
8
+ concerns:
9
+ - id: framework
10
+ tier: core
11
+ packages: [next, react, react-dom, typescript]
12
+ home: [fe.route, fe.source-root-pinned]
13
+ pattern: FE-ROUTE
14
+ forbids: []
15
+ why: App Router with thin routes; route handlers (except health/live and the auth callback slot) and Server Actions are refused by FE_ROUTE_HANDLER_FORBIDDEN and FE_SERVER_ACTION_FORBIDDEN.
16
+ - id: graphql
17
+ tier: core
18
+ packages: ['@apollo/client', graphql]
19
+ home: [fe.modules.api, fe.transport.client, fe.transport.outcome]
20
+ pattern: FE-API-GRAPHQL
21
+ forbids: [urql, graphql-request, '@urql/core', graphql-hooks, apollo-boost]
22
+ why: one file per operation under modules/api/graphql/{queries,mutations}, run by the auth or no-auth Apollo client with the link chain.
23
+ - id: codegen
24
+ tier: core
25
+ packages: ['@graphql-codegen/cli', '@graphql-codegen/client-preset']
26
+ home: [fe.modules.api]
27
+ pattern: FE-WIRE-GENERATED
28
+ forbids: []
29
+ why: the wire types are generated from be/contracts/<app>/schema.graphql into modules/api/graphql/__generated__, never typed by hand.
30
+ - id: rest
31
+ tier: core
32
+ packages: [axios]
33
+ home: [fe.modules.api]
34
+ pattern: FE-API-REST
35
+ forbids: [ky, superagent, node-fetch, cross-fetch, isomorphic-fetch]
36
+ why: modules/api/rest/<area>/*.ts for uploads, presigned URLs and auth endpoints only; GraphQL carries every other call.
37
+ - id: data
38
+ tier: core
39
+ packages: [swr]
40
+ home: [fe.hooks.swr]
41
+ pattern: FE-DATA-SWR
42
+ forbids: ['@tanstack/react-query', react-query]
43
+ why: hooks/swr/api/{graphql,rest}/{queries,mutations}/use*Swr.ts wrap one operation each; components call hooks only.
44
+ - id: toast
45
+ tier: core
46
+ packages: ['@heroui/react']
47
+ home: [fe.modules.toast]
48
+ pattern: FE-TOAST
49
+ forbids: [sonner, react-toastify, react-hot-toast, notistack]
50
+ why: mutation outcomes are reported by modules/toast (showGraphQLToast, use*WithToast) with next-intl labels.
51
+ - id: forms
52
+ tier: core
53
+ packages: [react-hook-form, '@hookform/resolvers', zod]
54
+ home: [fe.hooks.rhf]
55
+ pattern: FE-FORM
56
+ forbids: [formik, yup, final-form, react-final-form]
57
+ why: hooks/rhf/use<X>Form.ts builds a zod schema with next-intl messages and submits through an SWR mutation and the toast.
58
+ - id: state-global
59
+ tier: core
60
+ packages: ['@reduxjs/toolkit', react-redux]
61
+ home: [fe.redux]
62
+ pattern: FE-STATE-REDUX
63
+ forbids: [mobx, mobx-react, mobx-react-lite, recoil, jotai, valtio, redux-saga]
64
+ why: app-wide shared state (selected ids synced from the route, server-pushed entities) lives in redux/slices.
65
+ - id: state-local
66
+ tier: core
67
+ packages: [zustand]
68
+ home: [fe.hooks.zustand]
69
+ pattern: FE-STATE-ZUSTAND
70
+ forbids: []
71
+ why: feature-local UI, wizard and editor state lives in hooks/zustand/<store>.
72
+ - id: realtime
73
+ tier: opt
74
+ packages: [socket.io-client, eventemitter2]
75
+ home: [fe.hooks.socketio]
76
+ pattern: FE-REALTIME
77
+ forbids: [graphql-ws, subscriptions-transport-ws, '@supabase/realtime-js', pusher-js, ably]
78
+ why: one socket.io-client singleton per namespace and one side-effect component that dispatches to redux and SWR; lite runs the gateway in the be api process.
79
+ - id: auth
80
+ tier: core
81
+ packages: []
82
+ home: [fe.modules.auth]
83
+ pattern: FE-AUTH
84
+ forbids: []
85
+ why: modules/auth is the identity-provider client (lite - the @supabase/supabase-js auth client; full - the app's IdP client); it yields the session and the access token for the Apollo link and never reads data.
86
+ - id: i18n
87
+ tier: core
88
+ packages: [next-intl]
89
+ home: [fe.modules.i18n]
90
+ pattern: FE-I18N
91
+ forbids: [i18next, react-i18next, next-i18next, react-intl, '@lingui/react']
92
+ why: useTranslations for copy and the next-intl formatter for numbers, dates and currency.
93
+ - id: ui
94
+ tier: core
95
+ packages: ['@heroui/react', '@heroui/styles', tailwindcss, '@starci/grammar']
96
+ home: [fe.components, fe.feature]
97
+ pattern: FE-UI
98
+ forbids: ['@mantine/core', '@mui/material', '@chakra-ui/react', antd, styled-components, '@emotion/react', '@emotion/styled']
99
+ why: the grammar renderers over HeroUI and Tailwind; no second UI kit and no CSS-in-JS.
100
+ - id: icons
101
+ tier: core
102
+ packages: ['@phosphor-icons/react']
103
+ home: [fe.components]
104
+ pattern: FE-ICON
105
+ forbids: [react-icons, lucide-react, '@iconify/react', '@gravity-ui/icons', '@tabler/icons-react', '@fortawesome/react-fontawesome']
106
+ why: one application icon set.
107
+ - id: theme
108
+ tier: core
109
+ packages: [next-themes]
110
+ home: [fe.components.providers]
111
+ pattern: FE-THEME
112
+ forbids: []
113
+ why: the theme provider lives in components/providers.
114
+ - id: motion
115
+ tier: opt
116
+ packages: [framer-motion]
117
+ home: [fe.components]
118
+ pattern: FE-MOTION
119
+ forbids: [react-spring, '@react-spring/web', gsap]
120
+ why: one motion library.
121
+ - id: dates
122
+ tier: core
123
+ packages: [dayjs]
124
+ home: [fe.modules]
125
+ pattern: FE-DATE
126
+ forbids: [moment, moment-timezone, date-fns, luxon]
127
+ why: dayjs (modules/dayjs) for date math and parsing; display through the next-intl formatter.
128
+ - id: numbers
129
+ tier: opt
130
+ packages: [decimal.js]
131
+ home: [fe.modules]
132
+ pattern: FE-NUMBER
133
+ forbids: [big.js, bignumber.js, numeral]
134
+ why: decimal.js for money arithmetic in modules/utils; display through the next-intl formatter.
135
+ - id: storage
136
+ tier: core
137
+ packages: [superjson]
138
+ home: [fe.modules]
139
+ pattern: FE-STORAGE
140
+ forbids: [localforage, store2]
141
+ why: modules/storage (local and session storage) serializes through modules/superjson; components never touch storage directly.
142
+ - id: utils
143
+ tier: opt
144
+ packages: [lodash, usehooks-ts]
145
+ home: [fe.modules, fe.hooks.reuseables]
146
+ pattern: FE-UTILS
147
+ forbids: [underscore, ramda, lodash-es]
148
+ why: per-method lodash imports in modules/utils and reusable hooks in hooks/reuseables.
149
+ - id: charts
150
+ tier: opt
151
+ packages: [recharts]
152
+ home: [fe.components]
153
+ pattern: FE-CHART
154
+ forbids: [chart.js, react-chartjs-2, '@nivo/core', d3, echarts, echarts-for-react]
155
+ why: one chart library, styled by the dataviz grammar.
156
+ - id: flow
157
+ tier: opt
158
+ packages: ['@xyflow/react']
159
+ home: [fe.components]
160
+ pattern: FE-FLOW
161
+ forbids: [reactflow, cytoscape]
162
+ why: graph canvases go through the grammar FlowGraph over React Flow.
163
+ - id: markdown
164
+ tier: opt
165
+ packages: [react-markdown, remark-gfm, remark-directive]
166
+ home: [fe.components]
167
+ pattern: FE-MARKDOWN
168
+ forbids: [marked, markdown-it, showdown]
169
+ why: markdown renders through react-markdown, never dangerouslySetInnerHTML.
170
+ - id: code
171
+ tier: opt
172
+ packages: [react-shiki, '@monaco-editor/react']
173
+ home: [fe.components]
174
+ pattern: FE-CODE
175
+ forbids: [prismjs, react-syntax-highlighter, highlight.js, '@uiw/react-codemirror', codemirror]
176
+ why: react-shiki highlights and Monaco edits.
177
+ - id: diagrams
178
+ tier: opt
179
+ packages: [mermaid]
180
+ home: [fe.components]
181
+ pattern: FE-DIAGRAM
182
+ forbids: []
183
+ why: diagrams render through mermaid in a block.
184
+ - id: upload
185
+ tier: opt
186
+ packages: [react-dropzone]
187
+ home: [fe.components, fe.modules.api]
188
+ pattern: FE-UPLOAD
189
+ forbids: [react-dropzone-uploader, filepond, react-filepond, uppy]
190
+ why: react-dropzone in a block and a presigned PUT through modules/api/rest.
191
+ - id: pdf
192
+ tier: opt
193
+ packages: [react-pdf]
194
+ home: [fe.components]
195
+ pattern: FE-PDF
196
+ forbids: ['@react-pdf-viewer/core', pdfjs-dist]
197
+ why: PDF viewing through react-pdf in a block.
198
+ - id: device-id
199
+ tier: opt
200
+ packages: ['@fingerprintjs/fingerprintjs']
201
+ home: [fe.transport.client]
202
+ pattern: FE-API-GRAPHQL
203
+ forbids: []
204
+ why: the fingerprint link of the Apollo client attaches the device id.
205
+ - id: observability
206
+ tier: opt
207
+ packages: ['@sentry/nextjs']
208
+ home: [fe.source-root-pinned]
209
+ pattern: FE-OBSERVE
210
+ forbids: [logrocket, bugsnag-js, '@bugsnag/js', '@datadog/browser-rum']
211
+ why: errors are reported through @sentry/nextjs (sentry.*.config.ts, instrumentation.ts), never console-only.
212
+ - id: lint
213
+ tier: core
214
+ packages: [eslint, prettier, '@starci/eslint-canon-fe', '@starci/prettier-config']
215
+ home: [fe.tool-config]
216
+ pattern: FE-LINT
217
+ forbids: []
218
+ why: the canon lint and format configuration.