@starci/hfs 5.0.8 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +10 -1
- package/cache/import-graph.mjs +6 -61
- package/cache/lint-family.d.mts +10 -1
- package/cache/lint-family.mjs +12 -5
- package/codemod/directive-first.d.mts +48 -0
- package/codemod/directive-first.mjs +136 -0
- package/codemod/explicit-surface-edit.d.mts +14 -0
- package/codemod/explicit-surface-edit.mjs +57 -0
- package/codemod/explicit-surface-names.d.mts +30 -0
- package/codemod/explicit-surface-names.mjs +135 -0
- package/codemod/explicit-surface-render.d.mts +29 -0
- package/codemod/explicit-surface-render.mjs +197 -0
- package/codemod/explicit-surface.d.mts +50 -0
- package/codemod/explicit-surface.mjs +374 -0
- package/codemod/main.d.mts +7 -5
- package/codemod/main.mjs +37 -16
- package/codemod/source-tree.d.mts +8 -0
- package/codemod/source-tree.mjs +23 -0
- package/db/postgres.mjs +9 -1
- package/lint/eslint-list.cjs +29 -0
- package/lint/run.d.mts +19 -5
- package/lint/run.mjs +47 -14
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +31 -10
- package/runtime/knowledge/hfs/fe-stack.yaml +7 -7
- package/runtime/knowledge/hfs/fork-images.yaml +7 -0
- package/runtime/knowledge/hfs/rules.yaml +107 -8
- package/runtime/knowledge/hfs/slots.yaml +75 -24
- package/runtime/modules/kernel/failure-codes.yaml +62 -0
- package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
- package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
- package/runtime/scripts/hfs/architecture/test-world-files.d.mts +4 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +26 -7
- package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
- package/runtime/scripts/hfs/explicit-surface-rule.d.mts +58 -0
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +59 -0
- package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
- package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
- package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
- package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
- package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
- package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
- package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
- package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
- package/runtime/scripts/hfs/secret.mjs +10 -7
- package/runtime/scripts/hfs/slots.mjs +1 -1
- package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
- package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
- package/runtime/scripts/lib/declaration-surface.d.mts +35 -0
- package/runtime/scripts/lib/declaration-surface.mjs +112 -0
- package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
- package/runtime/scripts/lib/swarm-stack.mjs +2 -2
- package/scaffold/add-module.d.mts +16 -0
- package/scaffold/add-module.mjs +232 -0
- package/scaffold/add-queue-lite.mjs +49 -0
- package/scaffold/add.mjs +5 -0
- package/scaffold/app.mjs +7 -7
- package/scaffold/module-registry.d.mts +19 -0
- package/scaffold/module-registry.mjs +84 -0
- package/src/main.mjs +23 -2
- package/stack/cli.mjs +6 -1
- package/stack/context.d.mts +15 -0
- package/stack/context.mjs +1 -0
- package/stack/local-cli.d.mts +25 -0
- package/stack/local-cli.mjs +89 -0
- package/stack/local-config.d.mts +17 -0
- package/stack/local-config.mjs +37 -0
- package/stack/local-custody.d.mts +24 -0
- package/stack/local-custody.mjs +109 -0
- package/stack/local-doctor.d.mts +6 -0
- package/stack/local-doctor.mjs +49 -0
- package/stack/local-down.d.mts +8 -0
- package/stack/local-down.mjs +20 -0
- package/stack/local-host.d.mts +42 -0
- package/stack/local-host.mjs +163 -0
- package/stack/local-logs.d.mts +8 -0
- package/stack/local-logs.mjs +32 -0
- package/stack/local-ports.d.mts +7 -0
- package/stack/local-ports.mjs +39 -0
- package/stack/local-prepare.d.mts +22 -0
- package/stack/local-prepare.mjs +62 -0
- package/stack/local-run.d.mts +71 -0
- package/stack/local-run.mjs +200 -0
- package/stack/local-state.d.mts +35 -0
- package/stack/local-state.mjs +29 -0
- package/stack/local-status.d.mts +6 -0
- package/stack/local-status.mjs +36 -0
- package/stack/local-up.d.mts +11 -0
- package/stack/local-up.mjs +166 -0
- package/stack/local-verify.d.mts +6 -0
- package/stack/local-verify.mjs +21 -0
- package/sync/index.d.mts +13 -12
- package/sync/index.mjs +83 -28
- package/templates/app/module/jest.config.js.tpl +8 -0
- package/templates/app/module/src/index.ts.tpl +2 -0
- package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
- package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
- package/templates/app/module/src/manifest/index.ts.tpl +1 -0
- package/templates/app/module/src/public/index.ts.tpl +2 -0
- package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
- package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
- package/templates/app/module/src/template/index.ts.tpl +1 -0
- package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
- package/templates/app/module/src/workbench/index.ts.tpl +2 -0
- package/templates/app/package-scripts/package.json +1 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +76 -70
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +23 -13
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +25 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -26
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +1 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +2 -1
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +57 -14
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +13 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +13 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +90 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +28 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +145 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +95 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-identity.entity.ts +38 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-user.entity.ts +54 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +17 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +78 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +87 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +44 -0
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +2 -2
- package/templates/be/inbox/entities.index.ts.tpl +1 -1
- package/templates/be/inbox/module-definition.ts.tpl +8 -2
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +10 -2
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +8 -2
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -3
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +7 -6
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/job.processor.ts.tpl +1 -1
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +5 -4
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -3
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/consumer.ts.tpl +1 -1
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +9 -3
- package/templates/be/platform/graphql/graphql.module-definition.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +9 -3
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +2 -1
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +8 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/rate-limit.sql.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +8 -3
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +10 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +23 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +47 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
- package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/persistence/entities/index.ts +1 -1
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +18 -5
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +7 -1
- package/templates/be/table/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/table/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/table/entities.index.ts.tpl +1 -1
- package/templates/be/table/module-definition.ts.tpl +8 -3
- package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
- package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -3
- package/templates/be/test-world/world/test-world.config.ts.tpl +8 -1
- package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
- package/templates/fe/common/app-layout.tsx.partial +1 -1
- package/templates/fe/common/site-shell.tsx.partial +3 -3
- package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignIn.ts +11 -3
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +8 -2
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/hooks.ts +1 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +18 -12
- package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/hooks.ts +3 -2
- package/templates/fe/skeleton/apps/__app__/src/redux/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +12 -4
- package/templates/fe/skeleton/apps/__app__/src/redux/store.ts +8 -3
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +1 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +20 -2
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +5 -1
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +3 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +5 -1
- package/templates/fe/table/graphql/useMutateSwr.ts.tpl +2 -1
- package/templates/fe/table/graphql/useQuerySwr.ts.tpl +2 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/no-auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/attach-access-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/error.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/http.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/refresh-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/retry.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/timeout.ts +1 -1
- package/templates/fe/units/page/index.tsx.tpl +3 -2
- package/templates/fe/units/slot/SlotView.tsx.tpl +1 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -37
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -36
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
8
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
9
9
|
schema: starci/hfs-slots@3
|
|
10
|
-
version: 3.
|
|
10
|
+
version: 3.16.0
|
|
11
11
|
|
|
12
12
|
versioning:
|
|
13
13
|
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 3).
|
|
@@ -117,6 +117,8 @@ versioning:
|
|
|
117
117
|
# liteManagedBy is managed only in lite; its effective lite.path still lists literal files.
|
|
118
118
|
# the managed file list lives here and nowhere in code. A difference is HFS_MANAGED_FILE_DRIFT (a
|
|
119
119
|
# back end's tsconfig.json: HFS_TS_STRICT when a flag or an option was added).
|
|
120
|
+
# managedOnly true: the slot only lists files sync renders (needs managedBy); the path is judged by the slot that owns it, so the rendered
|
|
121
|
+
# file keeps the rules and the tier of its folder (a sign-in service is a domain service)
|
|
120
122
|
# rules rule codes from knowledge/hfs/rules.yaml.
|
|
121
123
|
# editions the editions (below) the slot exists in; default all of them. A slot outside the declared edition owns
|
|
122
124
|
# no path - under lite, what full keeps absent is removed, and what lite forbids is a finding.
|
|
@@ -250,17 +252,20 @@ ruleParams:
|
|
|
250
252
|
"node:https": [platform/http]
|
|
251
253
|
http: [platform/http]
|
|
252
254
|
https: [platform/http]
|
|
253
|
-
"@aws-sdk/client-s3": [
|
|
254
|
-
"@aws-sdk/s3-request-presigner": [
|
|
255
|
-
|
|
255
|
+
"@aws-sdk/client-s3": [] # the S3 client is a dependency of @starci/object-storage, imported by no app file (R331)
|
|
256
|
+
"@aws-sdk/s3-request-presigner": []
|
|
257
|
+
"@starci/object-storage": [integrations/object-storage] # the ObjectStorage port: one S3 adapter for MinIO, S3 and Spaces (R331)
|
|
258
|
+
minio: [] # storage goes through @starci/object-storage
|
|
256
259
|
"@supabase": [] # no Supabase client in a back end
|
|
257
|
-
"@nestjs/passport": [
|
|
258
|
-
passport: [
|
|
259
|
-
passport-google-oauth20: [
|
|
260
|
-
passport-local: [
|
|
261
|
-
"@nestjs/jwt": [
|
|
262
|
-
jose: [
|
|
263
|
-
bcryptjs: [
|
|
260
|
+
"@nestjs/passport": [] # the identity libraries are @starci/auth's dependencies, never an app's (R328)
|
|
261
|
+
passport: []
|
|
262
|
+
passport-google-oauth20: []
|
|
263
|
+
passport-local: []
|
|
264
|
+
"@nestjs/jwt": []
|
|
265
|
+
jose: []
|
|
266
|
+
bcryptjs: []
|
|
267
|
+
"@starci/auth": [integrations/passport] # the lite sign-in package: its server entry is reached only through integrations/passport (R328)
|
|
268
|
+
"@starci/auth/testing": [integrations/passport] # its test doubles, for the glue's specs
|
|
264
269
|
bcrypt: []
|
|
265
270
|
cache-manager: [integrations/cache, integrations/redis]
|
|
266
271
|
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability
|
|
@@ -909,7 +914,7 @@ slots:
|
|
|
909
914
|
# The db service supplies the database; schedules are platform/jobs ticks (R335); production has no Compose or host cron.
|
|
910
915
|
lite:
|
|
911
916
|
requires: [application-stacks.yaml]
|
|
912
|
-
allows: [application-stacks.yaml, images/db/Dockerfile, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/infra/metadata.json",
|
|
917
|
+
allows: [application-stacks.yaml, images/db/Dockerfile, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,swarm}/**", "<env>/infra/metadata.json",
|
|
913
918
|
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.{enc.yaml,enc}", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
914
919
|
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
915
920
|
rules: [HFS_STACKS_SHAPE, HFS_STACKS_IN_SIDE, HFS_PLAINTEXT_SECRET, HFS_IDENTITY_CUSTODY]
|
|
@@ -2028,36 +2033,68 @@ slots:
|
|
|
2028
2033
|
editions: [lite]
|
|
2029
2034
|
exemplifiedBy:
|
|
2030
2035
|
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/passport/
|
|
2031
|
-
# The
|
|
2032
|
-
#
|
|
2033
|
-
#
|
|
2034
|
-
#
|
|
2036
|
+
# The glue of @starci/auth, the lite sign-in package (Google OAuth with state and PKCE, email and password, EdDSA access tokens with
|
|
2037
|
+
# their JWKS, rotating refresh sessions): the one importer of the package (ruleParams.be.infraOwners), which owns the identity
|
|
2038
|
+
# libraries (@nestjs/passport, passport, passport-google-oauth20, passport-local, @nestjs/jwt, jose, bcryptjs) that no app imports.
|
|
2039
|
+
# It parses the typed options, builds the verifier and names the injectors; domain/identity verifies a token through the kit and
|
|
2040
|
+
# builds the principal (contexts/code/be/api-auth.md).
|
|
2035
2041
|
path: "src/modules/integrations/passport/"
|
|
2036
2042
|
presence: required
|
|
2037
2043
|
tracked: tracked
|
|
2038
2044
|
tier: integrations
|
|
2039
2045
|
owner: true
|
|
2040
|
-
requires: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.
|
|
2041
|
-
allows: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.
|
|
2042
|
-
logic: [passport.
|
|
2046
|
+
requires: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.kit.ts, passport.registration.ts] # Secrets, no defaults (R44); options and named injectors (R83); the kit and the module over @starci/auth
|
|
2047
|
+
allows: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.kit.ts, passport.registration.ts, errors/]
|
|
2048
|
+
logic: [passport.kit.ts] # the verifier mapping over the package: measured at 100 per file
|
|
2043
2049
|
tests: unit-beside
|
|
2044
2050
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INFRA_OWNER, BE_AUTH_PASSPORT_OWNER, BE_AUTH_JWT_OWN_JWKS, BE_AUTH_REFRESH_COOKIE]
|
|
2045
2051
|
coverage: required
|
|
2046
2052
|
since: 3.9.0
|
|
2053
|
+
- id: be.identity.sign-in
|
|
2054
|
+
since: 3.16.0
|
|
2055
|
+
profiles: [be]
|
|
2056
|
+
editions: [lite]
|
|
2057
|
+
editionWhy: "lite signs people in with the package @starci/auth, so its sign-in glue is rendered, not written (owner ruling auth-package); full uses its identity provider."
|
|
2058
|
+
# The identity capability's sign-in glue over @starci/auth: the entities of the session, attempt and user tables, the stores the package asks for (sessions, accounts, rate counter), the authenticator and the service the doors call, with their specs. `starci app sync` renders them; a hand edit is drift.
|
|
2059
|
+
path: "{src/modules/domain/identity/account-directory.service.spec.ts,src/modules/domain/identity/account-directory.service.ts,src/modules/domain/identity/authenticator.service.spec.ts,src/modules/domain/identity/authenticator.service.ts,src/modules/domain/identity/persistence/entities/auth-identity.entity.ts,src/modules/domain/identity/persistence/entities/auth-user.entity.ts,src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts,src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts,src/modules/domain/identity/persistence/entities/identity-session.entity.ts,src/modules/domain/identity/persistence/entities/index.ts,src/modules/domain/identity/rate-counter.service.spec.ts,src/modules/domain/identity/rate-counter.service.ts,src/modules/domain/identity/session-store.service.spec.ts,src/modules/domain/identity/session-store.service.ts,src/modules/domain/identity/sign-in.service.spec.ts,src/modules/domain/identity/sign-in.service.ts,src/tests/fixtures/builders/identity.builder.ts}"
|
|
2060
|
+
presence: optional
|
|
2061
|
+
tracked: tracked
|
|
2062
|
+
tier: inherit
|
|
2063
|
+
tests: unit-beside
|
|
2064
|
+
coverage: none
|
|
2065
|
+
managedBy: auth-lite
|
|
2066
|
+
managedOnly: true # lists files sync renders; the slot owning the path judges them
|
|
2067
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
2068
|
+
- id: be.feature.auth
|
|
2069
|
+
since: 3.16.0
|
|
2070
|
+
profiles: [be]
|
|
2071
|
+
editions: [lite]
|
|
2072
|
+
editionWhy: "lite signs people in with the package @starci/auth, so its sign-in glue is rendered, not written (owner ruling auth-package); full uses its identity provider."
|
|
2073
|
+
# The auth feature: the REST doors under /auth (login, refresh, session, logout, Google start and callback, jwks) over IdentitySignInService, one command or query and handler per door.
|
|
2074
|
+
path: "{src/features/api/auth/application/auth.contracts.ts,src/features/api/auth/application/get-jwks.handler.ts,src/features/api/auth/application/get-jwks.query.ts,src/features/api/auth/application/google-begin.command.ts,src/features/api/auth/application/google-begin.handler.ts,src/features/api/auth/application/google-complete.command.ts,src/features/api/auth/application/google-complete.handler.ts,src/features/api/auth/application/login.command.ts,src/features/api/auth/application/login.handler.ts,src/features/api/auth/application/logout.command.ts,src/features/api/auth/application/logout.handler.ts,src/features/api/auth/application/refresh.command.ts,src/features/api/auth/application/refresh.handler.ts,src/features/api/auth/application/session.command.ts,src/features/api/auth/application/session.handler.ts,src/features/api/auth/auth.module-definition.ts,src/features/api/auth/auth.module.ts,src/features/api/auth/auth.options.ts,src/features/api/auth/index.ts,src/features/api/auth/transport/http/auth-http.module-definition.ts,src/features/api/auth/transport/http/auth-http.module.ts,src/features/api/auth/transport/http/auth-http.options.ts,src/features/api/auth/transport/http/auth.controller.ts,src/features/api/auth/transport/http/dto/google-start.request.ts,src/features/api/auth/transport/http/dto/login.request.ts}"
|
|
2075
|
+
presence: optional
|
|
2076
|
+
tracked: tracked
|
|
2077
|
+
tier: inherit
|
|
2078
|
+
tests: none
|
|
2079
|
+
coverage: none
|
|
2080
|
+
managedBy: auth-lite
|
|
2081
|
+
managedOnly: true # lists files sync renders; the slot owning the path judges them
|
|
2082
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
2047
2083
|
- id: be.integrations.object-storage
|
|
2048
2084
|
profiles: [be]
|
|
2049
2085
|
exemplifiedBy:
|
|
2050
2086
|
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/object-storage/
|
|
2051
|
-
# The
|
|
2052
|
-
# lite): the
|
|
2087
|
+
# The glue of @starci/object-storage, the owner of the S3 client (@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner) over
|
|
2088
|
+
# S3-compatible storage (MinIO in lite): this folder keeps the bucket, parses the settings and registers the module; no app
|
|
2089
|
+
# file imports an S3 SDK or minio, and no other module imports the package.
|
|
2053
2090
|
path: "src/modules/integrations/object-storage/"
|
|
2054
2091
|
presence: optional
|
|
2055
2092
|
litePresence: required
|
|
2056
2093
|
tracked: tracked
|
|
2057
2094
|
tier: integrations
|
|
2058
2095
|
owner: true
|
|
2059
|
-
requires: [index.ts, object-storage.config.ts, object-storage.options.ts, object-storage.decorators.ts, object-storage.
|
|
2060
|
-
allows: [index.ts, object-storage.
|
|
2096
|
+
requires: [index.ts, object-storage.config.ts, object-storage.options.ts, object-storage.decorators.ts, object-storage.module-definition.ts, object-storage.module.ts]
|
|
2097
|
+
allows: [index.ts, object-storage.config.ts, object-storage.options.ts, object-storage.decorators.ts, object-storage.module-definition.ts, object-storage.module.ts, errors/]
|
|
2061
2098
|
tests: unit-beside
|
|
2062
2099
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INFRA_OWNER, BE_STORAGE_OWNER]
|
|
2063
2100
|
coverage: required
|
|
@@ -2591,7 +2628,7 @@ slots:
|
|
|
2591
2628
|
profiles: [fe]
|
|
2592
2629
|
# The identity-provider client (fe-stack auth, FE-AUTH): the session, the access token the Apollo link attaches and the
|
|
2593
2630
|
# sign-in/sign-out calls. It talks only to the app's own back-end auth doors and holds no credential: the refresh token lives in
|
|
2594
|
-
# the back end's httpOnly cookie and the access token in memory (ruling:lite-self-hosted-postgres-passport-minio).
|
|
2631
|
+
# the back end's httpOnly cookie and the access token in the memory of the one @starci/auth client (ruling:lite-self-hosted-postgres-passport-minio).
|
|
2595
2632
|
path: "apps/<app>/src/modules/auth/"
|
|
2596
2633
|
presence: optional
|
|
2597
2634
|
tracked: tracked
|
|
@@ -2601,6 +2638,20 @@ slots:
|
|
|
2601
2638
|
tests: none
|
|
2602
2639
|
since: 3.0.0
|
|
2603
2640
|
rules: [FE_STACK_FORBIDDEN]
|
|
2641
|
+
- id: fe.modules.auth.client
|
|
2642
|
+
since: 3.16.0
|
|
2643
|
+
profiles: [fe]
|
|
2644
|
+
editions: [lite]
|
|
2645
|
+
editionWhy: "lite signs people in with the package @starci/auth, so its auth client is rendered, not written (owner ruling auth-package); full wires its identity provider."
|
|
2646
|
+
# The AuthClient every edition shares, implemented by one @starci/auth/client instance over the app's own back-end auth doors.
|
|
2647
|
+
path: "apps/web/src/modules/auth/client.ts"
|
|
2648
|
+
presence: optional
|
|
2649
|
+
tracked: tracked
|
|
2650
|
+
tier: inherit
|
|
2651
|
+
tests: none
|
|
2652
|
+
managedBy: auth-lite
|
|
2653
|
+
managedOnly: true # lists files sync renders; the slot owning the path judges them
|
|
2654
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
2604
2655
|
- id: fe.modules.toast
|
|
2605
2656
|
profiles: [fe]
|
|
2606
2657
|
# Mutation feedback (fe-stack toast, FE-TOAST): the HeroUI toast wrapper, showGraphQLToast over a GraphQLResponse and the
|
|
@@ -5347,6 +5347,36 @@ SEALED_FILE_IN_WORK:
|
|
|
5347
5347
|
|
|
5348
5348
|
# --- HFS rule catalog (knowledge/hfs/rules.yaml): one code per rule, from R01 up ---
|
|
5349
5349
|
|
|
5350
|
+
STACKS_LOCAL_CONFIG_INVALID:
|
|
5351
|
+
title: "The local block of a docker-compose environment is not coherent"
|
|
5352
|
+
title_vi: "Khối local của môi trường docker-compose không nhất quán"
|
|
5353
|
+
meaning_vi: "environments.<env>.local thiếu hoặc có trường lạ, cổng trùng, dịch vụ không có trong tệp compose, vai trò cơ sở dữ liệu dùng chung, hoặc thăm dò không nằm trên loopback."
|
|
5354
|
+
causes_vi:
|
|
5355
|
+
- "Vi phạm R372: khối local được kiểm bằng đúng normalizeLocal mà các động từ starci app stack up, down, doctor dùng."
|
|
5356
|
+
nextStep_vi: "Sửa đúng trường nêu trong thông báo (environments.<env>.local.<đường dẫn>), rồi chạy lại starci app check."
|
|
5357
|
+
owner: op-retry
|
|
5358
|
+
kind: check-finding
|
|
5359
|
+
|
|
5360
|
+
STACKS_DEV_UNDECLARED:
|
|
5361
|
+
title: "A lite app declares no runnable dev environment"
|
|
5362
|
+
title_vi: "Ứng dụng lite chưa khai báo môi trường dev chạy được"
|
|
5363
|
+
meaning_vi: "Không có environment nào có runtime docker-compose và khối local, nên starci app stack up --env dev không có gì để chạy."
|
|
5364
|
+
causes_vi:
|
|
5365
|
+
- "Vi phạm R372: môi trường dev vẫn là Swarm hoặc chưa được khai báo."
|
|
5366
|
+
nextStep_vi: "Khai báo environments.dev với runtime docker-compose, composeFiles và khối local; runbook của nó nêu starci app stack up --env dev."
|
|
5367
|
+
owner: op-retry
|
|
5368
|
+
kind: check-finding
|
|
5369
|
+
|
|
5370
|
+
STACKS_RUNBOOK_DOCKER_COMMAND:
|
|
5371
|
+
title: "A dev runbook carries a docker command or omits the one-command start"
|
|
5372
|
+
title_vi: "Runbook dev chứa lệnh docker hoặc không nêu lệnh khởi động duy nhất"
|
|
5373
|
+
meaning_vi: "README của môi trường docker-compose có dòng docker build, run, compose hay stack deploy, hoặc không nêu starci app stack up --env <env>."
|
|
5374
|
+
causes_vi:
|
|
5375
|
+
- "Vi phạm R373 và quy tắc cứng 8: Docker do harness chạy, tác tử không chạy tay."
|
|
5376
|
+
nextStep_vi: "Thay các lệnh docker bằng starci app stack up --env <env> --execute và các động từ down, status, logs của nó."
|
|
5377
|
+
owner: op-retry
|
|
5378
|
+
kind: check-finding
|
|
5379
|
+
|
|
5350
5380
|
STACKS_DEPLOY_CONFIG_INVALID:
|
|
5351
5381
|
title: "A deploy block of application-stacks.yaml is not coherent"
|
|
5352
5382
|
title_vi: "Khối deploy trong application-stacks.yaml không nhất quán"
|
|
@@ -5674,3 +5704,35 @@ BE_JOBS_SCHEDULER_OWNER:
|
|
|
5674
5704
|
nextStep_vi: "Khai tick vào JOB_TICKS của platform/jobs/job-ticks.contracts.ts (name, cron, timeZone UTC, queue, payload) và xoá lịch tự viết; giữ advisoryXactLock và job_tick_claim trong JobTickService."
|
|
5675
5705
|
owner: op-retry
|
|
5676
5706
|
kind: check-finding
|
|
5707
|
+
|
|
5708
|
+
STACKS_CODE_BIND_MOUNT:
|
|
5709
|
+
title: "A stack or compose file bind-mounts code into a service container"
|
|
5710
|
+
title_vi: "Tệp stack hoặc compose gắn (bind-mount) mã vào container của dịch vụ"
|
|
5711
|
+
meaning_vi: "Một dịch vụ gắn thư mục hoặc tệp mã, plugin, script hay đường dẫn ghi được của repository từ máy chủ vào container."
|
|
5712
|
+
causes_vi:
|
|
5713
|
+
- "Vi phạm R374: mã của dịch vụ phải nằm trong image dựng từ .starcistacks/images/<tên>/, không gắn lúc chạy."
|
|
5714
|
+
nextStep_vi: "Đưa mã vào Dockerfile của .starcistacks/images/<tên>/ (FROM image đã ghim digest, hoặc image fork của harness) và bỏ bind-mount; chỉ giữ volume dữ liệu, secret và tệp cấu hình thuần dữ liệu chỉ đọc."
|
|
5715
|
+
|
|
5716
|
+
STACKS_IMAGE_BASE_UNPINNED:
|
|
5717
|
+
title: "A service image folder has a FROM that is not digest-pinned"
|
|
5718
|
+
title_vi: "Thư mục image của dịch vụ có FROM chưa ghim digest"
|
|
5719
|
+
meaning_vi: "Dockerfile trong .starcistacks/images/<tên>/ dùng FROM theo tag, không theo name@sha256:..."
|
|
5720
|
+
causes_vi:
|
|
5721
|
+
- "Vi phạm R374: mọi image nền phải ghim digest."
|
|
5722
|
+
nextStep_vi: "Ghim FROM bằng name@sha256:<digest>."
|
|
5723
|
+
|
|
5724
|
+
STACKS_IMAGE_BASE_NOT_FORK:
|
|
5725
|
+
title: "A service image folder does not build on the harness fork image"
|
|
5726
|
+
title_vi: "Thư mục image của dịch vụ không dựng trên image fork của harness"
|
|
5727
|
+
meaning_vi: "Harness sở hữu image fork StarCi cho dịch vụ này (images/sources.json) nhưng giai đoạn cuối của Dockerfile không FROM image đó."
|
|
5728
|
+
causes_vi:
|
|
5729
|
+
- "Vi phạm R374: dịch vụ tùy biến phải dựng trên image fork do harness ghim."
|
|
5730
|
+
nextStep_vi: "Đặt FROM <repository fork>@sha256:<digest> ở giai đoạn cuối."
|
|
5731
|
+
|
|
5732
|
+
STACKS_IMAGE_ENV_DIVERGES:
|
|
5733
|
+
title: "A service with an image folder runs a third-party image in an environment"
|
|
5734
|
+
title_vi: "Dịch vụ có thư mục image chạy image bên thứ ba ở một môi trường"
|
|
5735
|
+
meaning_vi: "Dev và production phải cùng chạy một image dựng từ .starcistacks/images/<tên>/."
|
|
5736
|
+
causes_vi:
|
|
5737
|
+
- "Vi phạm R374: một môi trường dùng image upstream hoặc image nước ngoài thay vì image dựng từ thư mục."
|
|
5738
|
+
nextStep_vi: "Dùng build.context trỏ tới thư mục image (compose) hoặc tham chiếu image của dự án (stack)."
|
|
@@ -510,6 +510,12 @@ $defs:
|
|
|
510
510
|
runbook: {type: string, minLength: 1}
|
|
511
511
|
secrets: {type: array, items: {$ref: '#/$defs/secret'}}
|
|
512
512
|
deploy: {$ref: '#/$defs/deploy'}
|
|
513
|
+
local:
|
|
514
|
+
description: >-
|
|
515
|
+
The dev declaration `starci app stack up|down|doctor|prepare|status|logs|verify` runs for a docker-compose environment (PROJECT-STACKS-6):
|
|
516
|
+
ports, compose services, the environment's own database role, dev-only custody to generate once, setup and seed commands, native app
|
|
517
|
+
processes, readiness and verification probes. Names, ports and argv lists only, never a value. The authority is scripts/hfs/stack-local-config.mjs; rule R340 judges it.
|
|
518
|
+
type: object
|
|
513
519
|
ingress:
|
|
514
520
|
description: "The one selected ingress component and the hosts it routes, or external with the stack name when the ingress is a shared stack outside this one (then no component; rule R275 judges which of the two is declared)."
|
|
515
521
|
type: object
|
|
@@ -1,57 +1,17 @@
|
|
|
1
|
-
// identity.mjs - the back-end identity machine. Under lite the one token verifier is be.integrations.passport:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// In both editions every jwtVerify call holds the JWT policy (R260, jwtPolicyProblems).
|
|
1
|
+
// identity.mjs - the back-end identity machine. Under lite the one token verifier is @starci/auth, composed by be.integrations.passport:
|
|
2
|
+
// no app file calls jose's jwtVerify, decodes a token unverified or reads authorization claims from a request body (R329), and the
|
|
3
|
+
// passport integration imports @starci/auth/server. In both editions every jwtVerify call holds the JWT policy (R260, jwtPolicyProblems).
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import { machineKit } from './machine-ast.mjs';
|
|
7
|
-
import {
|
|
6
|
+
import { jwtPolicyProblems, reportAt, unwrap } from './jwt-ast.mjs';
|
|
8
7
|
import { accessName } from '../../lib/ts-ast.mjs';
|
|
9
8
|
const PASSPORT_SLOT = 'be.integrations.passport';
|
|
9
|
+
const PACKAGE_ENTRY = '@starci/auth/server';
|
|
10
10
|
const BE_JWT_VERIFIED = 'BE_AUTH_JWT_OWN_JWKS';
|
|
11
11
|
/** The rule ids the back-end identity machine reports. */
|
|
12
12
|
export const IDENTITY_RULE_IDS = Object.freeze(['BE_AUTH_JWT_OWN_JWKS', 'BE_JWT_ALG_PINNED']);
|
|
13
13
|
const JWT_MODULES = new Set(['jose', 'jsonwebtoken', 'jwt-decode']);
|
|
14
14
|
const CLAIM_NAMES = new Set(['role', 'roles', 'membership', 'memberships']);
|
|
15
|
-
const objectLiteral = (kit, checker, node) => {
|
|
16
|
-
const current = unwrap(kit.ts, node);
|
|
17
|
-
if (!current)
|
|
18
|
-
return null;
|
|
19
|
-
if (kit.ts.isObjectLiteralExpression(current))
|
|
20
|
-
return current;
|
|
21
|
-
if (!kit.ts.isIdentifier(current))
|
|
22
|
-
return null;
|
|
23
|
-
for (const declaration of kit.declarationsOf(checker, current)) {
|
|
24
|
-
if (kit.ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
25
|
-
const value = unwrap(kit.ts, declaration.initializer);
|
|
26
|
-
if (kit.ts.isObjectLiteralExpression(value))
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
};
|
|
32
|
-
const staticStrings = (kit, checker, node) => {
|
|
33
|
-
const current = unwrap(kit.ts, node);
|
|
34
|
-
if (kit.ts.isStringLiteralLike(current))
|
|
35
|
-
return [current.text];
|
|
36
|
-
if (kit.ts.isArrayLiteralExpression(current)) {
|
|
37
|
-
const literals = current.elements.filter((element) => kit.ts.isStringLiteralLike(element));
|
|
38
|
-
if (literals.length === current.elements.length)
|
|
39
|
-
return literals.map((element) => element.text);
|
|
40
|
-
}
|
|
41
|
-
const value = kit.stringValue(checker, current);
|
|
42
|
-
return value === null ? null : [value];
|
|
43
|
-
};
|
|
44
|
-
const jwksArgument = (kit, checker, node) => {
|
|
45
|
-
const current = unwrap(kit.ts, node);
|
|
46
|
-
if (!current)
|
|
47
|
-
return false;
|
|
48
|
-
if (kit.ts.isCallExpression(current))
|
|
49
|
-
return importedFrom(kit, checker, current.expression, new Set(['jose']), new Set(['createLocalJWKSet']));
|
|
50
|
-
if (!kit.ts.isIdentifier(current))
|
|
51
|
-
return false;
|
|
52
|
-
return kit.declarationsOf(checker, current).some((declaration) => kit.ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
53
|
-
&& jwksArgument(kit, checker, declaration.initializer));
|
|
54
|
-
};
|
|
55
15
|
const importedCall = (kit, checker, call) => {
|
|
56
16
|
const direct = kit.importBinding(checker, call.expression);
|
|
57
17
|
if (direct?.module === 'jwt-decode' && direct.name === 'default')
|
|
@@ -84,41 +44,16 @@ const fromRequestBody = (kit, checker, node, seen = new Set()) => {
|
|
|
84
44
|
return kit.declarationsOf(checker, current).some(declaration => kit.ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
85
45
|
&& fromRequestBody(kit, checker, declaration.initializer, seen));
|
|
86
46
|
};
|
|
87
|
-
function verifyJwtCall(kit, checker, file, node, counters, violations) {
|
|
88
|
-
counters.verifyCalls += 1;
|
|
89
|
-
if (file.slot !== PASSPORT_SLOT) {
|
|
90
|
-
reportAt(violations, kit, BE_JWT_VERIFIED, file, node, `jwtVerify is called outside ${PASSPORT_SLOT}; the integration is the one token verifier.`);
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
const options = objectLiteral(kit, checker, node.arguments[2]);
|
|
94
|
-
const property = (name) => options ? kit.propertyOf(options, name) : null;
|
|
95
|
-
const values = (name) => {
|
|
96
|
-
const entry = property(name);
|
|
97
|
-
return entry ? staticStrings(kit, checker, kit.valueOfProperty(entry)) : null;
|
|
98
|
-
};
|
|
99
|
-
const issuer = property('issuer');
|
|
100
|
-
const audience = values('audience');
|
|
101
|
-
const algorithms = values('algorithms');
|
|
102
|
-
// The audience is pinned by a literal or, as R260 accepts, by typed configuration (the skeleton's TokenOptions.audience).
|
|
103
|
-
const audienceEntry = property('audience');
|
|
104
|
-
const pinnedAudience = (audience !== null && audience.length > 0)
|
|
105
|
-
|| (audienceEntry != null && fromConfig(kit.ts, checker, kit.valueOfProperty(audienceEntry)));
|
|
106
|
-
const good = jwksArgument(kit, checker, node.arguments[1]) && issuer != null && pinnedAudience
|
|
107
|
-
&& algorithms !== null && algorithms.length > 0;
|
|
108
|
-
if (!good) {
|
|
109
|
-
reportAt(violations, kit, BE_JWT_VERIFIED, file, node, 'jwtVerify must check the app\'s own key set (createLocalJWKSet(...) over the keys integrations/passport signs with) and pin issuer, audience and a non-empty algorithms list; jwtVerify then enforces signature and exp.');
|
|
110
|
-
}
|
|
111
|
-
else
|
|
112
|
-
counters.verifiedCalls += 1;
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
47
|
function inspectJwtCall(kit, checker, file, node, counters, violations) {
|
|
116
48
|
const binding = importedCall(kit, checker, node);
|
|
117
49
|
if (binding && JWT_MODULES.has(binding.module) && ['decode', 'decodeJwt', 'jwtDecode'].includes(binding.name)) {
|
|
118
50
|
reportAt(violations, kit, BE_JWT_VERIFIED, file, node, `${binding.module}.${binding.name} decodes a token without proving its signature; verify it through integrations/passport.`, { module: binding.module, method: binding.name });
|
|
119
51
|
}
|
|
120
|
-
if (binding?.module === 'jose' && binding.name === 'jwtVerify')
|
|
121
|
-
|
|
52
|
+
if (binding?.module === 'jose' && binding.name === 'jwtVerify') {
|
|
53
|
+
counters.verifyCalls += 1;
|
|
54
|
+
reportAt(violations, kit, BE_JWT_VERIFIED, file, node, 'jwtVerify is called in the app; @starci/auth verifies the own access tokens, reached through ' + PASSPORT_SLOT + '.');
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
122
57
|
return false;
|
|
123
58
|
}
|
|
124
59
|
function inspectRequestClaims(kit, file, node, checker, violations) {
|
|
@@ -146,18 +81,18 @@ function checkOwnJwks(input) {
|
|
|
146
81
|
const violations = [];
|
|
147
82
|
const integrations = [...input.graph.files.values()].filter((file) => file.slot === PASSPORT_SLOT);
|
|
148
83
|
if (!integrations.length)
|
|
149
|
-
return { violations, coverage: { status: 'not-applicable', integrationFiles: 0,
|
|
150
|
-
const counters = { verifyCalls: 0
|
|
84
|
+
return { violations, coverage: { status: 'not-applicable', integrationFiles: 0, verifyCalls: 0 } };
|
|
85
|
+
const counters = { verifyCalls: 0 };
|
|
151
86
|
for (const file of input.graph.files.values()) {
|
|
152
87
|
const checker = kit.checkerOf(file.sourceFile);
|
|
153
88
|
kit.walk(file.sourceFile, (node) => inspectBackendNode(kit, checker, file, node, counters, violations));
|
|
154
89
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return { violations, coverage: { status: 'checked', integrationFiles: integrations.length, verifyCalls: counters.verifyCalls,
|
|
90
|
+
const composes = integrations.some((file) => file.sourceFile.statements.some((statement) => kit.ts.isImportDeclaration(statement)
|
|
91
|
+
&& kit.ts.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === PACKAGE_ENTRY));
|
|
92
|
+
const anchor = integrations.find((file) => path.posix.basename(file.rel) === 'index.ts') ?? integrations[0];
|
|
93
|
+
if (!composes && anchor)
|
|
94
|
+
reportAt(violations, kit, BE_JWT_VERIFIED, anchor, anchor.sourceFile, PASSPORT_SLOT + ' does not import ' + PACKAGE_ENTRY + ', the one verifier of the own access tokens.');
|
|
95
|
+
return { violations, coverage: { status: 'checked', integrationFiles: integrations.length, verifyCalls: counters.verifyCalls, composesPackage: composes } };
|
|
161
96
|
}
|
|
162
97
|
/** R260 BE_JWT_ALG_PINNED: every jose jwtVerify call pins a literal secure algorithm list and a configured issuer, and binds the principal by a configured audience or the authenticated role. */
|
|
163
98
|
export function checkJwtPolicy(input) {
|
|
@@ -18,9 +18,10 @@ interface WorldViolation {
|
|
|
18
18
|
* its own slot, be.tests.world.kit) plus files at its root whose role suffix is in ruleParams.be.suffixes
|
|
19
19
|
* (`stripe.client.ts`, `checkout.contracts.ts`, `test-world.config.ts`, ...). Every tracked file below the world root is
|
|
20
20
|
* matched by the slot's own entries through `allowsFile`.
|
|
21
|
-
* 2. The declaration. `test-world.config.ts` declares the world in
|
|
22
|
-
* `export const { useTestWorld, useSandbox } = defineTestWorld({ ... })
|
|
23
|
-
* `
|
|
21
|
+
* 2. The declaration. `test-world.config.ts` declares the world in a named form R89 allows: the destructured export
|
|
22
|
+
* `export const { useTestWorld, useSandbox } = defineTestWorld({ ... })`, or the typed form
|
|
23
|
+
* `const world: TestWorldApi = defineTestWorld({ ... })` whose `useTestWorld` and `useSandbox` are exported consts typed from
|
|
24
|
+
* `TestWorldApi` (a `const` initialized by a `defineTestWorld(<object literal>)` call). A config without it (a default export, or a declaration the machine cannot
|
|
24
25
|
* read) is refused: the world it declares could not be judged.
|
|
25
26
|
* 3. Fakes against the stack (owner refinement 2026-09-30). Every service the stack declares runs real in the world; a fake
|
|
26
27
|
* (a `fakes/<provider>/` folder, or a `fakes` entry of the declaration such as `smtp: smtpFake()`) is a network-edge fake
|
|
@@ -15,9 +15,10 @@ import { byCodeUnit } from '../../lib/list.mjs';
|
|
|
15
15
|
* its own slot, be.tests.world.kit) plus files at its root whose role suffix is in ruleParams.be.suffixes
|
|
16
16
|
* (`stripe.client.ts`, `checkout.contracts.ts`, `test-world.config.ts`, ...). Every tracked file below the world root is
|
|
17
17
|
* matched by the slot's own entries through `allowsFile`.
|
|
18
|
-
* 2. The declaration. `test-world.config.ts` declares the world in
|
|
19
|
-
* `export const { useTestWorld, useSandbox } = defineTestWorld({ ... })
|
|
20
|
-
* `
|
|
18
|
+
* 2. The declaration. `test-world.config.ts` declares the world in a named form R89 allows: the destructured export
|
|
19
|
+
* `export const { useTestWorld, useSandbox } = defineTestWorld({ ... })`, or the typed form
|
|
20
|
+
* `const world: TestWorldApi = defineTestWorld({ ... })` whose `useTestWorld` and `useSandbox` are exported consts typed from
|
|
21
|
+
* `TestWorldApi` (a `const` initialized by a `defineTestWorld(<object literal>)` call). A config without it (a default export, or a declaration the machine cannot
|
|
21
22
|
* read) is refused: the world it declares could not be judged.
|
|
22
23
|
* 3. Fakes against the stack (owner refinement 2026-09-30). Every service the stack declares runs real in the world; a fake
|
|
23
24
|
* (a `fakes/<provider>/` folder, or a `fakes` entry of the declaration such as `smtp: smtpFake()`) is a network-edge fake
|
|
@@ -38,6 +39,7 @@ const FAKES_DIRECTORY = 'fakes/';
|
|
|
38
39
|
const CONFIG_FILE = 'test-world.config.ts';
|
|
39
40
|
const DEFINE = 'defineTestWorld';
|
|
40
41
|
const NAMED_FORM = `export const { useTestWorld, useSandbox } = ${DEFINE}({ ... })`;
|
|
42
|
+
const TYPED_FORM = `const world: TestWorldApi = ${DEFINE}({ ... })` + ' with export const useTestWorld / useSandbox typed from it';
|
|
41
43
|
const defaultEnvironment = DEFAULT_ENVIRONMENT;
|
|
42
44
|
const KEBAB = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
43
45
|
const unwrap = (ts, node) => unwrapEach(ts, node, [ts.isParenthesizedExpression, ts.isAsExpression, ts.isSatisfiesExpression]);
|
|
@@ -52,21 +54,38 @@ function defineLiteral(ts, expression) {
|
|
|
52
54
|
const literal = first ? unwrap(ts, first) : null;
|
|
53
55
|
return literal && ts.isObjectLiteralExpression(literal) ? literal : null;
|
|
54
56
|
}
|
|
55
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The declaration of the world: the object literal of a top-level `const` initialized by `defineTestWorld({ ... })`, or null. The config
|
|
59
|
+
* exports `useTestWorld` and `useSandbox` either by destructuring that call (the original form) or, typed, from the world the call
|
|
60
|
+
* built: `const world: TestWorldApi = defineTestWorld({ ... })` with `export const useTestWorld: TestWorldApi["useTestWorld"] = world.useTestWorld`.
|
|
61
|
+
* Both are read; a world no exported name reaches is refused by the caller.
|
|
62
|
+
*/
|
|
56
63
|
function configLiteral(ts, sourceFile) {
|
|
57
64
|
for (const statement of sourceFile.statements) {
|
|
58
|
-
if (!ts.isVariableStatement(statement)
|
|
65
|
+
if (!ts.isVariableStatement(statement))
|
|
59
66
|
continue;
|
|
60
67
|
if (!hasAnyFlag(statement.declarationList.flags, ts.NodeFlags.Const))
|
|
61
68
|
continue;
|
|
62
69
|
for (const declaration of statement.declarationList.declarations) {
|
|
63
70
|
const literal = declaration.initializer ? defineLiteral(ts, declaration.initializer) : null;
|
|
64
|
-
if (literal)
|
|
71
|
+
if (literal && (isExported(ts, statement) || exportsWorld(ts, sourceFile)))
|
|
65
72
|
return literal;
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
return null;
|
|
69
76
|
}
|
|
77
|
+
/** Whether the file exports both `useTestWorld` and `useSandbox` as consts (the typed form re-exports them from the built world). */
|
|
78
|
+
function exportsWorld(ts, sourceFile) {
|
|
79
|
+
const exported = new Set();
|
|
80
|
+
for (const statement of sourceFile.statements) {
|
|
81
|
+
if (!ts.isVariableStatement(statement) || !isExported(ts, statement))
|
|
82
|
+
continue;
|
|
83
|
+
for (const declaration of statement.declarationList.declarations)
|
|
84
|
+
if (ts.isIdentifier(declaration.name))
|
|
85
|
+
exported.add(declaration.name.text);
|
|
86
|
+
}
|
|
87
|
+
return exported.has('useTestWorld') && exported.has('useSandbox');
|
|
88
|
+
}
|
|
70
89
|
/** `stack` as the environment it names: the last segment of `".starcistacks/<env>"`; null when absent or not a literal. */
|
|
71
90
|
function environmentOf(ts, literal) {
|
|
72
91
|
const node = literal ? propertyOf(ts, literal, 'stack') : null;
|
|
@@ -152,7 +171,7 @@ function worldDeclaration(graph, ts, worldRoot, violations) {
|
|
|
152
171
|
if (configFile && !literal) {
|
|
153
172
|
violations.push({
|
|
154
173
|
ruleId: RULE, path: configPath, line: 1, column: 1, slot: WORLD_SLOT,
|
|
155
|
-
message: `${configPath} declares no world the machine can read. Declare it in the
|
|
174
|
+
message: `${configPath} declares no world the machine can read. Declare it in the named form \`${NAMED_FORM}\` or the typed form \`${TYPED_FORM}\` (a const of a ${DEFINE} call with an object literal, imported from @starci/test-world); a default export is refused by R89 and is not read.`,
|
|
156
175
|
});
|
|
157
176
|
}
|
|
158
177
|
return { configPath, configFile, literal };
|
|
@@ -15,8 +15,7 @@ import type { ArchitectureViolation, CheckInput } from './hfs-graph.mjs';
|
|
|
15
15
|
* - no file outside the client folder and the stream reader folder (`fe.transport.stream`, `modules/api/sse/clients/`) references the global `fetch`: a call, `globalThis.fetch`, `window.fetch`, or a
|
|
16
16
|
* reference passed as a value (`useSWR(key, fetch)`, `const send = fetch`), all of which a spelling-based rule misses; with no
|
|
17
17
|
* client at all every such reference is a finding that says the repository has no client;
|
|
18
|
-
* - axios is imported only by the api owner (modules/api, the api package) for its rest calls
|
|
19
|
-
* (modules/auth/auth-door.ts) for the app's own auth doors; every other HTTP library is a
|
|
18
|
+
* - axios is imported only by the api owner (modules/api, the api package) for its rest calls; every other HTTP library is a
|
|
20
19
|
* second transport wherever it is imported, by static import, `import()` or `require()`;
|
|
21
20
|
* - an `ApolloClient` is constructed only inside the client folder;
|
|
22
21
|
* - every GraphQL operation (`graphql/queries/query-*.ts`, `graphql/mutations/mutation-*.ts`, `graphql/subscriptions/subscription-*.ts`) imports the client (or the api
|
|
@@ -17,8 +17,7 @@ import { propertyValue } from './function-spec-providers.mjs';
|
|
|
17
17
|
* - no file outside the client folder and the stream reader folder (`fe.transport.stream`, `modules/api/sse/clients/`) references the global `fetch`: a call, `globalThis.fetch`, `window.fetch`, or a
|
|
18
18
|
* reference passed as a value (`useSWR(key, fetch)`, `const send = fetch`), all of which a spelling-based rule misses; with no
|
|
19
19
|
* client at all every such reference is a finding that says the repository has no client;
|
|
20
|
-
* - axios is imported only by the api owner (modules/api, the api package) for its rest calls
|
|
21
|
-
* (modules/auth/auth-door.ts) for the app's own auth doors; every other HTTP library is a
|
|
20
|
+
* - axios is imported only by the api owner (modules/api, the api package) for its rest calls; every other HTTP library is a
|
|
22
21
|
* second transport wherever it is imported, by static import, `import()` or `require()`;
|
|
23
22
|
* - an `ApolloClient` is constructed only inside the client folder;
|
|
24
23
|
* - every GraphQL operation (`graphql/queries/query-*.ts`, `graphql/mutations/mutation-*.ts`, `graphql/subscriptions/subscription-*.ts`) imports the client (or the api
|
|
@@ -105,17 +104,15 @@ function isApolloClientConstruction(ts, kit, checker, node) {
|
|
|
105
104
|
const binding = kit.importBinding(checker, node.expression);
|
|
106
105
|
return Boolean(binding?.name === 'ApolloClient' && binding.module.startsWith('@apollo/client'));
|
|
107
106
|
}
|
|
108
|
-
/** The one auth door of the session issuer's client (`modules/auth/auth-door.ts`, slot fe.modules.auth): it calls the app's own auth doors with axios. */
|
|
109
|
-
const isAuthDoor = (file) => file.slot === 'fe.modules.auth' && path.posix.basename(file.rel) === 'auth-door.ts';
|
|
110
107
|
function inspectLibraryImport({ ts, file, node, transport, report }) {
|
|
111
108
|
const specifier = moduleSpecifierOf(ts, node);
|
|
112
109
|
if (!specifier || !ts.isStringLiteralLike(specifier))
|
|
113
110
|
return;
|
|
114
111
|
const library = specifier.text.split('/')[0];
|
|
115
|
-
if (!HTTP_LIBRARIES.has(library) || (library === REST_LIBRARY && (API_OWNER_SLOTS.has(file.slot)
|
|
112
|
+
if (!HTTP_LIBRARIES.has(library) || (library === REST_LIBRARY && (API_OWNER_SLOTS.has(file.slot))))
|
|
116
113
|
return;
|
|
117
114
|
const where = library === REST_LIBRARY
|
|
118
|
-
? 'axios belongs to the api owner (modules/api/rest, the api package)
|
|
115
|
+
? 'axios belongs to the api owner (modules/api/rest, the api package) only: call their functions'
|
|
119
116
|
: `the transport is Apollo GraphQL (${transport}) with axios for the rest calls of modules/api/rest`;
|
|
120
117
|
report(file, node, `${specifier.text} is a second HTTP transport in ${file.rel}; ${where}.`, { library: specifier.text });
|
|
121
118
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { TypeScript } from '../lib/ts-ast.mjs';
|
|
2
|
+
/** The part of an ESLint rule context the rule reads (typescript-eslint's context satisfies it). */
|
|
3
|
+
export interface SurfaceContext {
|
|
4
|
+
readonly filename: string;
|
|
5
|
+
readonly sourceCode: {
|
|
6
|
+
readonly text: string;
|
|
7
|
+
getLocFromIndex(index: number): {
|
|
8
|
+
line: number;
|
|
9
|
+
column: number;
|
|
10
|
+
};
|
|
11
|
+
readonly parserServices?: {
|
|
12
|
+
readonly program?: {
|
|
13
|
+
getCompilerOptions(): {
|
|
14
|
+
module?: number;
|
|
15
|
+
};
|
|
16
|
+
} | null;
|
|
17
|
+
} | undefined;
|
|
18
|
+
};
|
|
19
|
+
report(descriptor: {
|
|
20
|
+
loc: {
|
|
21
|
+
start: {
|
|
22
|
+
line: number;
|
|
23
|
+
column: number;
|
|
24
|
+
};
|
|
25
|
+
end: {
|
|
26
|
+
line: number;
|
|
27
|
+
column: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
messageId: string;
|
|
31
|
+
data: Record<string, string>;
|
|
32
|
+
}): void;
|
|
33
|
+
}
|
|
34
|
+
/** The rule module the factory returns: a plain ESLint rule with one message. */
|
|
35
|
+
export interface SurfaceRule {
|
|
36
|
+
meta: {
|
|
37
|
+
type: 'problem';
|
|
38
|
+
docs: {
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
schema: [];
|
|
42
|
+
messages: {
|
|
43
|
+
inferred: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
create(context: SurfaceContext): {
|
|
47
|
+
Program(): void;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The rule. `ts` is the canon's own TypeScript (its peer dependency); the side's `module` option comes from the typed program when the file is linted with one.
|
|
52
|
+
*
|
|
53
|
+
* @param input - `ts` is the compiler.
|
|
54
|
+
* @returns An ESLint rule reporting each export of a TypeScript source whose type is inferred.
|
|
55
|
+
*/
|
|
56
|
+
export declare const explicitSurfaceRule: ({ ts }: {
|
|
57
|
+
ts: TypeScript;
|
|
58
|
+
}) => SurfaceRule;
|