@starci/hfs 5.0.7 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +7 -8
  3. package/cache/fake-session.mjs +1 -1
  4. package/cache/hfs-family.mjs +22 -9
  5. package/cache/import-graph.d.mts +32 -4
  6. package/cache/import-graph.mjs +88 -11
  7. package/cache/lint-family.d.mts +10 -3
  8. package/cache/lint-family.mjs +65 -37
  9. package/cache/records.d.mts +7 -0
  10. package/cache/records.mjs +17 -1
  11. package/cache/run.d.mts +1 -1
  12. package/cache/session.mjs +6 -2
  13. package/cache/side-key.d.mts +2 -0
  14. package/cache/side-key.mjs +5 -1
  15. package/cache/structure-memo.d.mts +34 -0
  16. package/cache/structure-memo.mjs +150 -0
  17. package/cache/types.d.mts +6 -2
  18. package/cache/wiring-family.mjs +1 -1
  19. package/codemod/directive-first.d.mts +48 -0
  20. package/codemod/directive-first.mjs +136 -0
  21. package/codemod/explicit-surface-edit.d.mts +14 -0
  22. package/codemod/explicit-surface-edit.mjs +57 -0
  23. package/codemod/explicit-surface-names.d.mts +30 -0
  24. package/codemod/explicit-surface-names.mjs +135 -0
  25. package/codemod/explicit-surface-render.d.mts +29 -0
  26. package/codemod/explicit-surface-render.mjs +197 -0
  27. package/codemod/explicit-surface.d.mts +50 -0
  28. package/codemod/explicit-surface.mjs +374 -0
  29. package/codemod/main.d.mts +7 -5
  30. package/codemod/main.mjs +37 -16
  31. package/codemod/small-renames.mjs +22 -2
  32. package/codemod/source-tree.d.mts +8 -0
  33. package/codemod/source-tree.mjs +23 -0
  34. package/lint/eslint-list.cjs +55 -0
  35. package/lint/run.d.mts +35 -61
  36. package/lint/run.mjs +91 -288
  37. package/package.json +1 -1
  38. package/release/lint-report.d.mts +1 -1
  39. package/release/lint-report.mjs +3 -3
  40. package/report/sonar.d.mts +1 -1
  41. package/report/sonar.mjs +3 -3
  42. package/report/stylelint-findings.d.mts +23 -0
  43. package/report/stylelint-findings.mjs +9 -0
  44. package/runtime/knowledge/hfs/canon-pins.yaml +8 -8
  45. package/runtime/knowledge/hfs/ci-standard.yaml +2 -2
  46. package/runtime/knowledge/hfs/rules.yaml +56 -5
  47. package/runtime/knowledge/hfs/slots.yaml +1 -1
  48. package/runtime/knowledge/sonar-gate.yaml +2 -2
  49. package/runtime/modules/kernel/failure-codes.yaml +4 -4
  50. package/runtime/scripts/api/node/run-node.d.mts +5 -0
  51. package/runtime/scripts/hfs/architecture/test-world-files.d.mts +4 -3
  52. package/runtime/scripts/hfs/architecture/test-world-files.mjs +26 -7
  53. package/runtime/scripts/hfs/check.d.mts +16 -1
  54. package/runtime/scripts/hfs/check.mjs +18 -7
  55. package/runtime/scripts/hfs/explicit-surface-rule.d.mts +58 -0
  56. package/runtime/scripts/hfs/explicit-surface-rule.mjs +59 -0
  57. package/runtime/scripts/hfs/gate-threshold.mjs +1 -1
  58. package/runtime/scripts/hfs/rule-memo.d.mts +30 -0
  59. package/runtime/scripts/hfs/rule-memo.mjs +24 -0
  60. package/runtime/scripts/hfs/rules/a15-findings.d.mts +6 -0
  61. package/runtime/scripts/hfs/rules/a15-findings.mjs +11 -2
  62. package/runtime/scripts/hfs/rules/ci-standard.mjs +2 -2
  63. package/runtime/scripts/hfs/rules/monorepo.d.mts +1 -3
  64. package/runtime/scripts/hfs/rules/monorepo.mjs +9 -11
  65. package/runtime/scripts/hfs/rules/pipeline.mjs +13 -9
  66. package/runtime/scripts/hfs/rules/read.d.mts +5 -0
  67. package/runtime/scripts/hfs/rules/read.mjs +13 -0
  68. package/runtime/scripts/hfs/rules/secrets.d.mts +2 -0
  69. package/runtime/scripts/hfs/rules/secrets.mjs +4 -1
  70. package/runtime/scripts/hfs/rules/stacks-release.mjs +35 -21
  71. package/runtime/scripts/lib/declaration-surface.d.mts +35 -0
  72. package/runtime/scripts/lib/declaration-surface.mjs +112 -0
  73. package/runtime/scripts/lib/worktree-exclude.mjs +1 -1
  74. package/scaffold/app.mjs +3 -3
  75. package/src/check-cache-structure-support.d.mts +52 -0
  76. package/src/main.d.mts +3 -2
  77. package/src/main.mjs +123 -109
  78. package/sync/ci-commands.d.mts +2 -1
  79. package/sync/ci-commands.mjs +19 -20
  80. package/sync/index.mjs +2 -2
  81. package/templates/app/package-scripts/core.json +2 -2
  82. package/templates/app/task-graph/turbo.json +0 -3
  83. package/templates/be/inbox/delivery-recorded.event.ts.tpl +2 -2
  84. package/templates/be/inbox/entities.index.ts.tpl +1 -1
  85. package/templates/be/inbox/module-definition.ts.tpl +8 -2
  86. package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +10 -2
  87. package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +8 -2
  88. package/templates/be/patterns/api/feature.module-definition.ts.tpl +9 -3
  89. package/templates/be/patterns/api/graphql.module-definition.ts.tpl +9 -3
  90. package/templates/be/patterns/cli/group.module-definition.ts.tpl +9 -3
  91. package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +8 -3
  92. package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +3 -2
  93. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -3
  94. package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +8 -3
  95. package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +3 -2
  96. package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +7 -6
  97. package/templates/be/patterns/jobs/job.module-definition.ts.tpl +9 -3
  98. package/templates/be/patterns/jobs/job.processor.ts.tpl +1 -1
  99. package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +9 -3
  100. package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +8 -3
  101. package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +3 -2
  102. package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +5 -4
  103. package/templates/be/patterns/projections/persistence.connection.ts.tpl +3 -2
  104. package/templates/be/patterns/projections/projection.module-definition.ts.tpl +8 -3
  105. package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +3 -2
  106. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -3
  107. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +8 -3
  108. package/templates/be/patterns/queues/queue.module-definition.ts.tpl +9 -3
  109. package/templates/be/patterns/reactors/consumer.ts.tpl +1 -1
  110. package/templates/be/patterns/reactors/message.module-definition.ts.tpl +9 -3
  111. package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +9 -3
  112. package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +9 -3
  113. package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +9 -3
  114. package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +9 -3
  115. package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +1 -1
  116. package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +1 -1
  117. package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +9 -3
  118. package/templates/be/patterns/saga/saga.module-definition.ts.tpl +9 -3
  119. package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +9 -3
  120. package/templates/be/platform/graphql/graphql.module-definition.ts +8 -3
  121. package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +9 -3
  122. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +9 -3
  123. package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +9 -3
  124. package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +9 -3
  125. package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +9 -3
  126. package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +8 -3
  127. package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +8 -3
  128. package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +8 -3
  129. package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +3 -2
  130. package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +2 -1
  131. package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +8 -3
  132. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +8 -3
  133. package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +8 -3
  134. package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -0
  135. package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +10 -4
  136. package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +8 -4
  137. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +10 -4
  138. package/templates/be/skeleton/src/modules/platform/http-security/persistence/connection.ts +3 -2
  139. package/templates/be/skeleton/src/modules/platform/http-security/persistence/rate-limit.sql.ts +3 -2
  140. package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +10 -4
  141. package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +8 -3
  142. package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +8 -3
  143. package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +8 -3
  144. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +1 -1
  145. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +5 -1
  146. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +5 -1
  147. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +1 -1
  148. package/templates/be/skeleton-lite/src/modules/platform/http-security/persistence/entities/index.ts +1 -1
  149. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +7 -1
  150. package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +7 -1
  151. package/templates/be/table/api/feature.module-definition.ts.tpl +9 -3
  152. package/templates/be/table/api/graphql.module-definition.ts.tpl +9 -3
  153. package/templates/be/table/entities.index.ts.tpl +1 -1
  154. package/templates/be/table/module-definition.ts.tpl +8 -3
  155. package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +1 -0
  156. package/templates/be/test-world/world/test-world.config.ts.tpl +8 -1
  157. package/templates/fe/common/app-layout.tsx.partial +1 -1
  158. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +2 -1
  159. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignIn.ts +11 -3
  160. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +8 -2
  161. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +2 -1
  162. package/templates/fe/skeleton/apps/__app__/src/modules/toast/hooks.ts +1 -1
  163. package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +4 -1
  164. package/templates/fe/skeleton/apps/__app__/src/redux/hooks.ts +3 -2
  165. package/templates/fe/skeleton/apps/__app__/src/redux/index.ts +1 -0
  166. package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +12 -4
  167. package/templates/fe/skeleton/apps/__app__/src/redux/store.ts +8 -3
  168. package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +4 -3
  169. package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +1 -1
  170. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +2 -1
  171. package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +2 -1
  172. package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +2 -1
  173. package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +2 -1
  174. package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -1
  175. package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +4 -3
  176. package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +2 -2
  177. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +2 -1
  178. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +2 -1
  179. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +2 -1
  180. package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +2 -1
  181. package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -1
  182. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +19 -1
  183. package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +1 -1
  184. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +5 -1
  185. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +4 -3
  186. package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +2 -2
  187. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +2 -1
  188. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +2 -1
  189. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +2 -1
  190. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +3 -1
  191. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +5 -1
  192. package/templates/fe/table/graphql/useMutateSwr.ts.tpl +2 -1
  193. package/templates/fe/table/graphql/useQuerySwr.ts.tpl +2 -1
  194. package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
  195. package/templates/fe/transport/graphql/clients/clients/no-auth.ts +1 -1
  196. package/templates/fe/transport/graphql/clients/links/attach-access-token.ts +1 -1
  197. package/templates/fe/transport/graphql/clients/links/error.ts +1 -1
  198. package/templates/fe/transport/graphql/clients/links/http.ts +1 -1
  199. package/templates/fe/transport/graphql/clients/links/locale.ts +1 -1
  200. package/templates/fe/transport/graphql/clients/links/refresh-token.ts +1 -1
  201. package/templates/fe/transport/graphql/clients/links/retry.ts +1 -1
  202. package/templates/fe/transport/graphql/clients/links/timeout.ts +1 -1
  203. package/templates/fe/units/page/index.tsx.tpl +3 -2
  204. package/templates/fe/units/slot/SlotView.tsx.tpl +1 -1
@@ -1,7 +1,8 @@
1
1
  import { hasLocale } from "next-intl"
2
2
  import { getMessages } from "next-intl/server"
3
3
  import { notFound } from "next/navigation"
4
- import type { ReactNode } from "react"
4
+ import type { Metadata } from "next"
5
+ import type { ReactElement, ReactNode } from "react"
5
6
  import { AppLayout, appLayoutMetadata } from "@/features/layouts/AppLayout"
6
7
  import { PRODUCT_TIME_ZONE, routing } from "@/modules/i18n"
7
8
  import { Providers } from "./providers"
@@ -14,10 +15,10 @@ type LayoutProps = {
14
15
  }
15
16
 
16
17
  /** The localized document title of this route segment. */
17
- export const generateMetadata = async (props: LayoutProps) => appLayoutMetadata((await props.params).locale)
18
+ export const generateMetadata = async (props: LayoutProps): Promise<Metadata> => appLayoutMetadata((await props.params).locale)
18
19
 
19
20
  /** The locale segment's document, providers and app chrome. */
20
- const Layout = async (props: LayoutProps) => {
21
+ const Layout = async (props: LayoutProps): Promise<ReactElement> => {
21
22
  const { locale } = await props.params
22
23
  if (!hasLocale(routing.locales, locale)) notFound()
23
24
  const messages = await getMessages()
@@ -1,6 +1,6 @@
1
1
  import type { Metadata } from "next"
2
2
  import { getTranslations } from "next-intl/server"
3
- import type { ReactNode } from "react"
3
+ import type { ReactElement, ReactNode } from "react"
4
4
  import { SiteShell } from "@/components/frames/SiteShell"
5
5
  import { siteUrl } from "@/modules/config"
6
6
  import { APP_ROUTES } from "@/modules/routes"
@@ -17,7 +17,7 @@ export const appLayoutMetadata = async (locale: string): Promise<Metadata> => {
17
17
  }
18
18
 
19
19
  /** The server-resolved app chrome around the routed body. */
20
- export const AppLayout = async (props: AppLayoutProps) => {
20
+ export const AppLayout = async (props: AppLayoutProps): Promise<ReactElement> => {
21
21
  const t = await getTranslations("app.shell")
22
22
  return (
23
23
  <html lang={props.locale}>
@@ -1,3 +1,4 @@
1
+ import type { ReactElement } from "react"
1
2
  import type { Metadata } from "next"
2
3
  import { getTranslations } from "next-intl/server"
3
4
  import { AppGuideOverlay } from "@/features/overlays/AppGuideOverlay"
@@ -10,7 +11,7 @@ export const appHomeMetadata = async (): Promise<Metadata> => {
10
11
  }
11
12
 
12
13
  /** The front door (the session is client-side, drawn by the blocks the page composes). */
13
- export const AppHomePage = async () => {
14
+ export const AppHomePage = async (): Promise<ReactElement> => {
14
15
  const t = await getTranslations("app.home")
15
16
  return (
16
17
  <>
@@ -1,8 +1,9 @@
1
+ import type { ReactElement } from "react"
1
2
  import { getTranslations } from "next-intl/server"
2
3
  import { LoadingNotice } from "@/components/composites/LoadingNotice"
3
4
 
4
5
  /** Shown while a route resolves. */
5
- export const AppLoadingPage = async () => {
6
+ export const AppLoadingPage = async (): Promise<ReactElement> => {
6
7
  const t = await getTranslations("app.loading")
7
8
  return <LoadingNotice label={t("message")} />
8
9
  }
@@ -1,9 +1,10 @@
1
+ import type { ReactElement } from "react"
1
2
  import { getTranslations } from "next-intl/server"
2
3
  import { NotFoundNotice } from "@/components/composites/NotFoundNotice"
3
4
  import { APP_ROUTES } from "@/modules/routes"
4
5
 
5
6
  /** Shown when a route does not exist. */
6
- export const AppNotFoundPage = async () => {
7
+ export const AppNotFoundPage = async (): Promise<ReactElement> => {
7
8
  const t = await getTranslations("app.notFound")
8
9
  return <NotFoundNotice title={t("title")} homeLabel={t("home")} homeHref={APP_ROUTES.home} />
9
10
  }
@@ -21,7 +21,7 @@ const LOCALE_HEADER = "x-next-intl-locale"
21
21
  * The request config next-intl's plugin loads. The locale is the one the middleware negotiated: this module is reachable from the proxy
22
22
  * through the navigation, and `next/root-params` can only be imported inside the app directory.
23
23
  */
24
- export default getRequestConfig(async () => {
24
+ const requestConfig: ReturnType<typeof getRequestConfig> = getRequestConfig(async () => {
25
25
  const requested = (await headers()).get(LOCALE_HEADER)
26
26
  const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale
27
27
  return {
@@ -31,3 +31,5 @@ export default getRequestConfig(async () => {
31
31
  getMessageFallback: ({ namespace, key }) => `web:${namespace ?? ""}.${key}`,
32
32
  }
33
33
  })
34
+
35
+ export default requestConfig
@@ -7,7 +7,11 @@ export const DEFAULT_LOCALE = "vi" as const
7
7
  export const PRODUCT_TIME_ZONE = "Asia/Ho_Chi_Minh"
8
8
 
9
9
  /** The one locale-routing table of the web app. */
10
- export const routing = {
10
+ export const routing: {
11
+ readonly locales: typeof LOCALES
12
+ readonly defaultLocale: typeof DEFAULT_LOCALE
13
+ readonly localePrefix: "as-needed"
14
+ } = {
11
15
  locales: LOCALES,
12
16
  defaultLocale: DEFAULT_LOCALE,
13
17
  localePrefix: "as-needed" as const,
@@ -1,5 +1,6 @@
1
1
  import { useSWRConfig } from "swr"
2
2
  import useSWRMutation from "swr/mutation"
3
+ import type { SWRMutationResponse } from "swr/mutation"
3
4
  import type { GraphQLResponse } from "@/modules/api"
4
5
  import { mutateCreate{{Singular}} } from "@/modules/api/graphql/mutations/mutation-create-{{singular}}"
5
6
  import type { MutateCreate{{Singular}}Data } from "@/modules/api/graphql/mutations/types/create-{{singular}}"
@@ -12,7 +13,7 @@ export const MUTATE_CREATE_{{singularUpper}}_SWR_KEY = "MUTATE_CREATE_{{singular
12
13
  * Creates one {{singular}} row; answers the GraphQLResponse so the caller reports it through modules/toast
13
14
  * (useGraphQLWithToast). A success primes the {{singular}} query of the new row with that response.
14
15
  */
15
- export const useMutateCreate{{Singular}}Swr = () => {
16
+ export const useMutateCreate{{Singular}}Swr = (): SWRMutationResponse<GraphQLResponse<MutateCreate{{Singular}}Data>, Error, string, never> => {
16
17
  const { mutate } = useSWRConfig()
17
18
  return useSWRMutation(MUTATE_CREATE_{{singularUpper}}_SWR_KEY, async () => {
18
19
  const response: GraphQLResponse<MutateCreate{{Singular}}Data> = await mutateCreate{{Singular}}({ request: {} })
@@ -1,4 +1,5 @@
1
1
  import useSWR from "swr"
2
+ import type { SWRResponse } from "swr"
2
3
  import type { GraphQLResponse } from "@/modules/api"
3
4
  import { query{{Singular}} } from "@/modules/api/graphql/queries/query-{{singular}}"
4
5
  import type { Query{{Singular}}Data } from "@/modules/api/graphql/queries/types/{{singular}}"
@@ -7,7 +8,7 @@ import type { Query{{Singular}}Data } from "@/modules/api/graphql/queries/types/
7
8
  export const QUERY_{{singularUpper}}_SWR_KEY = "QUERY_{{singularUpper}}_SWR"
8
9
 
9
10
  /** One {{singular}} row of the signed-in reader by id, as its GraphQLResponse; no id, no request. */
10
- export const useQuery{{Singular}}Swr = (id: string | undefined) =>
11
+ export const useQuery{{Singular}}Swr = (id: string | undefined): SWRResponse<GraphQLResponse<Query{{Singular}}Data>, Error> =>
11
12
  useSWR(
12
13
  id === undefined ? null : ([QUERY_{{singularUpper}}_SWR_KEY, id] as const),
13
14
  ([, rowId]): Promise<GraphQLResponse<Query{{Singular}}Data>> => query{{Singular}}({ request: { id: rowId } }),
@@ -23,7 +23,7 @@ export interface CreateAuthApolloClientOptions {
23
23
  * retry (transient network failures) -> error (report) -> timeout -> refresh (one token refresh and replay on
24
24
  * UNAUTHENTICATED) -> locale (`Accept-Language`) -> access token (`Authorization: Bearer`, from modules/auth) -> http (terminal).
25
25
  */
26
- export const createAuthApolloClient = ({ signal, report }: CreateAuthApolloClientOptions) =>
26
+ export const createAuthApolloClient = ({ signal, report }: CreateAuthApolloClientOptions): ApolloClient =>
27
27
  new ApolloClient({
28
28
  link: ApolloLink.from([
29
29
  createRetryLink(),
@@ -19,7 +19,7 @@ export interface CreateNoAuthApolloClientOptions {
19
19
  * The anonymous Apollo client for public operations: retry -> error -> timeout -> locale -> http. It never attaches a
20
20
  * token, so a public operation stays public even for a signed-in reader.
21
21
  */
22
- export const createNoAuthApolloClient = ({ signal, report }: CreateNoAuthApolloClientOptions) =>
22
+ export const createNoAuthApolloClient = ({ signal, report }: CreateNoAuthApolloClientOptions): ApolloClient =>
23
23
  new ApolloClient({
24
24
  link: ApolloLink.from([
25
25
  createRetryLink(),
@@ -2,7 +2,7 @@ import { SetContextLink } from "@apollo/client/link/context"
2
2
  import { contextHeaders } from "./context-headers"
3
3
 
4
4
  /** Attaches `Authorization: Bearer <access token>` from modules/auth when a session exists; an anonymous call goes out without it. */
5
- export const createAttachAccessTokenLink = (accessToken: () => Promise<string | undefined>) =>
5
+ export const createAttachAccessTokenLink = (accessToken: () => Promise<string | undefined>): SetContextLink =>
6
6
  new SetContextLink(async (context) => {
7
7
  const token = await accessToken()
8
8
  if (token === undefined) return {}
@@ -15,7 +15,7 @@ const codeOf = (error: GraphQLErrorEntry): string =>
15
15
  * Reports what came back from downstream to `report` when the app passes one. The operation maps every error to its
16
16
  * GraphQLResponse, so this link never swallows or rewrites one.
17
17
  */
18
- export const createErrorLink = (report?: ErrorReporter) =>
18
+ export const createErrorLink = (report?: ErrorReporter): ErrorLink =>
19
19
  new ErrorLink(({ error, operation }) => {
20
20
  if (report === undefined) return
21
21
  if (CombinedGraphQLErrors.is(error)) {
@@ -8,7 +8,7 @@ export interface CreateHttpLinkParams {
8
8
  }
9
9
 
10
10
  /** The terminal link: POSTs the operation to the back end's GraphQL endpoint (modules/config). */
11
- export const createHttpLink = ({ withCredentials = false, signal }: CreateHttpLinkParams) =>
11
+ export const createHttpLink = ({ withCredentials = false, signal }: CreateHttpLinkParams): HttpLink =>
12
12
  new HttpLink({
13
13
  uri: graphqlUrl(),
14
14
  credentials: withCredentials ? "include" : "same-origin",
@@ -6,7 +6,7 @@ const documentLocale = (): string | undefined =>
6
6
  typeof document === "undefined" || document.documentElement.lang === "" ? undefined : document.documentElement.lang
7
7
 
8
8
  /** The one writer of `Accept-Language`: every operation declares the language the back end answers and localizes its errors in. */
9
- export const createAttachLocaleLink = (locale: () => string | undefined = documentLocale) =>
9
+ export const createAttachLocaleLink = (locale: () => string | undefined = documentLocale): SetContextLink =>
10
10
  new SetContextLink((context) => {
11
11
  const value = locale()
12
12
  if (value === undefined) return {}
@@ -15,7 +15,7 @@ const isUnauthenticated = (error: unknown): boolean =>
15
15
  * On an UNAUTHENTICATED answer, asks modules/auth for a fresh access token once and replays the operation with it. When no
16
16
  * token comes back (the session ended) the original error travels on.
17
17
  */
18
- export const createRefreshTokenLink = (refreshAccessToken: () => Promise<string | undefined>) =>
18
+ export const createRefreshTokenLink = (refreshAccessToken: () => Promise<string | undefined>): ErrorLink =>
19
19
  new ErrorLink(({ error, operation, forward }) => {
20
20
  if (!isUnauthenticated(error) || operation.getContext()[REFRESHED] === true) return
21
21
  return new Observable((observer) => {
@@ -6,7 +6,7 @@ import { RETRY_POLICY } from "./options"
6
6
  * Retries a transient network failure with jittered exponential backoff. A GraphQL error is an answer of the back end and
7
7
  * is never retried. First in the chain, so every other link sees the operation once per attempt.
8
8
  */
9
- export const createRetryLink = () =>
9
+ export const createRetryLink = (): RetryLink =>
10
10
  new RetryLink({
11
11
  delay: { initial: RETRY_POLICY.initialDelayMs, max: RETRY_POLICY.maxDelayMs, jitter: true },
12
12
  attempts: {
@@ -2,7 +2,7 @@ import { ApolloLink, Observable } from "@apollo/client"
2
2
  import { OPERATION_TIMEOUT_MS } from "./options"
3
3
 
4
4
  /** Fails the operation when no answer arrived within the timeout; the upstream links see the timeout as an error. */
5
- export const createTimeoutLink = (timeoutMs = OPERATION_TIMEOUT_MS) =>
5
+ export const createTimeoutLink = (timeoutMs: number = OPERATION_TIMEOUT_MS): ApolloLink =>
6
6
  new ApolloLink(
7
7
  (operation, forward) =>
8
8
  new Observable((observer) => {
@@ -1,4 +1,5 @@
1
1
  import type { Metadata } from "next"
2
+ import type { ReactElement } from "react"
2
3
  import { getTranslations } from "next-intl/server"
3
4
  import { APP_ROUTES } from "@/modules/routes"
4
5
  import { {{Name}}Base } from "./component"
@@ -23,7 +24,7 @@ const copyOf = async (isLoading: boolean): Promise<{{Name}}BaseProps["props"]> =
23
24
  }
24
25
 
25
26
  /** The {{route}} page: a server owner that resolves its copy and composes its blocks; it reads no data. */
26
- export const {{Name}} = async () => <{{Name}}Base props={await copyOf(false)} />
27
+ export const {{Name}} = async (): Promise<ReactElement> => <{{Name}}Base props={await copyOf(false)} />
27
28
 
28
29
  /** The {{route}} route's loading view: the same page drawn in skeleton mode, never a separate layout (STATE-8). */
29
- export const {{LoadingName}} = async () => <{{Name}}Base props={await copyOf(true)} />
30
+ export const {{LoadingName}} = async (): Promise<ReactElement> => <{{Name}}Base props={await copyOf(true)} />
@@ -15,7 +15,7 @@ type SlotViewProps<T> = {
15
15
  }
16
16
 
17
17
  /** The one status view (FE-ERROR-1): every region renders its loading, forbidden, error and empty status through here. */
18
- export const SlotView = <T,>(props: SlotViewProps<T>) => {
18
+ export const SlotView = <T,>(props: SlotViewProps<T>): ReactNode => {
19
19
  const { slot } = props
20
20
  if (slot.isLoading === true) return props.render(props.placeholder, true)
21
21
  if (slot.isForbidden === true) return props.forbidden === "hide" ? null : <EmptyNotice message={props.labels.forbidden} />