@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
package/lint/run.d.mts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { type HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
2
|
-
import { type CanonDeclarations, type LintSideStats } from '../cache/lint-family.mjs';
|
|
2
|
+
import { type CanonDeclarations, type GraphSnapshot, type LintSideStats } from '../cache/lint-family.mjs';
|
|
3
3
|
import type { CheckSession } from '../cache/types.mjs';
|
|
4
4
|
type Dynamic = any;
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The project-graph findings of one canon, as its `projectGraphSnapshot` answers them: by side-relative posix path.
|
|
7
|
+
* The ESLint child builds this graph for the canon's graph rules, so it hands the snapshot out instead of the cache building the same graph again.
|
|
8
|
+
*/
|
|
9
|
+
export type GraphSnapshotParts = Readonly<Record<string, GraphSnapshot>>;
|
|
10
|
+
/** What a linter run answers: its json results, or why it could not complete (both when the run was partial), and the graph snapshots its child handed out, by canon name. */
|
|
6
11
|
export interface LinterRun {
|
|
7
12
|
results?: Dynamic[];
|
|
8
13
|
error?: string;
|
|
14
|
+
graph?: GraphSnapshotParts;
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
11
17
|
* What a linter run is asked for. `list` is the files an ESLint run lints (cwd-relative, posix) when they are named one by one: they go to the child
|
|
@@ -20,6 +26,12 @@ export interface LinterSpec {
|
|
|
20
26
|
bound?: string | null;
|
|
21
27
|
list?: readonly string[] | undefined;
|
|
22
28
|
files?: number | undefined;
|
|
29
|
+
graph?: readonly GraphCanon[] | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** An installed canon whose graph snapshot the ESLint child hands out: its package name and the entry file the child imports. */
|
|
32
|
+
export interface GraphCanon {
|
|
33
|
+
name: string;
|
|
34
|
+
entry: string;
|
|
23
35
|
}
|
|
24
36
|
/** The seam that runs a linter through the app's own install from cwd. */
|
|
25
37
|
export type LinterRunner = (spec: LinterSpec) => Promise<LinterRun>;
|
|
@@ -43,7 +55,7 @@ type RepoDeclaration = Dynamic;
|
|
|
43
55
|
export declare const ESLINT_HEAP_ENV = "STARCI_ESLINT_HEAP_MB";
|
|
44
56
|
export declare function eslintHeapArgs(env?: NodeJS.ProcessEnv): string[];
|
|
45
57
|
/** A linter's json results through the app's own install, run from `cwd` (a side folder): `{ results }` or `{ error }`. The child runs while the caller goes on. */
|
|
46
|
-
export declare function runLinter({ cwd, pkg, bin, args, bound, list, files }: LinterSpec, execute?: HeavyNodeRun): Promise<LinterRun>;
|
|
58
|
+
export declare function runLinter({ cwd, pkg, bin, args, bound, list, files, graph }: LinterSpec, execute?: HeavyNodeRun): Promise<LinterRun>;
|
|
47
59
|
/**
|
|
48
60
|
* The lint scope a flat config declares: `ignored(file)` for its global ignores (a block holding only `ignores` and `name`), which prune
|
|
49
61
|
* the walk, and `source(file)` for a file some block's `files` select while that block's own `ignores` do not (standard flat-config
|
|
@@ -66,7 +78,7 @@ export declare function installedLintConfig({ repoRoot, cwd, side }: {
|
|
|
66
78
|
side: string;
|
|
67
79
|
}): Promise<Dynamic[]>;
|
|
68
80
|
/** Execute each side's installed ESLint config once, whole or over a named list; the cached and the cacheless run share scope, coverage and tool-failure judgment. */
|
|
69
|
-
export declare function eslintRepository({ repoRoot, app, opts, changed, existing, run, scopes }: {
|
|
81
|
+
export declare function eslintRepository({ repoRoot, app, opts, changed, existing, run, scopes, handOutGraph }: {
|
|
70
82
|
repoRoot: string;
|
|
71
83
|
app?: RepoDeclaration;
|
|
72
84
|
opts?: {
|
|
@@ -77,6 +89,7 @@ export declare function eslintRepository({ repoRoot, app, opts, changed, existin
|
|
|
77
89
|
existing?: readonly string[];
|
|
78
90
|
run?: LinterRunner | undefined;
|
|
79
91
|
scopes?: Readonly<Record<string, readonly string[]>> | undefined;
|
|
92
|
+
handOutGraph?: boolean | undefined;
|
|
80
93
|
}): Promise<{
|
|
81
94
|
findings: LintFinding[];
|
|
82
95
|
errors: string[];
|
|
@@ -87,6 +100,7 @@ export declare function eslintRepository({ repoRoot, app, opts, changed, existin
|
|
|
87
100
|
skipped?: string;
|
|
88
101
|
}>;
|
|
89
102
|
};
|
|
103
|
+
graphs: Record<string, GraphSnapshotParts>;
|
|
90
104
|
}>;
|
|
91
105
|
/** What the cache reads of the canons a side lints with: the ESLint canon (which publishes the project-graph rules) and the grammar family of the fe side. */
|
|
92
106
|
export declare function canonDeclarations({ repoRoot, cwd, side }: {
|
|
@@ -101,7 +115,7 @@ export declare function canonDeclarations({ repoRoot, cwd, side }: {
|
|
|
101
115
|
export declare function gitBaseToken(repoRoot: string): string;
|
|
102
116
|
/** What the cached linters answer: the ESLint answer in the shape of eslintRepository's, the stylelint results, and each side's counts. */
|
|
103
117
|
export interface CachedLinters {
|
|
104
|
-
eslint: Awaited<ReturnType<typeof eslintRepository
|
|
118
|
+
eslint: Omit<Awaited<ReturnType<typeof eslintRepository>>, 'graphs'>;
|
|
105
119
|
stylelint: {
|
|
106
120
|
results: Dynamic[];
|
|
107
121
|
errors: string[];
|
package/lint/run.mjs
CHANGED
|
@@ -45,8 +45,23 @@ export function eslintHeapArgs(env = process.env) {
|
|
|
45
45
|
}
|
|
46
46
|
/** The ESLint list runner (eslint-list.cts, built beside this file). */
|
|
47
47
|
const LIST_RUNNER = fileURLToPath(new URL('./eslint-list.cjs', import.meta.url));
|
|
48
|
+
/** The snapshots an ESLint child wrote, by canon name; undefined when it wrote none or a file that is not a snapshot list (the cache then builds the graph itself). */
|
|
49
|
+
function readGraphSnapshots(file) {
|
|
50
|
+
try {
|
|
51
|
+
const written = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
52
|
+
const snapshots = written?.snapshots;
|
|
53
|
+
if (snapshots === null || typeof snapshots !== 'object' || Array.isArray(snapshots))
|
|
54
|
+
return undefined;
|
|
55
|
+
const sound = Object.values(snapshots).every((snapshot) => snapshot !== null && typeof snapshot === 'object' && snapshot.byFile !== null && typeof snapshot.byFile === 'object' && !Array.isArray(snapshot.byFile)
|
|
56
|
+
&& Object.values(snapshot.byFile).every((findings) => Array.isArray(findings)));
|
|
57
|
+
return sound ? snapshots : undefined;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
48
63
|
/** A linter's json results through the app's own install, run from `cwd` (a side folder): `{ results }` or `{ error }`. The child runs while the caller goes on. */
|
|
49
|
-
export async function runLinter({ cwd, pkg, bin, args, bound = null, list, files }, execute) {
|
|
64
|
+
export async function runLinter({ cwd, pkg, bin, args, bound = null, list, files, graph }, execute) {
|
|
50
65
|
let entry;
|
|
51
66
|
try {
|
|
52
67
|
const manifest = createRequire(path.join(cwd, 'package.json')).resolve(`${pkg}/package.json`);
|
|
@@ -57,18 +72,22 @@ export async function runLinter({ cwd, pkg, bin, args, bound = null, list, files
|
|
|
57
72
|
return { error: `${pkg} is not installed for ${cwd}` };
|
|
58
73
|
}
|
|
59
74
|
const heap = pkg === 'eslint' ? eslintHeapArgs() : [];
|
|
60
|
-
// A named list
|
|
61
|
-
|
|
75
|
+
// A named list, or a run that hands its project graph out, goes through the list runner: the job is a file it reads (ESLint lints all of it in one
|
|
76
|
+
// process, no command line holds a path), the whole side is the list `.`, and the graph snapshots come back in a file beside the job.
|
|
77
|
+
const listed = pkg === 'eslint' && (list !== undefined || graph !== undefined) ? makeTempDir('starci-eslint-') : null;
|
|
62
78
|
let childArgs = [entry, ...args];
|
|
63
|
-
|
|
79
|
+
const snapshotFile = listed === null || graph === undefined ? null : path.join(listed, 'graph.json');
|
|
80
|
+
if (listed !== null) {
|
|
64
81
|
const job = path.join(listed, 'job.json');
|
|
65
|
-
fs.writeFileSync(job, JSON.stringify({ files: list, fix: args.includes('--fix') }));
|
|
82
|
+
fs.writeFileSync(job, JSON.stringify({ files: list ?? ['.'], fix: args.includes('--fix'), ...(snapshotFile === null || graph === undefined ? {} : { graph: { out: snapshotFile, canons: graph } }) }));
|
|
66
83
|
childArgs = [LIST_RUNNER, job];
|
|
67
84
|
}
|
|
68
85
|
const count = list?.length ?? files;
|
|
69
86
|
let run;
|
|
87
|
+
let handed;
|
|
70
88
|
try {
|
|
71
89
|
run = completedNode(await runHeavyNode(execute, { kind: 'eslint', label: `app check ${pkg} ${path.basename(cwd)}`, args: [...heap, ...(bound ? linterBoundArgs() : []), ...childArgs], cwd, maxBuffer: 64 * 1024 * 1024, ...(count === undefined || pkg !== 'eslint' ? {} : { files: count }), ...(bound ? { env: { ...process.env, [BOUND_ENV]: bound } } : {}) }), pkg);
|
|
90
|
+
handed = snapshotFile === null ? undefined : readGraphSnapshots(snapshotFile);
|
|
72
91
|
}
|
|
73
92
|
finally {
|
|
74
93
|
if (listed !== null)
|
|
@@ -87,7 +106,7 @@ export async function runLinter({ cwd, pkg, bin, args, bound = null, list, files
|
|
|
87
106
|
return { error: `${pkg} produced a report that is not a result list` };
|
|
88
107
|
const completed = !run.error && !run.signal && [0, 1].includes(run.status)
|
|
89
108
|
&& (run.status === 0 || results.some((result) => result?.messages?.length || result?.warnings?.length));
|
|
90
|
-
return { results, ...(completed ? {} : { error: `${pkg} could not complete in ${path.basename(cwd)}/ (exit ${run.status}, signal ${run.signal ?? 'none'}): ${String(run.error?.message || run.stderr || run.stdout || '').trim().split('\n')[0]}` }) };
|
|
109
|
+
return { results, ...(handed === undefined || !completed ? {} : { graph: handed }), ...(completed ? {} : { error: `${pkg} could not complete in ${path.basename(cwd)}/ (exit ${run.status}, signal ${run.signal ?? 'none'}): ${String(run.error?.message || run.stderr || run.stdout || '').trim().split('\n')[0]}` }) };
|
|
91
110
|
}
|
|
92
111
|
const eslintFindings = (results, appRoot) => results.flatMap((result) => (result.messages ?? []).map((message) => ({
|
|
93
112
|
engine: 'eslint', rule: message.ruleId ?? 'eslint-error', code: CODE_PREFIX.exec(message.message ?? '')?.[1] ?? null, severity: message.severity === 1 ? 'warning' : 'error',
|
|
@@ -268,8 +287,18 @@ const spawnFailure = (error) => {
|
|
|
268
287
|
throw error;
|
|
269
288
|
return { error: `could not start eslint: ${String(error?.message ?? error)}` };
|
|
270
289
|
};
|
|
290
|
+
/** The installed canon of `side`, which publishes the project-graph rules, as the child imports it; undefined when it cannot be resolved (the cache then builds the graph itself). */
|
|
291
|
+
function graphCanonsOf(repoRoot, cwd, side) {
|
|
292
|
+
const name = `@starci/eslint-canon-${side}`;
|
|
293
|
+
try {
|
|
294
|
+
return [{ name, entry: ownCanonEntry({ repoRoot, cwd }, name) }];
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
271
300
|
/** Starts the ESLint child of every side that has work and returns one record per side: skipped, a scope error, or the running child. */
|
|
272
|
-
async function launchEslint({ app, repoRoot, opts, changed, existing, run = runLinter, scopes = {} }) {
|
|
301
|
+
async function launchEslint({ app, repoRoot, opts, changed, existing, run = runLinter, scopes = {}, handOutGraph = false }) {
|
|
273
302
|
const launches = [];
|
|
274
303
|
for (const side of app ? SIDES.filter((candidate) => opts.side === undefined || opts.side === candidate) : []) {
|
|
275
304
|
const sources = onSide(existing, side).filter((file) => /\.(?:[cm]?[jt]sx?)$/.test(file));
|
|
@@ -289,20 +318,22 @@ async function launchEslint({ app, repoRoot, opts, changed, existing, run = runL
|
|
|
289
318
|
continue;
|
|
290
319
|
}
|
|
291
320
|
const head = ['--format', 'json', ...(opts.fix ? ['--fix'] : [])];
|
|
321
|
+
// the child that builds the canon's project graph for its rules hands the snapshot out when the caller keeps the graph findings (the cache)
|
|
322
|
+
const graph = handOutGraph ? graphCanonsOf(repoRoot, cwd, side) : undefined;
|
|
292
323
|
// The files of a diff go to ONE child through a file (a diff of hundreds of long paths outgrows the Windows command line, and each extra
|
|
293
324
|
// child would build the whole TypeScript program again); the whole side is a single directory argument.
|
|
294
|
-
const launch = Promise.resolve().then(() => run(changed ? { cwd, pkg: 'eslint', bin: 'eslint', args: head, list: sources, bound: repoRoot } : { cwd, pkg: 'eslint', bin: 'eslint', args: [...head, '.'], files: expected.length, bound: repoRoot }));
|
|
325
|
+
const launch = Promise.resolve().then(() => run(changed ? { cwd, pkg: 'eslint', bin: 'eslint', args: head, list: sources, bound: repoRoot, ...(graph === undefined ? {} : { graph }) } : { cwd, pkg: 'eslint', bin: 'eslint', args: [...head, '.'], files: expected.length, bound: repoRoot, ...(graph === undefined ? {} : { graph }) }));
|
|
295
326
|
launches.push({ side, cwd, expected, run: launch.catch(spawnFailure) });
|
|
296
327
|
}
|
|
297
328
|
return launches;
|
|
298
329
|
}
|
|
299
330
|
/** Execute each side's installed ESLint config once, whole or over a named list; the cached and the cacheless run share scope, coverage and tool-failure judgment. */
|
|
300
|
-
export async function eslintRepository({ repoRoot, app = readRepoDeclaration(loadSlotManifest(), repoRoot), opts = {}, changed = null, existing = [], run, scopes }) {
|
|
301
|
-
const findings = [], errors = [], raw = [];
|
|
331
|
+
export async function eslintRepository({ repoRoot, app = readRepoDeclaration(loadSlotManifest(), repoRoot), opts = {}, changed = null, existing = [], run, scopes, handOutGraph }) {
|
|
332
|
+
const findings = [], errors = [], raw = [], graphs = {};
|
|
302
333
|
const engine = { sides: {} };
|
|
303
334
|
if (!app.sides)
|
|
304
335
|
throw new Error('ESLint runs at the app root, the folder of hfs.json');
|
|
305
|
-
const launches = await launchEslint({ app, repoRoot, opts, changed, existing, run, ...(scopes === undefined ? {} : { scopes }) });
|
|
336
|
+
const launches = await launchEslint({ app, repoRoot, opts, changed, existing, run, ...(scopes === undefined ? {} : { scopes }), ...(handOutGraph === true ? { handOutGraph } : {}) });
|
|
306
337
|
const settled = await Promise.allSettled(launches.map(launch => launch.run ?? null));
|
|
307
338
|
const rejected = settled.filter((run) => run.status === 'rejected');
|
|
308
339
|
const failed = rejected.find(run => isHostHeld(run.reason)) ?? rejected[0];
|
|
@@ -335,8 +366,10 @@ export async function eslintRepository({ repoRoot, app = readRepoDeclaration(loa
|
|
|
335
366
|
findings.push(...eslintFindings(linted.results, repoRoot));
|
|
336
367
|
}
|
|
337
368
|
engine.sides[side] = { files: linted.results?.length ?? 0 };
|
|
369
|
+
if (linted.graph !== undefined)
|
|
370
|
+
graphs[side] = linted.graph;
|
|
338
371
|
});
|
|
339
|
-
return { findings, errors, raw, engine };
|
|
372
|
+
return { findings, errors, raw, engine, graphs };
|
|
340
373
|
}
|
|
341
374
|
// ------------------------------------------------------------------------------------------------ the cached lint
|
|
342
375
|
/** A `crossFileInputs` export that has the shape the cache reads, or null (an installed canon without it, or with a broken one, is linted whole). */
|
|
@@ -441,8 +474,8 @@ export async function cachedLinters({ repoRoot, app, side: only, run, session })
|
|
|
441
474
|
canons, styleCanon: styled ? await styleDeclaration({ repoRoot, cwd }) : null, typescript: () => loadAppTypeScript(cwd), extraKey: '', gitToken, full, write: true,
|
|
442
475
|
eslint: async (files) => {
|
|
443
476
|
const listed = files === null ? null : files.map((file) => `${side}/${file}`);
|
|
444
|
-
const sub = await eslintRepository({ repoRoot, app, opts: { side }, changed: listed, existing: listed ?? [], run, scopes: { [side]: scope } });
|
|
445
|
-
return { results: sub.raw, errors: sub.errors };
|
|
477
|
+
const sub = await eslintRepository({ repoRoot, app, opts: { side }, changed: listed, existing: listed ?? [], run, scopes: { [side]: scope }, handOutGraph: true });
|
|
478
|
+
return { results: sub.raw, errors: sub.errors, ...(sub.graphs[side] === undefined ? {} : { graph: sub.graphs[side] }) };
|
|
446
479
|
},
|
|
447
480
|
stylelint: styled ? async (files) => {
|
|
448
481
|
const linted = await (run ?? runLinter)({ cwd, pkg: 'stylelint', bin: 'stylelint', args: [...(files ?? [STYLE_GLOB]), '--formatter', 'json'] });
|
package/package.json
CHANGED
|
@@ -30,36 +30,43 @@ pins:
|
|
|
30
30
|
side: fe
|
|
31
31
|
source: packages/event-stream/package.json
|
|
32
32
|
why: 'the one server-sent-events client of every front end (fe-stack event-stream, owner:event-stream-package): the transport client layer (modules/api/sse/clients) creates one instance for the app with the bearer token source and the stream operations follow on it; it carries Last-Event-ID resume, the capped doubling backoff, the heartbeat timeout, the single 401 renewal and the useSWRSubscription adapter, so no app keeps a hand-written reader.'
|
|
33
|
+
'@starci/next-data':
|
|
34
|
+
version: 0.1.0
|
|
35
|
+
group: starci
|
|
36
|
+
install: registry
|
|
37
|
+
side: fe
|
|
38
|
+
source: packages/next-data/package.json
|
|
39
|
+
why: 'the SWR data helpers of every front end (fe-stack data, owner:next-data-package): pollingRestart, the viewer and tenant key helpers, viewerSwrConfig and the viewer cache; each operation hook stays in the app and the package holds no component.'
|
|
33
40
|
'@starci/grammar':
|
|
34
|
-
version: 0.21.
|
|
41
|
+
version: 0.21.14
|
|
35
42
|
group: starci
|
|
36
43
|
install: registry
|
|
37
44
|
side: fe
|
|
38
45
|
source: packages/grammar/package.json
|
|
39
46
|
why: 'the grammar front ends read: the brand layer sets `--font-sans` and `--font-mono` and the grammar reads them; it carries the Input tel kind, the copyable and read-only Input, the IconButton disclosure props and count badge, ChatBubble, FillViewport, Textarea mentions and Enter-to-send, the leading PrimaryRailLayout rail, the plate-less Image, ListRow, the soft Alert, Progress steps, the floating primary IconButton, FlowGraph, the STATE-8 loading views and the bordered selectable TagGroup chips. The runtime source version wins for a @starci package.'
|
|
40
47
|
'@starci/eslint-canon-be':
|
|
41
|
-
version: 3.
|
|
48
|
+
version: 3.13.0
|
|
42
49
|
group: starci
|
|
43
50
|
install: registry
|
|
44
51
|
side: be
|
|
45
52
|
source: packages/eslint-canon-be/package.json
|
|
46
53
|
why: 'the back-end ESLint canon: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; `starciBeConfig({ hfs: loadHfs(import.meta.url) })` is the typed factory and the BE-CONVENTION laws are its rules.'
|
|
47
54
|
'@starci/eslint-canon-fe':
|
|
48
|
-
version: 9.
|
|
55
|
+
version: 9.11.0
|
|
49
56
|
group: starci
|
|
50
57
|
install: registry
|
|
51
58
|
side: fe
|
|
52
59
|
source: packages/eslint-canon-fe/package.json
|
|
53
60
|
why: 'the front-end ESLint canon: `loadHfs(import.meta.url)` of fe/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the fe side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; the front end has no tests (FE_NO_TESTS R97 in hfs) and no-vietnamese-in-source (R91) applies.'
|
|
54
61
|
'@starci/eslint-grammar-core':
|
|
55
|
-
version: 0.21.
|
|
62
|
+
version: 0.21.14
|
|
56
63
|
group: starci
|
|
57
64
|
install: registry
|
|
58
65
|
side: fe
|
|
59
66
|
source: packages/eslint-grammar-core/package.json
|
|
60
67
|
why: 'the StarCi Grammar component API lint, versioned with @starci/grammar; front ends compose this family package with the universal FE canon.'
|
|
61
68
|
'@starci/eslint-grammar-offset-pop':
|
|
62
|
-
version: 0.21.
|
|
69
|
+
version: 0.21.14
|
|
63
70
|
group: starci
|
|
64
71
|
install: registry
|
|
65
72
|
side: fe
|
|
@@ -85,14 +92,14 @@ pins:
|
|
|
85
92
|
side: both
|
|
86
93
|
source: packages/prettier-config/package.json
|
|
87
94
|
'@starci/jest-preset':
|
|
88
|
-
version: 2.6.
|
|
95
|
+
version: 2.6.1
|
|
89
96
|
group: starci
|
|
90
97
|
install: registry
|
|
91
98
|
side: be
|
|
92
99
|
source: packages/jest-preset/package.json
|
|
93
100
|
why: 'the Jest preset of a back end: the world runner runs up to min(--maxWorkers, slots) world files at once, each in its own process bound to one data slot of the test world (STARCI_TEST_WORLD_SLOT), and reads test-world state protocol 2 only: it pairs exactly with @starci/test-world, and any other state file is JEST_PRESET_WORLD_PAIR_MISMATCH. The unit run writes the lcov Sonar and Codecov import (services only); the integration, e2e and contract projects run every spec file in a worker process of its own (world-runner.cjs), so a process-global framework registry (@nestjs/graphql type metadata) never leaks between e2e files; it ships the unit kit (mockEntityManager, fakeTransaction, fakeQueryBuilder, queueQueryBuilders, fakeIds, FakeClock, Outcome matchers) and the recordingOutbox claim side.'
|
|
94
101
|
'@starci/test-world':
|
|
95
|
-
version: 1.3.
|
|
102
|
+
version: 1.3.4
|
|
96
103
|
group: starci
|
|
97
104
|
install: registry
|
|
98
105
|
side: be
|
|
@@ -105,8 +112,22 @@ pins:
|
|
|
105
112
|
side: be
|
|
106
113
|
source: packages/ai-router/package.json
|
|
107
114
|
why: 'the AI pool router of a back end: a NestJS module that routes OpenAI-compatible chat, stream and embeddings calls across keys, models and providers, with @starci/ai-router/testing for its specs; @starci/jest-preset compiles it as an ESM-only dependency.'
|
|
115
|
+
'@starci/auth':
|
|
116
|
+
version: 0.1.0
|
|
117
|
+
group: starci
|
|
118
|
+
install: registry
|
|
119
|
+
side: both
|
|
120
|
+
source: packages/auth/package.json
|
|
121
|
+
why: 'the lite sign-in: the NestJS module (EdDSA access tokens with a multi-key JWKS, passwords, Google OAuth with state and PKCE, rotating refresh sessions, the sign-in budget, the refresh cookie), the front-end client and its React bindings, the door contracts and test doubles; the one importer of the identity libraries, reached by a back end only through integrations/passport; @starci/jest-preset compiles it as an ESM-only dependency.'
|
|
122
|
+
'@starci/object-storage':
|
|
123
|
+
version: 0.1.0
|
|
124
|
+
group: starci
|
|
125
|
+
install: registry
|
|
126
|
+
side: be
|
|
127
|
+
source: packages/object-storage/package.json
|
|
128
|
+
why: 'object storage of a back end: a NestJS module and the ObjectStorage port over S3-compatible storage (presigned PUT and GET that sign the content type and size, head, get, put, list, delete, bucket setup, size and content-type limits, coded errors), with @starci/object-storage/testing for its specs; the only importer of @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner (R331).'
|
|
108
129
|
'@starci/hfs':
|
|
109
|
-
version: 5.0
|
|
130
|
+
version: 5.2.0
|
|
110
131
|
group: starci
|
|
111
132
|
install: registry
|
|
112
133
|
side: both
|
|
@@ -543,12 +564,12 @@ pins:
|
|
|
543
564
|
version: 3.1150.0
|
|
544
565
|
group: framework
|
|
545
566
|
side: be
|
|
546
|
-
why: 'the S3 transport
|
|
567
|
+
why: 'the S3 transport inside @starci/object-storage, the one ObjectStorage adapter of both editions (MinIO, S3, Spaces), which depends on it at exactly this version and which no app file imports; 3.1150.0 is the release of 2026-10-12, chosen over newer ones that had not aged two weeks.'
|
|
547
568
|
'@aws-sdk/s3-request-presigner':
|
|
548
569
|
version: 3.1150.0
|
|
549
570
|
group: framework
|
|
550
571
|
side: be
|
|
551
|
-
why: 'signs the presigned PUT and GET
|
|
572
|
+
why: 'signs the presigned PUT and GET inside @starci/object-storage locally, the same release as client-s3; no app file imports it.'
|
|
552
573
|
'@apollo/server':
|
|
553
574
|
version: 5.5.1
|
|
554
575
|
group: framework
|
|
@@ -37,17 +37,17 @@ concerns:
|
|
|
37
37
|
- id: rest
|
|
38
38
|
tier: core
|
|
39
39
|
packages: [axios]
|
|
40
|
-
home: [fe.modules.api
|
|
40
|
+
home: [fe.modules.api]
|
|
41
41
|
pattern: FE-API-REST
|
|
42
42
|
forbids: [ky, superagent, node-fetch, cross-fetch, isomorphic-fetch]
|
|
43
|
-
why: modules/api/rest/<area>/*.ts for uploads and presigned URLs,
|
|
43
|
+
why: modules/api/rest/<area>/*.ts for uploads and presigned URLs, only; GraphQL carries every other call.
|
|
44
44
|
- id: data
|
|
45
45
|
tier: core
|
|
46
|
-
packages: [swr]
|
|
46
|
+
packages: [swr, '@starci/next-data']
|
|
47
47
|
home: [fe.hooks.swr]
|
|
48
48
|
pattern: FE-DATA-SWR
|
|
49
49
|
forbids: ['@tanstack/react-query', react-query]
|
|
50
|
-
why: hooks/swr/api/{graphql,rest}/{queries,mutations}/use*Swr.ts wrap one operation each; components call hooks only.
|
|
50
|
+
why: hooks/swr/api/{graphql,rest}/{queries,mutations}/use*Swr.ts wrap one operation each; components call hooks only. The polling-restart middleware (pollingRestart), the key helpers, the viewer-keyed config and the viewer cache are @starci/next-data's (owner next-data-package); an app imports no Middleware or ScopedMutator type of swr and writes none of them.
|
|
51
51
|
- id: toast
|
|
52
52
|
tier: core
|
|
53
53
|
packages: ['@heroui/react']
|
|
@@ -96,11 +96,11 @@ concerns:
|
|
|
96
96
|
hand-written reader (read-event-stream.ts) and no second SSE library is installed.
|
|
97
97
|
- id: auth
|
|
98
98
|
tier: core
|
|
99
|
-
packages: []
|
|
99
|
+
packages: ['@starci/auth']
|
|
100
100
|
home: [fe.modules.auth]
|
|
101
101
|
pattern: FE-AUTH
|
|
102
|
-
forbids: ['@supabase/supabase-js', '@supabase/ssr', '@supabase/auth-js']
|
|
103
|
-
why: modules/auth is the session issuer's client (
|
|
102
|
+
forbids: ['@supabase/supabase-js', '@supabase/ssr', '@supabase/auth-js', jose, jwt-decode, next-auth, '@auth/core', keycloak-js, oidc-client-ts]
|
|
103
|
+
why: modules/auth is the session issuer's client (one @starci/auth client instance over the app's own back-end auth doors, the access token in its memory; full - the app's IdP client); it yields the session and the access token for the Apollo link and never reads data.
|
|
104
104
|
- id: i18n
|
|
105
105
|
tier: core
|
|
106
106
|
packages: [next-intl]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# The images the harness owns as StarCi forks (the harness repository's images/sources.json is their owner; stacks-service-images.spec.mts keeps this list
|
|
2
|
+
# equal to it). A service of an app that has an image folder named like a key here is built FROM that repository (R374 STACKS_IMAGE_BASE_NOT_FORK):
|
|
3
|
+
# the fork itself, or the harness runtime layer built on it (openclaw-runtime, images/README.md).
|
|
4
|
+
schema: starci/fork-images@1
|
|
5
|
+
images:
|
|
6
|
+
minio: ghcr.io/starci183/minio
|
|
7
|
+
openclaw: ghcr.io/starci183/openclaw-runtime
|
|
@@ -924,7 +924,7 @@ rules:
|
|
|
924
924
|
code: "BE_TEST_TOPOLOGY"
|
|
925
925
|
editions: [lite, full]
|
|
926
926
|
docs: [docs/quality-bar.md#r47-test-topology]
|
|
927
|
-
law: "One jest config, projects `unit`, `integration`, `e2e` and `contract`, live by folder, `diagnostics: false`; unit specs are `<name>.<role>.spec.ts` beside the file they test: required for the roles of `ruleParams.be.unitRoles` (`<name>.service.spec.ts` beside a `<name>.service.ts`, `<name>.cli.spec.ts` beside a cli command `<name>.cli.ts` of `src/features/cli`), optional for any other logic role (`ruleParams.be.logicRoles`) inside a slot whose `coverage` is required and beside a logic file such a slot declares (`logic`: `passport.tokens.spec.ts` beside `passport.tokens.ts`), except that the commit adding a measured logic file stages its `<name>.<role>.spec.ts` beside it (owner 2026-10-20, judged by `starci app check --staged` in the pre-commit hook), and nowhere else but beside a webhook or realtime door that has no service of its own (`<provider>.webhook.spec.ts`, `<channel>.gateway.spec.ts`, `<x>.subscription.spec.ts`) (every unit-tested subject has exactly one; any other `*.spec.ts` outside `be/src/tests/{world,integration,e2e,contract}` is a finding, including specs in apps), and the unit project collects coverage from the logic roles of the measured roots and the logic files their slots declare only (the scope derived from the slot manifest, R204) with a per-file threshold of 100 on lines, branches, functions and statements (`test` runs `--coverage`; `importHelpers` and `tslib`); `be/src/tests/world/` is the ONLY test infrastructure: `global-setup.ts` starts the app's own stack (`.starcistacks/<env>`, named by `stack` in `test-world.config.ts`, the declaration of the test-world library in
|
|
927
|
+
law: "One jest config, projects `unit`, `integration`, `e2e` and `contract`, live by folder, `diagnostics: false`; unit specs are `<name>.<role>.spec.ts` beside the file they test: required for the roles of `ruleParams.be.unitRoles` (`<name>.service.spec.ts` beside a `<name>.service.ts`, `<name>.cli.spec.ts` beside a cli command `<name>.cli.ts` of `src/features/cli`), optional for any other logic role (`ruleParams.be.logicRoles`) inside a slot whose `coverage` is required and beside a logic file such a slot declares (`logic`: `passport.tokens.spec.ts` beside `passport.tokens.ts`), except that the commit adding a measured logic file stages its `<name>.<role>.spec.ts` beside it (owner 2026-10-20, judged by `starci app check --staged` in the pre-commit hook), and nowhere else but beside a webhook or realtime door that has no service of its own (`<provider>.webhook.spec.ts`, `<channel>.gateway.spec.ts`, `<x>.subscription.spec.ts`) (every unit-tested subject has exactly one; any other `*.spec.ts` outside `be/src/tests/{world,integration,e2e,contract}` is a finding, including specs in apps), and the unit project collects coverage from the logic roles of the measured roots and the logic files their slots declare only (the scope derived from the slot manifest, R204) with a per-file threshold of 100 on lines, branches, functions and statements (`test` runs `--coverage`; `importHelpers` and `tslib`); `be/src/tests/world/` is the ONLY test infrastructure: `global-setup.ts` starts the app's own stack (`.starcistacks/<env>`, named by `stack` in `test-world.config.ts`, the declaration of the test-world library in a named form: `export const { useTestWorld, useSandbox } = defineTestWorld({...})`, or `const world: TestWorldApi = defineTestWorld({...})` with `useTestWorld` and `useSandbox` exported typed from it (a default export is not read): every service it declares runs REAL, behind toxiproxy, images and versions read from the stack at run time, never spelled in test source) or attaches to a warm one that answers, and runs the migrations once over the cli app's connections (the runner of the cli migrate command); `global-teardown.ts` stops only what its setup started; `use-test-world.ts` exports `useTestWorld({ apps } | { modules })` (`world.apps.<name>.api`, `world.db.<connection>`, `world.infra.<service>` with `latency(ms)`, `cut()` and `restore()` on the real service, `world.fake.<provider>`, `world.waitFor`) and `fakes/<provider>/` holds the network-edge fakes (`failNext`, `replayWebhook`, `delay`, payload fixtures) of external SaaS the team does not operate ONLY: a fake of a service the stack declares is refused, except stateless compute that needs special hardware or an external model (GPU inference, a self-hosted embedding model), whose `stacks` entry in `test-world.config.ts` declares `{ fakedBy: <fake>, reason }` with a non-empty `reason` (a stateful service, or one with a persistent volume, is never accepted); it alone owns containers, DataSource, migrations and `process.env`; nothing under `be/src/tests` overrides a provider or DI token; `be/src/tests/integration/<capability>/*.integration-spec.ts` (`{ modules }`, with the real peer apps its client calls beside them as `apps`; real infrastructure, no HTTP door of ours), `be/src/tests/e2e/<area>/*.e2e-spec.ts` (`{ apps }`) and `be/src/tests/contract/<provider>/*.contract-spec.ts` (provider sandboxes, skipped without sandbox config, run only by `test:contract`, never by `test` or `test:e2e`) agree folder with suffix; an integration or e2e spec passes `useTestWorld` one object literal with plain keys (no variable, spread or computed key) and calls it; the contract layer has no skip either: a contract spec writes `sandbox.describe(...)` from the test-world library's `useSandbox(...)`, and when the sandbox config is absent that describe registers one failing test naming the missing keys (the body never runs), and every `fakes/<provider>/` that serves payload fixtures (`payloads/*.json`) has a `contract/<provider>/*.contract-spec.ts` that references those fixtures and asserts `shapeOf(real)` equals `shapeOf(fixture)` (the helper `shapeOf` is named by `ruleParams.be.contractShape` of the slot manifest; fakes the test-world library provides are guarded by the library); no `.test.ts`, `int-spec` or `harness-spec`, no `live/` and no `be/src/tests/e2e/world`."
|
|
928
928
|
kinds: [check, codemod, lint]
|
|
929
929
|
gates: [land, ci]
|
|
930
930
|
failureCodes: ["BE_TEST_TOPOLOGY", "BE_CONTRACT_UNGUARDED", "BE_UNIT_SPEC_NOT_STAGED"]
|
|
@@ -4084,6 +4084,7 @@ rules:
|
|
|
4084
4084
|
enforcers:
|
|
4085
4085
|
- {kind: hfs, id: fe-stack, at: scripts/hfs/rules/fe-stack.mjs}
|
|
4086
4086
|
- {kind: eslint-fe, id: fe-stack-import}
|
|
4087
|
+
- {kind: eslint-fe, id: swr-extension-from-package}
|
|
4087
4088
|
proves:
|
|
4088
4089
|
- {path: packages/eslint/fe/frontend-boundary-coverage.spec.mjs, editions: [lite, full], codes: [FE_STACK_FORBIDDEN]}
|
|
4089
4090
|
- {path: packages/hfs/src/frontend-boundary-coverage.spec.mjs, editions: [lite, full], codes: [FE_STACK_FORBIDDEN]}
|
|
@@ -5696,7 +5697,7 @@ rules:
|
|
|
5696
5697
|
ruling: "A model call made straight from code uses LangChain with retrieved context, through the ai-router gateway (owner ruling be-ai-langchain-gateway): the LangChain clients point at the gateway's base URL with its virtual key."
|
|
5697
5698
|
- id: R328
|
|
5698
5699
|
code: "BE_AUTH_PASSPORT_OWNER"
|
|
5699
|
-
law: "The identity libraries (@nestjs/passport, passport, passport-google-oauth20, passport-local, @nestjs/jwt, jose and bcryptjs) are
|
|
5700
|
+
law: "The identity libraries (@nestjs/passport, passport, passport-google-oauth20, passport-local, @nestjs/jwt, jose and bcryptjs) are the dependencies of @starci/auth and are imported by no app file; @starci/auth/server is imported only by integrations/passport, its one glue. bcrypt and every Supabase client are owned nowhere."
|
|
5700
5701
|
kinds: [lint]
|
|
5701
5702
|
gates: [pre-commit, settle, land, ci]
|
|
5702
5703
|
failureCodes: ["BE_AUTH_PASSPORT_OWNER"]
|
|
@@ -5718,7 +5719,7 @@ rules:
|
|
|
5718
5719
|
ruling: "Lite leaves Supabase: its identity is the app own Passport integration (Google OAuth and email/password) on its self-hosted PostgreSQL; full keeps Keycloak."
|
|
5719
5720
|
- id: R329
|
|
5720
5721
|
code: "BE_AUTH_JWT_OWN_JWKS"
|
|
5721
|
-
law: "A lite back-end principal comes only from the app's own access token verified by
|
|
5722
|
+
law: "A lite back-end principal comes only from the app's own access token, verified by @starci/auth (EdDSA pinned, issuer and audience checked, a local key set that may hold several keys), never from a remote JWKS, a token decoded without its signature check, a jwtVerify call in the app or a body claim."
|
|
5722
5723
|
kinds: [check, lint]
|
|
5723
5724
|
gates: [pre-commit, settle, land, ci]
|
|
5724
5725
|
failureCodes: ["BE_AUTH_JWT_OWN_JWKS"]
|
|
@@ -5742,7 +5743,7 @@ rules:
|
|
|
5742
5743
|
ruling: "The lite back end verifies its own asymmetric access tokens against its own JWKS; it trusts no remote identity provider."
|
|
5743
5744
|
- id: R330
|
|
5744
5745
|
code: "BE_AUTH_REFRESH_COOKIE"
|
|
5745
|
-
law: "A refresh token travels only in
|
|
5746
|
+
law: "A refresh token travels only in the httpOnly, Secure cookie that @starci/auth sets, rotates (with a reuse grace window and reuse detection) and clears; no app file writes that cookie, and the token never appears as a field of a request or response body."
|
|
5746
5747
|
kinds: [lint]
|
|
5747
5748
|
gates: [pre-commit, settle, land, ci]
|
|
5748
5749
|
failureCodes: ["BE_AUTH_REFRESH_COOKIE"]
|
|
@@ -5764,7 +5765,7 @@ rules:
|
|
|
5764
5765
|
ruling: "The refresh token of the lite identity rotates and lives only in an httpOnly cookie; the browser holds no credential."
|
|
5765
5766
|
- id: R331
|
|
5766
5767
|
code: "BE_STORAGE_OWNER"
|
|
5767
|
-
law: "The S3 client (@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner)
|
|
5768
|
+
law: "The S3 client libraries (@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner) are the dependencies of @starci/object-storage, the ObjectStorage port over S3-compatible storage (MinIO in lite) with presigned PUT and GET, and are imported by no app file; @starci/object-storage is imported only by integrations/object-storage, its one glue; no module imports minio."
|
|
5768
5769
|
kinds: [lint]
|
|
5769
5770
|
gates: [pre-commit, settle, land, ci]
|
|
5770
5771
|
failureCodes: ["BE_STORAGE_OWNER"]
|
|
@@ -5773,11 +5774,11 @@ rules:
|
|
|
5773
5774
|
editions: [lite, full]
|
|
5774
5775
|
docs: [contexts/code/be/infra.md]
|
|
5775
5776
|
proves:
|
|
5776
|
-
- packages/eslint-canon-be/src/
|
|
5777
|
+
- packages/eslint-canon-be/src/object-storage-package.spec.ts
|
|
5777
5778
|
exemplifiedBy:
|
|
5778
|
-
- {path: packages/hfs/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.
|
|
5779
|
+
- {path: packages/hfs/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts, edition: lite}
|
|
5779
5780
|
witness:
|
|
5780
|
-
- {path: packages/eslint-canon-be/src/
|
|
5781
|
+
- {path: packages/eslint-canon-be/src/object-storage-package.spec.ts, editions: [lite, full], codes: [BE_STORAGE_OWNER]}
|
|
5781
5782
|
cases:
|
|
5782
5783
|
- id: case-1
|
|
5783
5784
|
date: "2026-10-29"
|
|
@@ -6525,3 +6526,101 @@ rules:
|
|
|
6525
6526
|
source: owner
|
|
6526
6527
|
quote: "The skeleton is the ready structure with its data missing; it is drawn from isLoading alone and from the reads the region shows."
|
|
6527
6528
|
ruling: "Several reads feed one frame: loading is any read loading with no data; a key change reloads only the region keyed by it; a refresh keeps the content."
|
|
6529
|
+
- id: R368
|
|
6530
|
+
code: "HFS_EXPLICIT_EXPORT_SURFACE"
|
|
6531
|
+
law: "An exported declaration states its type where it is exported: the return type of an exported function, the type of an exported variable and the type of its parameters are written, not inferred from a body or from other files, because the isolated declaration emit must be able to write the file's surface alone. A component that returns only JSX is explicit. Each export that cannot be written is reported at its place with what to annotate, in a front-end and a back-end file alike."
|
|
6532
|
+
kinds: [lint]
|
|
6533
|
+
gates: [pre-commit, settle, land, ci]
|
|
6534
|
+
failureCodes: ["HFS_EXPLICIT_EXPORT_SURFACE"]
|
|
6535
|
+
enforcers:
|
|
6536
|
+
- {kind: eslint-fe, id: explicit-export-surface}
|
|
6537
|
+
- {kind: eslint-be, id: explicit-export-surface}
|
|
6538
|
+
editions: [lite, full]
|
|
6539
|
+
docs: [contexts/code/shared/typing.md]
|
|
6540
|
+
templateNa: "A template exports nothing the lint result cache keys on."
|
|
6541
|
+
proves:
|
|
6542
|
+
- {path: packages/eslint-canon-fe/src/explicit-surface.spec.ts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6543
|
+
- {path: packages/eslint-canon-be/src/explicit-surface.spec.ts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6544
|
+
- {path: packages/hfs/src/explicit-surface.spec.mts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6545
|
+
exemplifiedBy:
|
|
6546
|
+
- {path: packages/eslint-canon-fe/src/explicit-surface.spec.ts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6547
|
+
witness:
|
|
6548
|
+
- {path: packages/eslint-canon-fe/src/explicit-surface.spec.ts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6549
|
+
- {path: packages/eslint-canon-be/src/explicit-surface.spec.ts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6550
|
+
- {path: packages/hfs/src/explicit-surface.spec.mts, editions: [lite, full], codes: [HFS_EXPLICIT_EXPORT_SURFACE]}
|
|
6551
|
+
cases:
|
|
6552
|
+
- id: case-1
|
|
6553
|
+
date: "2026-11-03"
|
|
6554
|
+
source: owner
|
|
6555
|
+
quote: "enforce it by lint"
|
|
6556
|
+
ruling: "A file whose exported surface is inferred is open in the lint result cache: one body edit lints every importer again. The exported surface is written out, and the rule reports exactly what the cache counts as open."
|
|
6557
|
+
- id: R369
|
|
6558
|
+
code: "FE_DIRECTIVE_FIRST"
|
|
6559
|
+
law: "A `use client` or `use server` directive is the first statement of its module, above every import: below another statement it is an expression that does nothing (Prettier writes it as a parenthesised string) and next build refuses the file."
|
|
6560
|
+
kinds: [lint]
|
|
6561
|
+
gates: [pre-commit, settle, land, ci]
|
|
6562
|
+
failureCodes: ["FE_DIRECTIVE_FIRST"]
|
|
6563
|
+
enforcers:
|
|
6564
|
+
- {kind: eslint-fe, id: directive-first}
|
|
6565
|
+
editions: [lite, full]
|
|
6566
|
+
docs: [contexts/code/fe/boundaries.md]
|
|
6567
|
+
templateNa: "A template places its directives first."
|
|
6568
|
+
proves:
|
|
6569
|
+
- {path: packages/eslint-canon-fe/src/directive-first.spec.ts, editions: [lite, full], codes: [FE_DIRECTIVE_FIRST]}
|
|
6570
|
+
exemplifiedBy:
|
|
6571
|
+
- {path: packages/eslint-canon-fe/src/directive-first.spec.ts, editions: [lite, full], codes: [FE_DIRECTIVE_FIRST]}
|
|
6572
|
+
witness:
|
|
6573
|
+
- {path: packages/eslint-canon-fe/src/directive-first.spec.ts, editions: [lite, full], codes: [FE_DIRECTIVE_FIRST]}
|
|
6574
|
+
cases:
|
|
6575
|
+
- id: case-1
|
|
6576
|
+
date: "2026-11-03"
|
|
6577
|
+
source: owner
|
|
6578
|
+
quote: "no lint caught it"
|
|
6579
|
+
ruling: "A directive below an import broke Nivo's CI build on 2026-11-02; the directive is the first statement of its module and a lint reports it elsewhere."
|
|
6580
|
+
- id: R372
|
|
6581
|
+
code: "STACKS_LOCAL_CONFIG_INVALID"
|
|
6582
|
+
law: "In lite and full apps, every docker-compose environment of .starcistacks/application-stacks.yaml declares a coherent `local` block, judged by the same normalizeLocal the dev verbs of `starci app stack` (doctor, prepare, up, status, logs, verify, down) use: closed fields, one loopback port each, compose services that exist in the compose files, an environment-owned database role (never postgres, never the app role), dev-only custody generators and loopback probes; and a lite app declares at least one such dev environment, so `starci app stack up --env dev` has something to run."
|
|
6583
|
+
kinds: [check]
|
|
6584
|
+
gates: [settle, land, ci]
|
|
6585
|
+
failureCodes: ["STACKS_LOCAL_CONFIG_INVALID", "STACKS_DEV_UNDECLARED"]
|
|
6586
|
+
enforcers:
|
|
6587
|
+
- {kind: hfs, id: stacks-local, at: scripts/hfs/rules/stacks-local.mjs}
|
|
6588
|
+
editions: [lite, full]
|
|
6589
|
+
docs:
|
|
6590
|
+
- {path: contexts/project/stacks.md#project-stacks-6-every-runbook-declares-the-same-operations, editions: [lite, full]}
|
|
6591
|
+
proves:
|
|
6592
|
+
- {path: packages/hfs/src/stack-local.spec.mjs, editions: [lite, full]}
|
|
6593
|
+
exemplifiedBy:
|
|
6594
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks, edition: lite}
|
|
6595
|
+
witness:
|
|
6596
|
+
- {path: packages/hfs/src/stack-local.spec.mjs, editions: [lite, full]}
|
|
6597
|
+
- id: R373
|
|
6598
|
+
code: "STACKS_RUNBOOK_DOCKER_COMMAND"
|
|
6599
|
+
law: "The runbook of a docker-compose environment names `starci app stack up --env <env>` and carries no `docker build|run|compose|create|start|stop|pull|push` or `docker stack deploy` command: Docker is run by the harness verb, never by hand (hard rule 8)."
|
|
6600
|
+
kinds: [check]
|
|
6601
|
+
gates: [settle, land, ci]
|
|
6602
|
+
failureCodes: ["STACKS_RUNBOOK_DOCKER_COMMAND"]
|
|
6603
|
+
enforcers:
|
|
6604
|
+
- {kind: hfs, id: stacks-local, at: scripts/hfs/rules/stacks-local.mjs}
|
|
6605
|
+
editions: [lite, full]
|
|
6606
|
+
docs:
|
|
6607
|
+
- {path: contexts/project/stacks.md#project-stacks-10-local-runs, editions: [lite, full]}
|
|
6608
|
+
proves:
|
|
6609
|
+
- {path: packages/hfs/src/stack-local.spec.mjs, editions: [lite, full]}
|
|
6610
|
+
exemplifiedBy:
|
|
6611
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks, edition: lite}
|
|
6612
|
+
witness:
|
|
6613
|
+
- {path: packages/hfs/src/stack-local.spec.mjs, editions: [lite, full]}
|
|
6614
|
+
- id: R374
|
|
6615
|
+
code: "STACKS_CODE_BIND_MOUNT"
|
|
6616
|
+
law: "A service the app customises runs from an image built out of `.starcistacks/images/<name>/` (a Dockerfile whose every FROM is digest-pinned and whose final stage is the harness's image for that service, the fork or its runtime layer, when the harness owns one), the same image in every environment; no stack or compose file bind-mounts code, plugins or scripts into a service container (data volumes, secrets, least grants and read-only plain config data files stay allowed)."
|
|
6617
|
+
kinds: [check]
|
|
6618
|
+
gates: [settle, land, ci]
|
|
6619
|
+
failureCodes: ["STACKS_CODE_BIND_MOUNT", "STACKS_IMAGE_BASE_UNPINNED", "STACKS_IMAGE_BASE_NOT_FORK", "STACKS_IMAGE_ENV_DIVERGES"]
|
|
6620
|
+
enforcers:
|
|
6621
|
+
- {kind: hfs, id: stacks-service-images, at: scripts/hfs/rules/stacks-service-images.mjs}
|
|
6622
|
+
editions: [lite, full]
|
|
6623
|
+
docs:
|
|
6624
|
+
- {path: contexts/project/stacks.md#project-stacks-11-a-customised-service-is-an-image-the-app-builds-the-same-in-every-environment, editions: [lite, full]}
|
|
6625
|
+
proves:
|
|
6626
|
+
- {path: packages/hfs/src/stacks-service-images.spec.mjs, editions: [lite, full]}
|