@starci/hfs 4.2.13 → 4.2.15

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 +18 -0
  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 +200 -8
  6. package/runtime/knowledge/hfs/fe-stack.yaml +221 -0
  7. package/runtime/knowledge/hfs/rules.yaml +50 -47
  8. package/runtime/knowledge/hfs/slots.yaml +307 -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 +43 -0
  20. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +0 -2
  21. package/runtime/scripts/hfs/architecture/config.mjs +2 -1
  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 +140 -36
  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 +32 -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,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.2.15 - 2026-10-13
4
+
5
+ - Changed: the heroicons family is the one application icon set (owner ruling 2026-10-13): the scaffold installs `@heroicons/react` 2.2.0 and `@starci/heroicons` 0.3.3 (fe-stack `icons`, tier core) in every product fe app in place of `@phosphor-icons/react`, and the `modules/toast` skeleton draws its status indicators with `@heroicons/react/24/outline` (CheckCircleIcon, XCircleIcon, ExclamationTriangleIcon, InformationCircleIcon). Carry the canon pins of eslint-canon-be 3.2.17, eslint-canon-fe 8.2.16 and CLI 1.2.15.
6
+
7
+ ## 4.2.14 - 2026-10-13
8
+
9
+ - 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`.
10
+ - 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.
11
+ - 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.
12
+ - 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.
13
+ - 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.
14
+ - 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).
15
+ - 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.
16
+ - 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.
17
+ - 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.
18
+ - 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.
19
+ - Fixed: the full api `graphql.module.ts.tpl` imported a `<Feature>Module` the feature module never exported; it imports `<Feature>ApiModule`.
20
+
3
21
  ## 4.2.13 - 2026-10-12
4
22
 
5
23
  - 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.
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.13",
3
+ "version": "4.2.15",
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",
@@ -15,28 +15,28 @@ 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
17
  '@starci/heroicons':
18
- version: 0.3.3
18
+ version: 0.3.4
19
19
  group: starci
20
20
  install: registry
21
21
  side: fe
22
22
  source: packages/heroicons/package.json
23
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.'
24
24
  '@starci/grammar':
25
- version: 0.9.13
25
+ version: 0.9.14
26
26
  group: starci
27
27
  install: registry
28
28
  side: fe
29
29
  source: packages/grammar/package.json
30
30
  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
31
  '@starci/eslint-canon-be':
32
- version: 3.2.14
32
+ version: 3.2.17
33
33
  group: starci
34
34
  install: registry
35
35
  side: be
36
36
  source: packages/eslint/be/package.json
37
37
  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
38
  '@starci/eslint-canon-fe':
39
- version: 8.2.14
39
+ version: 8.2.16
40
40
  group: starci
41
41
  install: registry
42
42
  side: fe
@@ -50,7 +50,7 @@ pins:
50
50
  source: packages/stylelint/package.json
51
51
  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
52
  '@starci/tsconfig':
53
- version: 2.0.3
53
+ version: 2.0.4
54
54
  group: starci
55
55
  install: registry
56
56
  side: both
@@ -62,7 +62,7 @@ pins:
62
62
  side: both
63
63
  source: packages/prettier-config/package.json
64
64
  '@starci/jest-preset':
65
- version: 2.2.6
65
+ version: 2.2.7
66
66
  group: starci
67
67
  install: registry
68
68
  side: be
@@ -76,14 +76,14 @@ pins:
76
76
  source: packages/test-world/package.json
77
77
  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
78
  '@starci/cli':
79
- version: 1.2.13
79
+ version: 1.2.15
80
80
  group: starci
81
81
  install: registry
82
82
  side: both
83
83
  source: packages/cli/package.json
84
84
  why: 'the only StarCi command package; product repositories install it for starci app, while @starci/hfs remains its pinned transitive implementation.'
85
85
  '@starci/hfs':
86
- version: 4.2.13
86
+ version: 4.2.15
87
87
  group: starci
88
88
  install: registry
89
89
  side: both
@@ -299,6 +299,198 @@ pins:
299
299
  group: ui
300
300
  side: fe
301
301
  why: 'the PostCSS plugin tailwindcss 4 runs through; the reference app declares ^4 and resolves 4.3.3.'
302
+ # --- front-end stack (knowledge/hfs/fe-stack.yaml; owner ruling 2026-10-13: lite = full)
303
+ '@apollo/client':
304
+ version: 4.1.6
305
+ group: framework
306
+ side: fe
307
+ 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.'
308
+ '@graphql-codegen/cli':
309
+ version: 7.4.6
310
+ group: tooling
311
+ side: fe
312
+ 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.'
313
+ '@graphql-codegen/client-preset':
314
+ version: 6.2.3
315
+ group: tooling
316
+ side: fe
317
+ why: 'the codegen preset of the front-end wire types (fe-stack codegen); the latest registry version on 2026-10-13.'
318
+ 'axios':
319
+ version: 1.15.2
320
+ group: framework
321
+ side: fe
322
+ 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.'
323
+ 'swr':
324
+ version: 2.4.2
325
+ group: framework
326
+ side: fe
327
+ 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.'
328
+ 'react-hook-form':
329
+ version: 7.77.0
330
+ group: framework
331
+ side: fe
332
+ why: 'the form library, hooks/rhf (fe-stack forms); version read from the starci-academy package-lock on 2026-10-13.'
333
+ '@hookform/resolvers':
334
+ version: 5.4.0
335
+ group: framework
336
+ side: fe
337
+ why: 'the zod resolver of react-hook-form (fe-stack forms); version read from the starci-academy package-lock on 2026-10-13.'
338
+ 'zod':
339
+ version: 4.4.3
340
+ group: framework
341
+ side: fe
342
+ 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.'
343
+ '@reduxjs/toolkit':
344
+ version: 2.11.2
345
+ group: framework
346
+ side: fe
347
+ why: 'app-wide shared state, redux/ (fe-stack state-global); version read from the starci-academy package-lock on 2026-10-13.'
348
+ 'react-redux':
349
+ version: 9.2.0
350
+ group: framework
351
+ side: fe
352
+ 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.'
353
+ 'zustand':
354
+ version: 5.0.12
355
+ group: framework
356
+ side: fe
357
+ why: 'feature-local client state, hooks/zustand (fe-stack state-local); version read from the starci-academy package-lock on 2026-10-13.'
358
+ 'socket.io-client':
359
+ version: 4.8.3
360
+ group: framework
361
+ side: fe
362
+ why: 'the realtime client, hooks/socketio (fe-stack realtime); version read from the starci-academy package-lock on 2026-10-13.'
363
+ 'eventemitter2':
364
+ version: 6.4.9
365
+ group: framework
366
+ side: fe
367
+ 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.'
368
+ '@heroicons/react':
369
+ version: 2.2.0
370
+ group: ui
371
+ side: fe
372
+ why: 'the base of the one application icon set (fe-stack icons, with @starci/heroicons for the glyphs it lacks); eslint-canon-fe (icon.mjs) admits @heroicons/react/24/outline and /16/solid.'
373
+ 'next-themes':
374
+ version: 0.4.6
375
+ group: ui
376
+ side: fe
377
+ why: 'light and dark theme switching in components/providers (fe-stack theme); version read from the starci-academy package-lock on 2026-10-13.'
378
+ 'framer-motion':
379
+ version: 12.42.2
380
+ group: ui
381
+ side: fe
382
+ why: 'motion (fe-stack motion); version read from the starci-academy package-lock on 2026-10-13.'
383
+ 'dayjs':
384
+ version: 1.11.21
385
+ group: framework
386
+ side: fe
387
+ 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.'
388
+ 'decimal.js':
389
+ version: 10.6.0
390
+ group: framework
391
+ side: fe
392
+ why: 'money and decimal arithmetic, modules/utils (fe-stack numbers); version read from the starci-academy package-lock on 2026-10-13.'
393
+ 'superjson':
394
+ version: 2.2.6
395
+ group: framework
396
+ side: fe
397
+ 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.'
398
+ 'lodash':
399
+ version: 4.17.23
400
+ group: framework
401
+ side: fe
402
+ why: 'per-method utilities, modules/utils (fe-stack utils); version read from the starci-academy package-lock on 2026-10-13.'
403
+ 'usehooks-ts':
404
+ version: 3.1.1
405
+ group: framework
406
+ side: fe
407
+ why: 'reusable React hooks, hooks/reuseables (fe-stack utils); version read from the starci-academy package-lock on 2026-10-13.'
408
+ 'recharts':
409
+ version: 3.8.1
410
+ group: ui
411
+ side: fe
412
+ why: 'charts (fe-stack charts); version read from the starci-academy package-lock on 2026-10-13.'
413
+ 'react-markdown':
414
+ version: 10.1.0
415
+ group: ui
416
+ side: fe
417
+ why: 'markdown rendering (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
418
+ 'remark-gfm':
419
+ version: 4.0.1
420
+ group: ui
421
+ side: fe
422
+ why: 'GitHub-flavoured markdown for react-markdown (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
423
+ 'remark-directive':
424
+ version: 4.0.0
425
+ group: ui
426
+ side: fe
427
+ why: 'markdown directives for react-markdown (fe-stack markdown); version read from the starci-academy package-lock on 2026-10-13.'
428
+ 'react-shiki':
429
+ version: 0.9.2
430
+ group: ui
431
+ side: fe
432
+ why: 'code highlighting (fe-stack code); version read from the starci-academy package-lock on 2026-10-13.'
433
+ '@monaco-editor/react':
434
+ version: 4.7.0
435
+ group: ui
436
+ side: fe
437
+ why: 'the code editor (fe-stack code); version read from the starci-academy package-lock on 2026-10-13.'
438
+ 'mermaid':
439
+ version: 11.16.0
440
+ group: ui
441
+ side: fe
442
+ why: 'diagrams (fe-stack diagrams); version read from the starci-academy package-lock on 2026-10-13.'
443
+ 'react-dropzone':
444
+ version: 15.0.0
445
+ group: ui
446
+ side: fe
447
+ why: 'file drop zones of uploads (fe-stack upload); version read from the starci-academy package-lock on 2026-10-13.'
448
+ 'react-pdf':
449
+ version: 10.4.1
450
+ group: ui
451
+ side: fe
452
+ why: 'PDF viewing (fe-stack pdf); version read from the starci-academy package-lock on 2026-10-13.'
453
+ '@fingerprintjs/fingerprintjs':
454
+ version: 5.2.0
455
+ group: framework
456
+ side: fe
457
+ 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.'
458
+ '@sentry/nextjs':
459
+ version: 11.7.0
460
+ group: framework
461
+ side: fe
462
+ why: 'error reporting through sentry.*.config.ts and instrumentation.ts (fe-stack observability); the latest registry version on 2026-10-13, academy has none.'
463
+ # --- back-end transports the front end calls, and the realtime gateway
464
+ '@nestjs/graphql':
465
+ version: 13.4.5
466
+ group: framework
467
+ side: be
468
+ why: 'the GraphQL transport of every back end (be.transport.graphql) that the front end calls; the ecommerce-app resolution on 2026-10-13.'
469
+ '@nestjs/apollo':
470
+ version: 13.4.5
471
+ group: framework
472
+ side: be
473
+ why: 'the Apollo driver of @nestjs/graphql; the ecommerce-app resolution on 2026-10-13.'
474
+ '@apollo/server':
475
+ version: 5.5.1
476
+ group: framework
477
+ side: be
478
+ why: 'the Apollo server behind @nestjs/apollo; the ecommerce-app resolution on 2026-10-13.'
479
+ '@nestjs/websockets':
480
+ version: 11.2.5
481
+ group: framework
482
+ side: be
483
+ 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.'
484
+ '@nestjs/platform-socket.io':
485
+ version: 11.2.5
486
+ group: framework
487
+ side: be
488
+ why: 'the socket.io platform of @nestjs/websockets; the @nestjs/core pin line.'
489
+ '@as-integrations/express5':
490
+ version: 1.1.2
491
+ group: framework
492
+ side: be
493
+ 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.'
302
494
  # --- Supabase lite
303
495
  '@supabase/ssr':
304
496
  version: 0.12.7
@@ -0,0 +1,221 @@
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: ['@heroicons/react', '@starci/heroicons']
103
+ home: [fe.components]
104
+ pattern: FE-ICON
105
+ forbids: ['@phosphor-icons/react', react-icons, lucide-react, '@iconify/react', '@gravity-ui/icons', '@tabler/icons-react', '@fortawesome/react-fontawesome']
106
+ why: >-
107
+ one application icon set, the heroicons family (owner ruling 2026-10-13), on the 24/outline and 16/solid cuts. @heroicons/react
108
+ is the base set and is used first; @starci/heroicons holds only the glyphs heroicons lacks (brand or domain glyphs drawn in
109
+ heroicons style), never duplicates a heroicons glyph, and a missing glyph is added to it.
110
+ - id: theme
111
+ tier: core
112
+ packages: [next-themes]
113
+ home: [fe.components.providers]
114
+ pattern: FE-THEME
115
+ forbids: []
116
+ why: the theme provider lives in components/providers.
117
+ - id: motion
118
+ tier: opt
119
+ packages: [framer-motion]
120
+ home: [fe.components]
121
+ pattern: FE-MOTION
122
+ forbids: [react-spring, '@react-spring/web', gsap]
123
+ why: one motion library.
124
+ - id: dates
125
+ tier: core
126
+ packages: [dayjs]
127
+ home: [fe.modules]
128
+ pattern: FE-DATE
129
+ forbids: [moment, moment-timezone, date-fns, luxon]
130
+ why: dayjs (modules/dayjs) for date math and parsing; display through the next-intl formatter.
131
+ - id: numbers
132
+ tier: opt
133
+ packages: [decimal.js]
134
+ home: [fe.modules]
135
+ pattern: FE-NUMBER
136
+ forbids: [big.js, bignumber.js, numeral]
137
+ why: decimal.js for money arithmetic in modules/utils; display through the next-intl formatter.
138
+ - id: storage
139
+ tier: core
140
+ packages: [superjson]
141
+ home: [fe.modules]
142
+ pattern: FE-STORAGE
143
+ forbids: [localforage, store2]
144
+ why: modules/storage (local and session storage) serializes through modules/superjson; components never touch storage directly.
145
+ - id: utils
146
+ tier: opt
147
+ packages: [lodash, usehooks-ts]
148
+ home: [fe.modules, fe.hooks.reuseables]
149
+ pattern: FE-UTILS
150
+ forbids: [underscore, ramda, lodash-es]
151
+ why: per-method lodash imports in modules/utils and reusable hooks in hooks/reuseables.
152
+ - id: charts
153
+ tier: opt
154
+ packages: [recharts]
155
+ home: [fe.components]
156
+ pattern: FE-CHART
157
+ forbids: [chart.js, react-chartjs-2, '@nivo/core', d3, echarts, echarts-for-react]
158
+ why: one chart library, styled by the dataviz grammar.
159
+ - id: flow
160
+ tier: opt
161
+ packages: ['@xyflow/react']
162
+ home: [fe.components]
163
+ pattern: FE-FLOW
164
+ forbids: [reactflow, cytoscape]
165
+ why: graph canvases go through the grammar FlowGraph over React Flow.
166
+ - id: markdown
167
+ tier: opt
168
+ packages: [react-markdown, remark-gfm, remark-directive]
169
+ home: [fe.components]
170
+ pattern: FE-MARKDOWN
171
+ forbids: [marked, markdown-it, showdown]
172
+ why: markdown renders through react-markdown, never dangerouslySetInnerHTML.
173
+ - id: code
174
+ tier: opt
175
+ packages: [react-shiki, '@monaco-editor/react']
176
+ home: [fe.components]
177
+ pattern: FE-CODE
178
+ forbids: [prismjs, react-syntax-highlighter, highlight.js, '@uiw/react-codemirror', codemirror]
179
+ why: react-shiki highlights and Monaco edits.
180
+ - id: diagrams
181
+ tier: opt
182
+ packages: [mermaid]
183
+ home: [fe.components]
184
+ pattern: FE-DIAGRAM
185
+ forbids: []
186
+ why: diagrams render through mermaid in a block.
187
+ - id: upload
188
+ tier: opt
189
+ packages: [react-dropzone]
190
+ home: [fe.components, fe.modules.api]
191
+ pattern: FE-UPLOAD
192
+ forbids: [react-dropzone-uploader, filepond, react-filepond, uppy]
193
+ why: react-dropzone in a block and a presigned PUT through modules/api/rest.
194
+ - id: pdf
195
+ tier: opt
196
+ packages: [react-pdf]
197
+ home: [fe.components]
198
+ pattern: FE-PDF
199
+ forbids: ['@react-pdf-viewer/core', pdfjs-dist]
200
+ why: PDF viewing through react-pdf in a block.
201
+ - id: device-id
202
+ tier: opt
203
+ packages: ['@fingerprintjs/fingerprintjs']
204
+ home: [fe.transport.client]
205
+ pattern: FE-API-GRAPHQL
206
+ forbids: []
207
+ why: the fingerprint link of the Apollo client attaches the device id.
208
+ - id: observability
209
+ tier: opt
210
+ packages: ['@sentry/nextjs']
211
+ home: [fe.source-root-pinned]
212
+ pattern: FE-OBSERVE
213
+ forbids: [logrocket, bugsnag-js, '@bugsnag/js', '@datadog/browser-rum']
214
+ why: errors are reported through @sentry/nextjs (sentry.*.config.ts, instrumentation.ts), never console-only.
215
+ - id: lint
216
+ tier: core
217
+ packages: [eslint, prettier, '@starci/eslint-canon-fe', '@starci/prettier-config']
218
+ home: [fe.tool-config]
219
+ pattern: FE-LINT
220
+ forbids: []
221
+ why: the canon lint and format configuration.