@starci/hfs 1.0.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 (74) hide show
  1. package/README.md +38 -0
  2. package/bin/hfs.mjs +100 -0
  3. package/package.json +28 -0
  4. package/runtime/engine/runtime-root.mjs +32 -0
  5. package/runtime/engine/yaml.mjs +161 -0
  6. package/runtime/knowledge/hfs/canon-pins.yaml +212 -0
  7. package/runtime/knowledge/hfs/slots.yaml +842 -0
  8. package/runtime/modules/kernel/failure-codes.yaml +169 -0
  9. package/runtime/scripts/lib/glob.mjs +23 -0
  10. package/runtime/scripts/lib/hfs-check.mjs +305 -0
  11. package/runtime/scripts/lib/hfs-slots.mjs +675 -0
  12. package/runtime/scripts/lib/path-key.mjs +15 -0
  13. package/sync/cli.mjs +15 -0
  14. package/sync/hygiene.mjs +92 -0
  15. package/sync/index.mjs +224 -0
  16. package/sync/skeleton.mjs +54 -0
  17. package/sync/sonar-key.mjs +45 -0
  18. package/templates/be/e2e.yml +21 -0
  19. package/templates/be/gitignore +2 -0
  20. package/templates/be/pre-commit +8 -0
  21. package/templates/be/skeleton/apps/__app__/src/__app__.composition.spec.ts +31 -0
  22. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +7 -0
  23. package/templates/be/skeleton/apps/__app__/src/app.module.ts +23 -0
  24. package/templates/be/skeleton/apps/__app__/src/main.ts +20 -0
  25. package/templates/be/skeleton/src/features/system-health/index.ts +1 -0
  26. package/templates/be/skeleton/src/features/system-health/system-health.module.ts +6 -0
  27. package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.spec.ts +13 -0
  28. package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.ts +18 -0
  29. package/templates/be/skeleton/src/features/system-health/transport/http/system-health-http.module.ts +10 -0
  30. package/templates/be/skeleton/src/modules/platform/config/env-source.spec.ts +36 -0
  31. package/templates/be/skeleton/src/modules/platform/config/env-source.ts +40 -0
  32. package/templates/be/skeleton/src/modules/platform/config/errors/config.error.ts +21 -0
  33. package/templates/be/skeleton/src/modules/platform/config/index.ts +5 -0
  34. package/templates/be/skeleton/src/modules/platform/config/server.config.spec.ts +19 -0
  35. package/templates/be/skeleton/src/modules/platform/config/server.config.ts +15 -0
  36. package/templates/be/skeleton/src/modules/platform/config/server.options.ts +8 -0
  37. package/templates/be/skeleton/src/modules/platform/errors/domain-error.spec.ts +15 -0
  38. package/templates/be/skeleton/src/modules/platform/errors/domain-error.ts +11 -0
  39. package/templates/be/skeleton/src/modules/platform/errors/error.filter.spec.ts +38 -0
  40. package/templates/be/skeleton/src/modules/platform/errors/error.filter.ts +24 -0
  41. package/templates/be/skeleton/src/modules/platform/errors/index.ts +2 -0
  42. package/templates/be/skeleton/src/modules/platform/logging/index.ts +5 -0
  43. package/templates/be/skeleton/src/modules/platform/logging/json-logger.spec.ts +33 -0
  44. package/templates/be/skeleton/src/modules/platform/logging/json-logger.ts +35 -0
  45. package/templates/be/skeleton/src/modules/platform/logging/log-id.ts +9 -0
  46. package/templates/be/skeleton/src/modules/platform/logging/logger.port.ts +19 -0
  47. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +11 -0
  48. package/templates/be/sonar-project.properties +10 -0
  49. package/templates/be/starciwork.gitignore +39 -0
  50. package/templates/common/ci.yml +50 -0
  51. package/templates/common/codecov.yml +13 -0
  52. package/templates/common/gitignore.base +33 -0
  53. package/templates/common/pre-push +5 -0
  54. package/templates/fe/e2e.yml +22 -0
  55. package/templates/fe/gitignore +3 -0
  56. package/templates/fe/pre-commit +7 -0
  57. package/templates/fe/skeleton/apps/__app__/next.config.ts +11 -0
  58. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/error.tsx +22 -0
  59. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +31 -0
  60. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/not-found.tsx +15 -0
  61. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +27 -0
  62. package/templates/fe/skeleton/apps/__app__/src/app/global-error.tsx +24 -0
  63. package/templates/fe/skeleton/apps/__app__/src/app/globals.css +1 -0
  64. package/templates/fe/skeleton/apps/__app__/src/app/health/live/route.spec.ts +10 -0
  65. package/templates/fe/skeleton/apps/__app__/src/app/health/live/route.ts +5 -0
  66. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +8 -0
  67. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +19 -0
  68. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages.spec.ts +27 -0
  69. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/navigation.ts +5 -0
  70. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +13 -0
  71. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.spec.ts +10 -0
  72. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +9 -0
  73. package/templates/fe/skeleton/apps/__app__/src/proxy.ts +10 -0
  74. package/templates/fe/sonar-project.properties +11 -0
@@ -0,0 +1,27 @@
1
+ import type { Metadata } from "next"
2
+ import { getTranslations, setRequestLocale } from "next-intl/server"
3
+
4
+ interface HomePageProps {
5
+ readonly params: Promise<{ readonly locale: string }>
6
+ }
7
+
8
+ /** The document title comes from the catalog of the requested locale. */
9
+ export const generateMetadata = async ({ params }: HomePageProps): Promise<Metadata> => {
10
+ const { locale } = await params
11
+ const t = await getTranslations({ locale, namespace: "home" })
12
+ return { title: t("title") }
13
+ }
14
+
15
+ /** The first page of the app; a server component, so no catalog is shipped for it. */
16
+ const HomePage = async ({ params }: HomePageProps) => {
17
+ const { locale } = await params
18
+ setRequestLocale(locale)
19
+ const t = await getTranslations("home")
20
+ return (
21
+ <main>
22
+ <h1>{t("title")}</h1>
23
+ </main>
24
+ )
25
+ }
26
+
27
+ export default HomePage
@@ -0,0 +1,24 @@
1
+ "use client"
2
+
3
+ import { DEFAULT_LOCALE } from "../modules/i18n/config"
4
+ import messages from "../modules/i18n/messages/vi.json"
5
+
6
+ interface GlobalErrorProps {
7
+ readonly reset: () => void
8
+ }
9
+
10
+ /** Last-resort boundary above the locale layout: it has no provider, so it reads the default catalog directly. */
11
+ const GlobalError = ({ reset }: GlobalErrorProps) => (
12
+ <html lang={DEFAULT_LOCALE}>
13
+ <body>
14
+ <main role="alert">
15
+ <h1>{messages.errors.global.title}</h1>
16
+ <button type="button" onClick={reset}>
17
+ {messages.errors.global.retry}
18
+ </button>
19
+ </main>
20
+ </body>
21
+ </html>
22
+ )
23
+
24
+ export default GlobalError
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,10 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { GET } from "./route"
3
+
4
+ describe("GET /health/live", () => {
5
+ it("answers the { status, info, error, details } shape from process-local state", async () => {
6
+ const response = GET()
7
+ expect(response.status).toBe(200)
8
+ await expect(response.json()).resolves.toEqual({ status: "ok", info: {}, error: {}, details: {} })
9
+ })
10
+ })
@@ -0,0 +1,5 @@
1
+ /** Liveness is answered per request, never prerendered at build time. */
2
+ export const dynamic = "force-dynamic"
3
+
4
+ /** Liveness probe: `{ status, info, error, details }` from process-local state; it never touches a dependency. */
5
+ export const GET = (): Response => Response.json({ status: "ok", info: {}, error: {}, details: {} })
@@ -0,0 +1,8 @@
1
+ /** The locales the {{app}} app serves; every catalog under `messages/` has the same keys. */
2
+ export const LOCALES = ["vi"] as const
3
+
4
+ /** A served locale. */
5
+ export type Locale = (typeof LOCALES)[number]
6
+
7
+ /** The locale served at the unprefixed path. */
8
+ export const DEFAULT_LOCALE: Locale = "vi"
@@ -0,0 +1,19 @@
1
+ {
2
+ "home": {
3
+ "title": "Trang chủ"
4
+ },
5
+ "notFound": {
6
+ "title": "Không tìm thấy trang",
7
+ "home": "Về trang chủ"
8
+ },
9
+ "errors": {
10
+ "page": {
11
+ "title": "Đã xảy ra lỗi",
12
+ "retry": "Thử lại"
13
+ },
14
+ "global": {
15
+ "title": "Đã xảy ra lỗi nghiêm trọng",
16
+ "retry": "Thử lại"
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import vi from "./messages/vi.json"
3
+
4
+ const leaves = (value: unknown, path = ""): string[] =>
5
+ typeof value === "object" && value !== null
6
+ ? Object.entries(value).flatMap(([key, child]) => leaves(child, path ? `${path}.${key}` : key))
7
+ : [path]
8
+
9
+ const valueAt = (path: string): unknown => path.split(".").reduce<unknown>((node, key) => (node as Record<string, unknown>)[key], vi)
10
+
11
+ describe("messages/vi.json", () => {
12
+ it("carries the strings the error, not-found and home routes render", () => {
13
+ expect(leaves(vi).sort()).toEqual([
14
+ "errors.global.retry",
15
+ "errors.global.title",
16
+ "errors.page.retry",
17
+ "errors.page.title",
18
+ "home.title",
19
+ "notFound.home",
20
+ "notFound.title",
21
+ ])
22
+ })
23
+
24
+ it("has no empty string", () => {
25
+ for (const path of leaves(vi)) expect(valueAt(path)).not.toBe("")
26
+ })
27
+ })
@@ -0,0 +1,5 @@
1
+ import { createNavigation } from "next-intl/navigation"
2
+ import { routing } from "./routing"
3
+
4
+ /** Locale-aware links and navigation; components import these instead of `next/link` and `next/navigation`. */
5
+ export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing)
@@ -0,0 +1,13 @@
1
+ import { hasLocale } from "next-intl"
2
+ import { getRequestConfig } from "next-intl/server"
3
+ import { routing } from "./routing"
4
+
5
+ /** Resolves the locale of a request and loads its catalog; an unknown locale falls back to the default. */
6
+ export default getRequestConfig(async ({ requestLocale }) => {
7
+ const requested = await requestLocale
8
+ const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale
9
+ return {
10
+ locale,
11
+ messages: (await import(`./messages/${locale}.json`)).default,
12
+ }
13
+ })
@@ -0,0 +1,10 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { routing } from "./routing"
3
+
4
+ describe("routing", () => {
5
+ it("serves vi by default, without a prefix", () => {
6
+ expect(routing.defaultLocale).toBe("vi")
7
+ expect(routing.localePrefix).toBe("as-needed")
8
+ expect(routing.locales).toContain("vi")
9
+ })
10
+ })
@@ -0,0 +1,9 @@
1
+ import { defineRouting } from "next-intl/routing"
2
+ import { DEFAULT_LOCALE, LOCALES } from "./config"
3
+
4
+ /** One routing table for the proxy, the navigation helpers and the request config: vi default, prefix only when not default. */
5
+ export const routing = defineRouting({
6
+ locales: LOCALES,
7
+ defaultLocale: DEFAULT_LOCALE,
8
+ localePrefix: "as-needed",
9
+ })
@@ -0,0 +1,10 @@
1
+ import createMiddleware from "next-intl/middleware"
2
+ import { routing } from "./modules/i18n/routing"
3
+
4
+ /** Negotiates the locale and redirects; the default locale (vi) is served without a prefix. */
5
+ export default createMiddleware(routing)
6
+
7
+ /** Everything except the API, the health probe, framework files and files with an extension. */
8
+ export const config = {
9
+ matcher: ["/((?!api|health|_next|_vercel|.*\\..*).*)"],
10
+ }
@@ -0,0 +1,11 @@
1
+ sonar.projectKey={{sonarKey}}
2
+ sonar.host.url=https://sonar.starci.org
3
+ sonar.sourceEncoding=UTF-8
4
+ sonar.sources=apps
5
+ sonar.tests=apps
6
+ sonar.exclusions={{sonarExclusions}}
7
+ sonar.test.inclusions=**/*.spec.ts,**/*.spec.tsx
8
+ sonar.coverage.exclusions={{sonarCoverageExclusions}}
9
+ sonar.javascript.lcov.reportPaths=coverage/lcov.info
10
+ sonar.typescript.tsconfigPaths={{tsconfigPaths}}
11
+ sonar.nodejs.maxspace=8192