@starci/hfs 5.1.0 → 5.3.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 +54 -0
- package/README.md +11 -2
- package/cache/hfs-family.d.mts +2 -3
- package/cache/hfs-family.mjs +4 -9
- package/cache/secret-records.d.mts +7 -0
- package/cache/secret-records.mjs +12 -0
- package/cache/session.mjs +4 -2
- package/codemod/account-doors.d.mts +36 -0
- package/codemod/account-doors.mjs +128 -0
- package/codemod/main.mjs +31 -1
- package/codemod/module-definition.d.mts +52 -0
- package/codemod/module-definition.mjs +242 -0
- package/codemod/quality-binding.d.mts +33 -0
- package/codemod/quality-binding.mjs +38 -0
- package/codemod/secrets-cache.d.mts +35 -0
- package/codemod/secrets-cache.mjs +70 -0
- package/codemod/sops-rules.d.mts +34 -0
- package/codemod/sops-rules.mjs +69 -0
- package/codemod/theme-tokens.mjs +48 -2
- package/db/postgres.mjs +9 -1
- package/lint/run.d.mts +6 -0
- package/lint/run.mjs +63 -10
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +65 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +9 -9
- package/runtime/knowledge/hfs/fork-images.yaml +7 -0
- package/runtime/knowledge/hfs/rules.yaml +67 -15
- package/runtime/knowledge/hfs/slots.yaml +174 -30
- package/runtime/modules/kernel/failure-codes.yaml +67 -4
- package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
- package/runtime/scripts/hfs/architecture/config.d.mts +4 -0
- package/runtime/scripts/hfs/architecture/config.mjs +25 -1
- package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
- package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +1 -1
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +6 -1
- package/runtime/scripts/hfs/architecture/root-packages.d.mts +26 -0
- package/runtime/scripts/hfs/architecture/root-packages.mjs +78 -0
- 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/architecture/type-context.d.mts +4 -1
- package/runtime/scripts/hfs/architecture/type-context.mjs +30 -0
- package/runtime/scripts/hfs/architecture/typescript.d.mts +7 -1
- package/runtime/scripts/hfs/architecture/typescript.mjs +21 -1
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +1 -1
- 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/database-access.mjs +39 -1
- package/runtime/scripts/hfs/rules/monorepo.d.mts +4 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +10 -5
- package/runtime/scripts/hfs/rules/secrets.mjs +11 -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-errors.d.mts +3 -0
- package/runtime/scripts/hfs/secret-errors.mjs +8 -0
- package/runtime/scripts/hfs/secret-init.d.mts +6 -0
- package/runtime/scripts/hfs/secret-init.mjs +23 -0
- package/runtime/scripts/hfs/secret-sops.mjs +9 -1
- package/runtime/scripts/hfs/secret-standard.d.mts +20 -0
- package/runtime/scripts/hfs/secret-standard.mjs +106 -0
- package/runtime/scripts/hfs/secret.mjs +15 -8
- 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/hfs/view.d.mts +23 -0
- package/runtime/scripts/hfs/view.mjs +68 -0
- package/runtime/scripts/lib/declaration-surface.d.mts +4 -0
- package/runtime/scripts/lib/declaration-surface.mjs +55 -1
- package/runtime/scripts/lib/secret-records.d.mts +13 -0
- package/runtime/scripts/lib/secret-records.mjs +83 -0
- package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
- package/runtime/scripts/lib/swarm-stack.mjs +2 -2
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-module.d.mts +16 -0
- package/scaffold/add-module.mjs +232 -0
- package/scaffold/add-queue-lite.mjs +50 -3
- package/scaffold/add.mjs +5 -0
- package/scaffold/app.d.mts +2 -2
- package/scaffold/app.mjs +16 -18
- package/scaffold/lite-exports.mjs +1 -15
- package/scaffold/module-registry.d.mts +19 -0
- package/scaffold/module-registry.mjs +84 -0
- package/scripts/sync-bundles.d.mts +8 -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/images/openclaw-runtime/Dockerfile +18 -0
- package/stack/images/openclaw-runtime/entrypoint.sh +23 -0
- package/stack/images/openclaw-runtime/openclaw-init.mjs +101 -0
- package/stack/images/openclaw-runtime/openclaw-relay.mjs +25 -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 +169 -0
- package/stack/local-verify.d.mts +6 -0
- package/stack/local-verify.mjs +21 -0
- package/stack/runtime-layers.d.mts +21 -0
- package/stack/runtime-layers.mjs +67 -0
- package/sync/hygiene.mjs +3 -1
- package/sync/index.d.mts +15 -12
- package/sync/index.mjs +114 -28
- package/templates/app/auth-doors.sql.partial +115 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
- 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/core.json +1 -1
- 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 +85 -70
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +24 -12
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +43 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +21 -26
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +6 -1
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +76 -13
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +44 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.handler.ts +22 -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/resend.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.handler.ts +22 -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/application/signup.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +6 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +21 -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 +6 -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 +145 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/forgot.request.ts +16 -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/features/api/auth/transport/http/dto/resend.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/reset.request.ts +18 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/signup.request.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/token.request.ts +11 -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 +19 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +209 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +84 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.spec.ts +233 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.ts +176 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-sign-in.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/account-doors.port.ts +98 -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-email-token.entity.ts +34 -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 +10 -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 +95 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +93 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +40 -0
- package/templates/be/inbox/module-definition.ts.tpl +2 -9
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +2 -11
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +3 -9
- package/templates/be/platform/graphql/graphql.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +3 -13
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +4 -9
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +5 -11
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +2 -9
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +3 -0
- 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 +12 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +40 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +22 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +50 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity-mail.messages.ts +65 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.config.ts +33 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.decorators.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module.ts +27 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.options.ts +22 -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 +5 -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/entities.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/rate-limit.guard.spec.ts +11 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +22 -4
- package/templates/be/table/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/table/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/table/module-definition.ts.tpl +2 -9
- 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 -4
- package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/email-doors.ts +16 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/index.ts +5 -0
- 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/useViewerId.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +15 -12
- package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +30 -67
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +6 -21
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +10 -16
- 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]/auth/confirm/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/forgot/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/resend/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/reset/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/signup/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +5 -9
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/component.tsx +51 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/index.tsx +84 -0
- 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/pages/AppConfirmPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppForgotPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +11 -3
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +4 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResendPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResetPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppSignupPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/emailAuth.shared.ts +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/useEmailDoor.ts +32 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +7 -30
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +13 -14
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +4 -17
- 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 -41
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -40
- 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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.3.0 - 2026-11-03
|
|
4
|
+
|
|
5
|
+
- Fixed: the secrets family of the committed check cache stores codes, paths, positions and digests only (`cache/secret-records.mts`, `runtime/scripts/lib/secret-records.mts`), and the one secret judgement of R06 judges a file of `.starcistacks/cache/check/secrets/` by that contract, so `starci app check` can no longer write a cache the staged guard refuses (`HFS_PLAINTEXT_SECRET` for the `secrets/` folder name). A cache written before reads back as digests. Codemod: `starci app codemod secrets-cache`.
|
|
6
|
+
- Added: `@starci/nest-kit` in the starter dependencies and in every module-definition template: a module definition is one call (`defineConfigurableModule<XOptions>()`, `defineCapabilityModule<XOptions>()` for a capability's representative module). The lint on stated export types (R368) accepts a destructured export of those two calls and no other; the digest of the file carries the call, so a change of the options type reaches the importers; `capability-module-shape` (R45) accepts the helper and `module-per-transport` confines it to the definition file. Codemod: `starci app codemod module-definition` rewrites the destructured and the typed builder forms and adds the dependency.
|
|
7
|
+
- Fixed: `HFS_MONO_WORKSPACES` asks for `packages/*` after the fe workspaces once the app tracks a root package (slot `app.packages`, the module packages), so `starci app sync` and `starci app check` keep the entry `app add module` writes; the managed `test` and `build:be` scripts build the module packages first and the CI fast job runs `npm run test:modules` (PROJECT-3, BE-TEST-13).
|
|
8
|
+
- Added: `starci app stack up` builds the harness runtime layer an app image stands on (`openclaw-runtime`) from the recipe this package ships (`stack/images/openclaw-runtime`, kept byte-equal to the harness `images/openclaw-runtime` by `npm run bundles`) when the host neither holds nor can pull it, as a governed heavy build.
|
|
9
|
+
- Fixed: a secret member no creation rule of `.sops.yaml` reaches is refused `SECRET_SOPS_RULE_MISSING` before sops runs (a rule written for the legacy `.enc` spelling does not reach a `.enc.yaml` member), naming the path and `starci app secret init --env <env>`. Codemod: `starci app codemod sops-rules` migrates the legacy rule to the managed one (public recipients only).
|
|
10
|
+
- Fixed: `starci app secret set` on a tooling member (`sonar.enc.yaml`, any member a service credential's custody path names) no longer declares it in `environments.<env>.secrets` (which made CI custody refuse the analysis token); it binds the member to its service (`credentials` and `ci.secrets`, the shape the template renders) or prints the exact binding. Codemod: `starci app codemod quality-binding` binds an app scaffolded before and names a runtime entry an earlier set declared.
|
|
11
|
+
- Added: the lite starter depends on `@starci/mail` and renders the five email doors (BE `IdentityEmailService` and `identity_email_tokens`; FE `/auth/signup`, `/auth/resend`, `/auth/forgot`, `/auth/confirm`, `/auth/reset`, `EmailDoorForm`, the managed `modules/auth/email-doors.ts`), a Mailpit dev sink pinned by digest (`mail-sink.spec.mts`), and the stack links `@starci/mail` in the scaffold type-check.
|
|
12
|
+
- Fixed: the `theme-tokens` codemod drops an alias whose rename would name itself (`--starci-core-accent: var(--accent)` became `--accent: var(--accent)`, which is invalid at computed-value time and blanked every brand colour) and a rule block that held only such aliases, with its comment; R61 now lists the stylelint rule `no-self-reference` (`@starci/stylelint-canon` 2.6.0) among its enforcers.
|
|
13
|
+
- Changed: the scaffold type-check runs the back-end and front-end chains and both editions concurrently (the same checks, async spawns).
|
|
14
|
+
- Fixed: an app's root workspace packages (`packages/agent-sdk`, `packages/module-<name>`, the managed `packages/modules`) are first-party packages of the import graph. A side imports one by its package name through the `exports` of its manifest: that is no import that leaves the side (`ARCH_INTERNAL_IMPORT_OUTSIDE` stopped the front-end lint run on `@scope/agent-sdk/workbench`) and adds no edge; an import into the package's source past its entry points stays refused, with a message that lists the entry points. The packages are read from the app root's `workspaces` (patterns that start with no side folder) and from the slots `app.packages` and `app.module-registry` (`architecture/root-packages.mts`, `rootPackages` of the architecture config). Spec: `architecture-root-packages.spec.mts`.
|
|
15
|
+
- Added: the slots `app.packages`, `app.module-registry` and `app.packages-lint` in the runtime slot manifest (3.17.0; the first two were only in `contexts/hfs/slots.yaml`, so `packages/` classified as `HFS_SLOT_UNDECLARED`). `eslint.config.mjs` at the app root is the managed config of the root packages, no longer a forbidden tool config.
|
|
16
|
+
- Added: `starci app sync` renders `eslint.config.mjs` at the app root whenever the app has a root package: the back-end canon over the back-end subpaths of `packages/<pkg>/src/**`, the front-end canon and the Grammar family's `<family>GrammarPackagesConfig` over `src/workbench/**` and `src/public/**`. `starci app check --gate lint` runs it as a third scope (`packages`, from the app root, uncached) beside `be` and `fe`. The package view of the slot resolver (`openPackagesHfsView`, `declaredPackagesHfsView` in `view.mts`) is the profile of a side over the paths of the app root; a package file is of the tier `package` and has no owner. Spec: `lint-packages.spec.mts`.
|
|
17
|
+
- Changed: the lite sign-in and the email doors reach accounts through the account doors. The baseline migration includes `templates/app/auth-doors.sql.partial`, which is now the source of truth of the doors (it is no longer a byte copy of an app's own migration): eight SECURITY DEFINER functions `public.be_auth_user_by_email`, `_by_id`, `_by_google`, `be_auth_create_user`, `be_auth_link_google`, `be_auth_touch_user`, `be_auth_confirm_user(user, now)` (stamps the confirmation only, no sign-in record) and `be_auth_set_password(user, hash, confirm, now)` (sets the hash and, when `confirm` is true, confirms the mailbox), each with a pinned `search_path` and EXECUTE revoked from public, anon and authenticated and granted to `app_be` alone, plus a `lower(email)` index on `auth.users` the by-address door reads. `be_auth_set_password` is a four-argument function: an app that already has a three-argument one of its own keeps both. The three grants of `app_be` on schema `auth`, `auth.users` and `auth.identities` are gone from the baseline, so one injectable query no longer reaches every account's hash, role and metadata. The eight doors are in `ruleParams.be.dbFunctions`; an app lists none of them in hfs.json.
|
|
18
|
+
- Changed: the managed identity glue (slot `be.identity.sign-in`): `AuthUserEntity` and `AuthIdentityEntity` are deleted; the account directory, the authenticator and `IdentityEmailService` (signup insert, mailbox confirm, password reset) call the doors through the new managed `persistence/account-doors.port.ts`, which validates every answer (`returnedRows`, `rowShape`); the database decides `disabled` at its own now() (a banned account no longer receives a reset mail). The rate counter over `identity_attempts` and the fail-closed refusal mapping are unchanged.
|
|
19
|
+
- Added: `IdentitySignInService.endOthers(principal)` signs the caller out of every other device, and `Principal.sessionId` carries the device session of the access token. The authenticator logs an account created by Google sign-in as `identity.sign_in.account_created` (new managed `identity-sign-in.log-events.ts`) with the account id only. The lite starter carries `platform/database/database.client.ts` and exports `callDbFunction`.
|
|
20
|
+
- Changed: R255 (`DB_APP_ROLE`) also refuses any grant to the declared back-end role on schema `auth`, on a table or sequence in it, or by default privilege for it, with no transition tolerance. The migrations are judged in order, so a later migration that revokes a grant clears it. BE-AUTH-2, BE-PERSISTENCE-4 and BE-PERSISTENCE-17 follow.
|
|
21
|
+
- Added: `starci app codemod account-doors [--contract] [--execute]`, the upgrade of a lite app already on the template: it writes the expand migration `db/migrations/<ts>_account-doors.sql` with the doors the app lacks (a door is known by name and argument types, so an app that has the six earlier doors gets only the two new ones) and, with `--contract`, the migration `<ts>_contract_revoke-auth-grants.sql` that revokes the three grants from the declared role.
|
|
22
|
+
- Changed: `starci app sync --execute` deletes the retired `auth-user.entity.ts` and `auth-identity.entity.ts` of an app on the old glue.
|
|
23
|
+
- Upgrade, for a lite app scaffolded at 3.16.0 or later (the old glue grants `app_be` on `auth.users`): the new glue calls functions the app's database does not have until the expand migration is applied. Ship these three steps in one release, in this order; R255 reports the old grants until step 3 is in the chain, so they cannot ship apart.
|
|
24
|
+
1. `npx starci app codemod account-doors --execute` (expand): writes the migration that creates the doors the app lacks and the index.
|
|
25
|
+
2. `npx starci app sync --execute`: moves the glue onto the doors and deletes the two entity files; `npx starci app check` is green except R255 until step 3.
|
|
26
|
+
3. `npx starci app codemod account-doors --contract --execute` (contract): writes the migration that revokes the old grants. It refuses while an account door is undefined or a back-end source still maps `schema: "auth"`.
|
|
27
|
+
4. The deploy applies the expand migration before the services and the contract migration after the accept.
|
|
28
|
+
5. Rollback: the release before this one still reads the auth tables, so a rollback to it after the accept must restore the grants by hand, as the database owner:
|
|
29
|
+
```sql
|
|
30
|
+
grant usage on schema auth to app_be;
|
|
31
|
+
grant select, insert, update on table auth.users to app_be;
|
|
32
|
+
grant select, insert on table auth.identities to app_be;
|
|
33
|
+
```
|
|
34
|
+
(use the role `db.appRole` of hfs.json when it is not app_be). Revoke them again with the contract migration when the rollback is undone.
|
|
35
|
+
6. An app that already defines the six earlier doors (Nivo) runs step 1 too and gets only `be_auth_confirm_user` and the four-argument `be_auth_set_password`; its hfs.json `sides.be.dbFunctions` may drop the doors, which the canon now lists.
|
|
36
|
+
|
|
37
|
+
## 5.2.0 - 2026-11-03
|
|
38
|
+
|
|
39
|
+
- Added: a scaffolded app of each edition type-checks and builds on every side (`scaffold-type-check.spec.mts`: one install per edition, `tsc` of the back end and its specs, the back-end build, the workspace packages, `tsc` and `next build` of each front-end app). The templates follow the Grammar contract: `Link` and `Page` for `SiteShell` and `NotFoundNotice`, a callable `toast`, the session reducers as a type, explicit next-intl navigation generics, optional access to the test world's apps and connections, no unused import in the migrations fixture. The lite dev api process no longer runs `--transpile-only`.
|
|
40
|
+
- Added: the custody kind `ed25519-pem` of `local.custody` (the dev sign-in key is EdDSA); the lite dev environment names `AUTH_WEB_ORIGIN`.
|
|
41
|
+
- Added: `starci app add module <name>` and `starci app remove module <name>`: a module package `packages/module-<name>` of the product's agent SDK (`./template`, `./manifest`, `./workbench`, `./public`), registered in the root `workspaces` and the back-end and front-end dependencies, listed in the managed registry package `packages/modules` that `starci app sync` renders and drift-checks. Slots `app.packages` and `app.module-registry` (manifest 3.17.0). The module packages enter the Sonar sources, the Codecov paths and a `build:modules` / `test:modules` script pair that the typecheck, the lint and the build run first.
|
|
42
|
+
- Added: rule R374 (`STACKS_CODE_BIND_MOUNT`, `STACKS_IMAGE_BASE_UNPINNED`, `STACKS_IMAGE_BASE_NOT_FORK`, `STACKS_IMAGE_ENV_DIVERGES`): a customised service is an image built from `.starcistacks/images/<name>/` on the harness's pinned base, no bind-mounted code, the same image in every environment (PROJECT-STACKS-11).
|
|
43
|
+
- Changed: FE-PACKAGES-2 (a shared package, a module's workbench included, draws only with Grammar) and BE-TEST-13 (a module package's logic is inside the 100 per file scope).
|
|
44
|
+
- Added: the lite starter composes `@starci/auth`: it depends on the package and on none of the identity libraries, `integrations/passport` is the glue the slot lists (`passport.config.ts`, `passport.options.ts`, `passport.decorators.ts`, `passport.kit.ts`, `errors/`), the web app keeps one `@starci/auth/client` instance in `modules/auth/client.ts`, and the baseline migration holds the package's session tables (`identity_sessions`, `identity_refresh_tokens`) byte for byte.
|
|
45
|
+
- Changed: `infraOwners` own `@nestjs/passport`, `passport*`, `@nestjs/jwt`, `jose` and `bcryptjs` by nobody and `@starci/auth` by `integrations/passport` alone (R328); the `own-jwks-principal` machine refuses a `jwtVerify` call anywhere in the app and requires the `@starci/auth/server` import in the passport integration (R329); `transport-owner` no longer admits an auth door in `modules/auth`; the `auth` stack concern names `@starci/auth` and forbids `jose`, `jwt-decode`, `next-auth`, `@auth/core`, `keycloak-js` and `oidc-client-ts`.
|
|
46
|
+
- Changed: the lite stack templates name `AUTH_WEB_ORIGIN` and the Ed25519 signing key; `AUTH_JWT_PRIVATE_KEY` is an EdDSA key and the public key is derived from it.
|
|
47
|
+
- Added: the lite starter signs in with no hand-written auth code. The managed slots `be.identity.sign-in`, `be.feature.auth` and `fe.modules.auth.client` render, from `templates/be/auth-lite` and `templates/fe/auth-lite`, the `/auth` doors (login, refresh, session, logout, Google start and callback, jwks), the entities of the session, attempt, user and identity tables, the session, account and rate stores, the authenticator, `IdentitySignInService`, their specs and the web app's `createAuthClient` client; an edit is `HFS_MANAGED_FILE_DRIFT`. The identity error family gains the sign-in codes, the passport family keeps its `PASSPORT_` codes.
|
|
48
|
+
- Added: the slot field `managedOnly` (a slot that only lists files sync renders; the slot owning the path judges them). Manifest 3.16.0.
|
|
49
|
+
- Changed: the sync and the scaffold count the back-end source files their own targets render, so the CI shard count and the coverage scope are the same before and after the targets are written (one sync leaves the app in sync).
|
|
50
|
+
- Added: the canon pin of `@starci/auth` 0.1.0.
|
|
51
|
+
- Added: `starci app stack prepare|doctor|up|status|logs|down|verify --env <env>` for a docker-compose (dev) environment, driven by the new `environments.<env>.local` block of application-stacks.yaml: ports, compose services and their health, the environment's own database role (the migrations run as it and the app role gets its own password), dev-only custody generators, least grants (`STACK_GRANT_<NAME>`), data directories (`STACK_DATA_DIR`, `${data}`), setup and seed commands (a seed runs after the migration or after readiness), native app processes, readiness and verification probes. `up --execute` is the one command (host table, custody, setup, services, database, migrate with a receipt, seed once, processes, readiness, origins); `prepare`, `up` and `down` are dry runs until `--execute`, `down` never removes a volume, `doctor` prints the owner-run install step per OS for each missing prerequisite and installs nothing. A swarm environment keeps the swarm verbs and refuses the dev-only ones.
|
|
52
|
+
- Added: `starci app secret init --env <dev env> [--execute]` creates the dev-only values `local.custody` declares (hex, base64, password, uuid, rsa-pem, ec-pem), once: a sealed value is kept, a half-present pair is refused, a vps or production environment is refused.
|
|
53
|
+
- Added: `db migrate` runs its client tools inside the database container named by `STARCI_DB_CONTAINER`, so a host without PostgreSQL client tools migrates a dev database.
|
|
54
|
+
- Added: rules R372 (`STACKS_LOCAL_CONFIG_INVALID`, `STACKS_DEV_UNDECLARED`: a coherent local block, and a lite app declares its dev environment) and R373 (`STACKS_RUNBOOK_DOCKER_COMMAND`: the dev runbook names `starci app stack up --env <env>` and carries no docker command).
|
|
55
|
+
- Changed: the lite skeleton's dev environment is `docker-compose` with a local block, a compose file and a runbook of the new verbs (it was a Swarm stack with illustrative digests); slot `app.starcistacks` admits `<env>/infra/compose/**` in lite; `add queue` in lite adds the Redis to the dev compose file and the queue settings to the dev processes.
|
|
56
|
+
|
|
3
57
|
## 5.1.0
|
|
4
58
|
|
|
5
59
|
- Added: `starci app codemod explicit-surface` writes the inferred type of every export the classifier counts as open (R368): return types, variable, parameter and property types, from the app's own TypeScript program. It prefers a named type the program exports, writes `ReactElement` for components and Next's `Metadata` for `generateMetadata`, adds merged type-only imports after a directive prologue, compiles the result beside the original and writes a file only when it gains no diagnostic, and reports (with a suggested fix) destructured exports and anonymous, computed, too long, `any`, unnameable and predicate types. Prettier of the app formats what is written.
|
package/README.md
CHANGED
|
@@ -24,10 +24,19 @@ starci app world <up|down|status> [--stack <dir>] [--services <list>] [--k3d] [-
|
|
|
24
24
|
starci app slot <path> [--cwd <dir>] [--json]
|
|
25
25
|
starci app emit [--cwd <dir>]
|
|
26
26
|
starci app db drift [--db-url-env <NAME>] [--json] [--cwd <dir>]
|
|
27
|
-
starci app
|
|
27
|
+
starci app stack <prepare|up|down> --env <dev env> [--execute] | <doctor|status|verify> --env <dev env> | logs --env <dev env> --service <name> [--lines <n>]
|
|
28
|
+
starci app secret init [--env <dev env> [--execute]] | <set|get|unset|rename|remove|materialize|list|verify|recipients|migrate|split|audit|server-key> --env <env> ... [--cwd <dir>]
|
|
28
29
|
starci app ci secret set <NAME> [--from-custody] [--env <env>] [--repo <owner/name>] [--execute]
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
**The dev environment in one command.** `starci app stack up --env dev --execute` brings a docker-compose environment up from its `local` block of
|
|
33
|
+
`.starcistacks/application-stacks.yaml` (PROJECT-STACKS-6 of `contexts/project/stacks.md`): the host table (`doctor` prints the owner-run install
|
|
34
|
+
step per OS for each missing prerequisite and installs nothing), the dev custody (`secret init --env dev --execute` creates the dev-only values once),
|
|
35
|
+
the setup commands, the compose services and their health, the environment's own database role, the migrations with a receipt (`db migrate` runs its
|
|
36
|
+
client tools in the database container named by `STARCI_DB_CONTAINER`, so the host needs no PostgreSQL client), the seed once, the native app
|
|
37
|
+
processes, readiness and the origins. `prepare`, `up` and `down` are dry runs until `--execute`; `down` never removes a volume. State (process ids,
|
|
38
|
+
logs, receipts) is under the state base (`STARCI_LOCAL_ROOT`), the opened custody under the private secrets directory outside the checkout.
|
|
39
|
+
|
|
31
40
|
`starci app add` writes every front-end unit, in either edition, already in the shape of the harness's `contexts/code/fe`
|
|
32
41
|
and `contexts/ui`, from `templates/fe/units`, with its copy in every catalog; nothing that exists is overwritten.
|
|
33
42
|
|
|
@@ -139,7 +148,7 @@ Every file of a spec-required role of `ruleParams.be.unitRoles` — a `*.service
|
|
|
139
148
|
|
|
140
149
|
The spec skeleton is `Test.createTestingModule({ providers: [Service, { provide: TOKEN, useValue: double }, ...] }).compile()` and `moduleRef.get(Service)`, with one provider per constructor dependency and nothing else, every double imported from `@starci/jest-preset` (the root), and one placeholder `it` per public method. The double of a token comes from `ruleParams.be.specDoubles` of the slot manifest, the table the lint law `spec-infra-double-from-kit` holds a spec to (`*_ENTITY_MANAGER` -> `mockEntityManager()`, `CLOCK` -> `new FakeClock(...)`, `EVENT_BUS` -> `recordingEventBus()`, `QUEUE_OUTBOX` -> `recordingQueueOutbox()`, `CACHE` -> `fakeCache(clock)`, lock, lease, fence and hold -> `fakeLock(clock)`, ids -> `fakeIds()`, `*_OPTIONS` -> a literal to fill in, everything else and every class -> `mock<T>()`), so the skeleton satisfies the law by construction: no cast, no `new` of the service, no ambient clock. A back end only (a front end has no services); the files are written for prettier (print width 120).
|
|
141
150
|
|
|
142
|
-
`starci app check` runs ESLint once per side through the app's installed managed flat config: the backend canon on `be/`, and the universal FE canon plus the explicitly selected Grammar-family package on `fe
|
|
151
|
+
`starci app check` runs ESLint once per side through the app's installed managed flat config: the backend canon on `be/`, and the universal FE canon plus the explicitly selected Grammar-family package on `fe/`; an app with root workspace packages (`packages/agent-sdk`, `packages/module-<name>`) has a third run from the app root through the managed root `eslint.config.mjs` that `starci app sync` renders, over `packages/<pkg>/src/**` by subpath (the backend canon on everything but `src/workbench/` and `src/public/`, the workbench contract kept; the universal FE canon and the Grammar family on those two, the contract left out). It also reads the app's `hfs.json` and tracked paths (`git ls-files`), checks the work tree (the root rules once, the side rules per side), and runs the whole architecture machine over each side folder. HFS findings carry their why code and Vietnamese text (`modules/kernel/failure-codes.yaml`); ESLint findings retain their rule id and failure code.
|
|
143
152
|
|
|
144
153
|
Its own checks (`scripts/hfs/check.mjs`, `scripts/hfs/rules/`; the rendered-file checks (`sync/managed.mjs`) and the prettier check (`sync/format.mjs`) live in `sync/` because they read the templates and the repository's own install, and reach `starci app check` as `extraFindings`; the jest / vitest preset the coverage exclusions come from and prettier are read from the repository's `node_modules`, so run `npm ci` first):
|
|
145
154
|
|
package/cache/hfs-family.d.mts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
2
2
|
import { type MachineFn, type RepositoryResult } from '../runtime/scripts/hfs/check.mjs';
|
|
3
3
|
import type { Presets } from '../sync/index.mjs';
|
|
4
|
+
import { SECRETS_FOLDER, SECRET_CODES } from './secret-records.mjs';
|
|
4
5
|
import type { CheckSession, FamilyReport } from './types.mjs';
|
|
5
6
|
export declare const HFS_FOLDER = "check/hfs";
|
|
6
|
-
export
|
|
7
|
-
/** The finding codes of the secrets family: plaintext secrets, sealed custody, untracked secret files. */
|
|
8
|
-
export declare const SECRET_CODES: ReadonlySet<string>;
|
|
7
|
+
export { SECRETS_FOLDER, SECRET_CODES };
|
|
9
8
|
/** What the structure pass answers. */
|
|
10
9
|
export interface PassResult {
|
|
11
10
|
/** The whole-app check result of the pass (the shape `printCheck` reads), or the slot result when only secrets were asked for. */
|
package/cache/hfs-family.mjs
CHANGED
|
@@ -16,15 +16,10 @@ import { withRuleMemo } from '../runtime/scripts/hfs/rule-memo.mjs';
|
|
|
16
16
|
import { RecordSet, scopedStep } from './records.mjs';
|
|
17
17
|
import { FILE_RULES_STEP, RULE_STEP_PREFIX, UNIT_STEP_PREFIX, createStructureMemo } from './structure-memo.mjs';
|
|
18
18
|
import { makeReport } from './report.mjs';
|
|
19
|
+
import { SECRETS_FOLDER, SECRET_CODES, safeSecretFinding, safeSecretRecords } from './secret-records.mjs';
|
|
19
20
|
import { sideKey } from './side-key.mjs';
|
|
20
21
|
export const HFS_FOLDER = 'check/hfs';
|
|
21
|
-
export
|
|
22
|
-
/** The finding codes of the secrets family: plaintext secrets, sealed custody, untracked secret files. */
|
|
23
|
-
export const SECRET_CODES = new Set([
|
|
24
|
-
'HFS_PLAINTEXT_SECRET', 'HFS_SECRET_PLAINTEXT_FILE', 'HFS_SECRET_OUTSIDE_ENC', 'HFS_SCRIPT_PLAINTEXT_LOAD', 'HFS_LITE_SECRET_CUSTODY', 'HFS_IDENTITY_CUSTODY',
|
|
25
|
-
'HFS_AGE_KEY_CUSTODY', 'RT_SECRET_TRACKED', 'STACKS_SECRET_ENV_LITERAL', 'STACKS_SECRET_NOT_EXTERNAL', 'STACKS_SECRET_UNVERSIONED', 'SEALED_CUSTODY_LOCATION',
|
|
26
|
-
'APP_SECRET_IN_CI', 'CI_SECRET_SEALED', 'BE_LOG_SECRET', 'BE_SECRET_DEFAULT', 'HFS_DOCKER_SECRETS',
|
|
27
|
-
]);
|
|
22
|
+
export { SECRETS_FOLDER, SECRET_CODES };
|
|
28
23
|
const compact = (finding) => ({
|
|
29
24
|
path: finding.path ?? '.', line: finding.line ?? 0, column: finding.column ?? 0, rule: finding.code, message: String(finding.message ?? ''),
|
|
30
25
|
});
|
|
@@ -169,8 +164,8 @@ export async function runStructurePass(session, input) {
|
|
|
169
164
|
const secretDecision = session.decide('secrets');
|
|
170
165
|
const prior = session.records(SECRETS_FOLDER);
|
|
171
166
|
const secretFindings = result.findings.filter((finding) => SECRET_CODES.has(finding.code));
|
|
172
|
-
session.replace(SECRETS_FOLDER, findingRecords(secretFindings, session.index.hashOf.bind(session.index), 'secrets'));
|
|
173
|
-
secretsReport = makeReport({ family: 'secrets', mode: secretDecision.mode, reason: secretDecision.reason, checked: filesChecked, reused: filesReused, previous: prior.flatMap((record) => record.findings), current: secretFindings.map(compact) });
|
|
167
|
+
session.replace(SECRETS_FOLDER, safeSecretRecords(findingRecords(secretFindings, session.index.hashOf.bind(session.index), 'secrets')));
|
|
168
|
+
secretsReport = makeReport({ family: 'secrets', mode: secretDecision.mode, reason: secretDecision.reason, checked: filesChecked, reused: filesReused, previous: prior.flatMap((record) => record.findings.map(safeSecretFinding)), current: secretFindings.map((finding) => safeSecretFinding(compact(finding))) });
|
|
174
169
|
session.report(secretsReport);
|
|
175
170
|
}
|
|
176
171
|
return { result, contracts, machineReused: reasons, report, secretsReport };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SECRETS_FOLDER, SECRET_CODES } from '../runtime/scripts/lib/secret-records.mjs';
|
|
2
|
+
import type { CacheRecord, Finding } from './types.mjs';
|
|
3
|
+
export { SECRETS_FOLDER, SECRET_CODES };
|
|
4
|
+
/** A finding as the secrets cache stores it: the same code, path and position, the message as its digest. */
|
|
5
|
+
export declare function safeSecretFinding(finding: Finding): Finding;
|
|
6
|
+
/** The records as the secrets cache stores them: each finding safe, no `data`. */
|
|
7
|
+
export declare function safeSecretRecords(records: readonly CacheRecord[]): CacheRecord[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// secret-records.mts - the writer side of the secrets records contract (runtime/scripts/lib/secret-records.mts): the cache is committed, so the
|
|
2
|
+
// records of `check/secrets` are projected to codes, paths, positions and digests before they are stored or compared, whoever produced them.
|
|
3
|
+
import { messageDigest, SECRETS_FOLDER, SECRET_CODES } from '../runtime/scripts/lib/secret-records.mjs';
|
|
4
|
+
export { SECRETS_FOLDER, SECRET_CODES };
|
|
5
|
+
/** A finding as the secrets cache stores it: the same code, path and position, the message as its digest. */
|
|
6
|
+
export function safeSecretFinding(finding) {
|
|
7
|
+
return { path: finding.path, line: finding.line, column: finding.column, rule: finding.rule, message: messageDigest(finding.message) };
|
|
8
|
+
}
|
|
9
|
+
/** The records as the secrets cache stores them: each finding safe, no `data`. */
|
|
10
|
+
export function safeSecretRecords(records) {
|
|
11
|
+
return records.map((record) => ({ path: record.path, step: record.step, hash: record.hash, findings: record.findings.map(safeSecretFinding) }));
|
|
12
|
+
}
|
package/cache/session.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { createContentIndex } from './content-index.mjs';
|
|
|
8
8
|
import { computeFingerprint, fingerprintDifferences, parseFingerprint, serializeFingerprint } from './fingerprint.mjs';
|
|
9
9
|
import { hashFile } from './hash.mjs';
|
|
10
10
|
import { groupByArea, parseRecords, serializeRecords } from './jsonl.mjs';
|
|
11
|
+
import { SECRETS_FOLDER, safeSecretRecords } from './secret-records.mjs';
|
|
11
12
|
import { readCacheDir, resolveTrust, verifyCommitted } from './trust.mjs';
|
|
12
13
|
import { CACHE_DIR } from './types.mjs';
|
|
13
14
|
/** `STARCI_APP_CHECK_CACHE`: `full` ignores the cache (the release gate sets it), `verify` is read by the caller (runs diff, then full, compares). */
|
|
@@ -122,10 +123,11 @@ export function createDiskSession(options) {
|
|
|
122
123
|
return global() ?? { mode: 'diff', reason: `against ${trust.reason}` };
|
|
123
124
|
},
|
|
124
125
|
records(folder) {
|
|
125
|
-
return replaced.get(folder)?.records ?? trustedRecords(folder);
|
|
126
|
+
return replaced.get(folder)?.records ?? (folder === SECRETS_FOLDER ? safeSecretRecords(trustedRecords(folder)) : trustedRecords(folder));
|
|
126
127
|
},
|
|
127
128
|
replace(folder, records, replaceOptions) {
|
|
128
|
-
|
|
129
|
+
// The secrets family never stores secret material, whoever produced the records (secret-records.mts).
|
|
130
|
+
replaced.set(folder, { records: folder === SECRETS_FOLDER ? safeSecretRecords(records) : records, depth: replaceOptions?.depth ?? 2 });
|
|
129
131
|
},
|
|
130
132
|
unchanged: (paths, captured) => paths.every((file) => {
|
|
131
133
|
const now = hashFile(path.join(root, file));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** One migration the codemod writes (or would write). */
|
|
2
|
+
export interface AccountDoorsChange {
|
|
3
|
+
file: string;
|
|
4
|
+
phase: 'expand' | 'contract';
|
|
5
|
+
why: string;
|
|
6
|
+
}
|
|
7
|
+
/** One case left for a person. */
|
|
8
|
+
export interface AccountDoorsFinding {
|
|
9
|
+
file: string;
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
/** The report of the upgrade (`starci app codemod account-doors`). */
|
|
13
|
+
export interface AccountDoorsReport {
|
|
14
|
+
schema: string;
|
|
15
|
+
root: string;
|
|
16
|
+
write: boolean;
|
|
17
|
+
phase: 'expand' | 'contract';
|
|
18
|
+
changes: AccountDoorsChange[];
|
|
19
|
+
findings: AccountDoorsFinding[];
|
|
20
|
+
next: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Upgrade of a lite app that signs in over direct grants on auth.users and auth.identities to the account doors. The expand step (default)
|
|
24
|
+
* writes the migration that creates the doors the app lacks, with the text the lite baseline includes; the app then runs `starci app sync --execute`
|
|
25
|
+
* (the sign-in glue switches to the doors and the two entity files go). The contract step (`contract`) writes the migration that revokes the three
|
|
26
|
+
* grants, once the doors exist and no be source maps the auth tables; R255 reports the grants until that migration is in the chain. Nothing is written while a case is left for a person, and a step that is already
|
|
27
|
+
* done changes nothing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function migrateAccountDoors({ root, write, contract, now }: {
|
|
30
|
+
root: string;
|
|
31
|
+
write?: boolean;
|
|
32
|
+
contract?: boolean;
|
|
33
|
+
now?: () => number;
|
|
34
|
+
}): AccountDoorsReport;
|
|
35
|
+
/** Render the migrations and what is left for a person as Markdown; JSON consumers read the same report object. */
|
|
36
|
+
export declare function accountDoorsMarkdown(report: AccountDoorsReport): string;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { nextStamp } from '../db/new.mjs';
|
|
5
|
+
import { MIGRATIONS_DIR } from '../runtime/scripts/hfs/rules/database-constants.mjs';
|
|
6
|
+
import { filesUnder, relativePosix } from './source-tree.mjs';
|
|
7
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
|
+
/** The owner of the account doors: the partial the lite baseline includes, byte for byte. */
|
|
9
|
+
const DOORS_PARTIAL = path.join(PACKAGE_ROOT, 'templates', 'app', 'auth-doors.sql.partial');
|
|
10
|
+
/** A grant to anyone on schema auth or on a table in it. */
|
|
11
|
+
const AUTH_GRANT = /\bgrant\b[^;]*?\bon\b\s+(?:schema\s+auth\b|(?:table\s+)?auth\.)/iu;
|
|
12
|
+
const EXPAND_NAME = 'account-doors';
|
|
13
|
+
const CONTRACT_NAME = 'contract_revoke-auth-grants';
|
|
14
|
+
const SOURCE = /\.[cm]?tsx?$/iu;
|
|
15
|
+
const ENTITY_FILES = ['auth-user.entity.ts', 'auth-identity.entity.ts'];
|
|
16
|
+
/** The argument types of a signature, the `p_` parameter names dropped and case and spacing folded: a door is known by its name and types alone. */
|
|
17
|
+
const typesOf = (args) => args.split(',').map((arg) => arg.trim().replace(/^p_\w+\s+/u, '').replace(/\s+/gu, ' ').toLowerCase()).join(', ');
|
|
18
|
+
/** The partial split into its head (the comment and the index) and its doors, in the partial's order. */
|
|
19
|
+
function partialParts(text) {
|
|
20
|
+
const lines = text.split('\n');
|
|
21
|
+
const doors = [...text.matchAll(/^(?:-- [^\n]*\n)?CREATE FUNCTION public\.(\w+)\(([^)]*)\)[\s\S]*?\$\$;\n/gmu)].map((match) => {
|
|
22
|
+
const own = lines.filter((line) => /^(?:REVOKE|GRANT) EXECUTE ON FUNCTION public\.(\w+)\(/u.exec(line)?.[1] === match[1] && typesOf(/\(([^)]*)\)/u.exec(line)[1]) === typesOf(match[2]));
|
|
23
|
+
return { name: match[1], types: typesOf(match[2]), block: match[0], grants: `${own.join('\n')}\n` };
|
|
24
|
+
});
|
|
25
|
+
return { head: text.slice(0, text.indexOf(doors[0].block)), doors };
|
|
26
|
+
}
|
|
27
|
+
/** The doors a text defines, as name(types) keys. */
|
|
28
|
+
const definedDoors = (statements) => [...statements.matchAll(/create\s+(?:or\s+replace\s+)?function\s+public\.(\w+)\s*\(([^)]*)\)/giu)].map((match) => `${match[1].toLowerCase()}(${typesOf(match[2])})`);
|
|
29
|
+
const migrationsOf = (root) => {
|
|
30
|
+
const dir = path.join(root, MIGRATIONS_DIR);
|
|
31
|
+
return fs.existsSync(dir) ? fs.readdirSync(dir).filter((name) => name.endsWith('.sql')).sort() : [];
|
|
32
|
+
};
|
|
33
|
+
const textOf = (root, name) => fs.readFileSync(path.join(root, MIGRATIONS_DIR, name), 'utf8');
|
|
34
|
+
/** The statements of a migration, its line comments left out. */
|
|
35
|
+
const statementsOf = (root, name) => textOf(root, name).replace(/--[^\n]*/gu, '');
|
|
36
|
+
/** The application role hfs.json declares (`db.appRole`), app_be by default. */
|
|
37
|
+
function appRoleOf(root) {
|
|
38
|
+
const role = JSON.parse(fs.readFileSync(path.join(root, 'hfs.json'), 'utf8')).db?.appRole;
|
|
39
|
+
return typeof role === 'string' && /^[a-z_][a-z0-9_]*$/u.test(role) ? role : 'app_be';
|
|
40
|
+
}
|
|
41
|
+
/** The be source that still maps or names the auth tables: an entity of schema auth, or the retired entity files. */
|
|
42
|
+
function accountTableReferences(root) {
|
|
43
|
+
const be = path.join(root, 'be', 'src');
|
|
44
|
+
if (!fs.existsSync(be))
|
|
45
|
+
return [];
|
|
46
|
+
return filesUnder(be, SOURCE).map((file) => relativePosix(root, file)).filter((file) => {
|
|
47
|
+
const retired = ENTITY_FILES.some((name) => file.endsWith(`/${name}`));
|
|
48
|
+
return retired || /schema:\s*["']auth["']/u.test(fs.readFileSync(path.join(root, file), 'utf8'));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function contractText(role) {
|
|
52
|
+
return [
|
|
53
|
+
'-- contract: the back end reaches auth.users and auth.identities through the account doors (public.be_auth_*), so the application role',
|
|
54
|
+
'-- gives up its direct grants. The release before the doors still reads those tables, so a rollback to it after the accept needs them granted again.',
|
|
55
|
+
`revoke select, insert, update on table auth.users from ${role};`,
|
|
56
|
+
`revoke select, insert on table auth.identities from ${role};`,
|
|
57
|
+
`revoke usage on schema auth from ${role};`,
|
|
58
|
+
'',
|
|
59
|
+
].join('\n');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Upgrade of a lite app that signs in over direct grants on auth.users and auth.identities to the account doors. The expand step (default)
|
|
63
|
+
* writes the migration that creates the doors the app lacks, with the text the lite baseline includes; the app then runs `starci app sync --execute`
|
|
64
|
+
* (the sign-in glue switches to the doors and the two entity files go). The contract step (`contract`) writes the migration that revokes the three
|
|
65
|
+
* grants, once the doors exist and no be source maps the auth tables; R255 reports the grants until that migration is in the chain. Nothing is written while a case is left for a person, and a step that is already
|
|
66
|
+
* done changes nothing.
|
|
67
|
+
*/
|
|
68
|
+
export function migrateAccountDoors({ root, write = false, contract = false, now = Date.now }) {
|
|
69
|
+
const phase = contract ? 'contract' : 'expand';
|
|
70
|
+
const report = { schema: 'starci/account-doors-report@1', root, write, phase, changes: [], findings: [], next: [] };
|
|
71
|
+
const declaration = JSON.parse(fs.readFileSync(path.join(root, 'hfs.json'), 'utf8'));
|
|
72
|
+
if (declaration.edition !== 'lite') {
|
|
73
|
+
report.findings.push({ file: 'hfs.json', reason: 'the account doors belong to the lite edition; a full app signs in through its identity provider' });
|
|
74
|
+
return report;
|
|
75
|
+
}
|
|
76
|
+
const names = migrationsOf(root);
|
|
77
|
+
const parts = partialParts(fs.readFileSync(DOORS_PARTIAL, 'utf8'));
|
|
78
|
+
const defined = new Set(names.flatMap((name) => definedDoors(statementsOf(root, name))));
|
|
79
|
+
const missing = parts.doors.filter((door) => !defined.has(`${door.name}(${door.types})`));
|
|
80
|
+
const stamp = nextStamp(names, now());
|
|
81
|
+
if (!contract) {
|
|
82
|
+
if (!missing.length)
|
|
83
|
+
return report;
|
|
84
|
+
const file = `${MIGRATIONS_DIR}/${stamp}_${EXPAND_NAME}.sql`;
|
|
85
|
+
const kept = missing.length === parts.doors.length ? 'all' : missing.map((door) => door.name).join(', ');
|
|
86
|
+
report.changes.push({ file, phase, why: `creates the account doors the app lacks (${kept}; SECURITY DEFINER, pinned search_path, EXECUTE for the application role alone) and the lower(email) index the by-address door reads` });
|
|
87
|
+
report.next.push('run `npx starci app sync --execute` (the sign-in glue moves onto the doors and the retired entity files are deleted), then verify and release both together', 'then run `npx starci app codemod account-doors --contract --execute` to revoke the old grants (R255 reports them until that migration is in the chain) and release the three together');
|
|
88
|
+
const text = `${parts.head}${missing.map((door) => door.block).join('\n')}\n${missing.map((door) => door.grants).join('')}`;
|
|
89
|
+
if (write)
|
|
90
|
+
fs.writeFileSync(path.join(root, file), text, { flag: 'wx' });
|
|
91
|
+
return report;
|
|
92
|
+
}
|
|
93
|
+
if (missing.length)
|
|
94
|
+
report.findings.push({ file: MIGRATIONS_DIR, reason: `no migration defines the account doors ${missing.map((door) => door.name).join(', ')}: run the expand step first` });
|
|
95
|
+
for (const file of accountTableReferences(root)) {
|
|
96
|
+
report.findings.push({ file, reason: 'still maps or names the auth tables; run `npx starci app sync --execute` so the sign-in glue uses the doors, and release that before the contract migration' });
|
|
97
|
+
}
|
|
98
|
+
if (report.findings.length)
|
|
99
|
+
return report;
|
|
100
|
+
if (names.some((name) => name.includes(`_${CONTRACT_NAME}`)) || !names.some((name) => AUTH_GRANT.test(statementsOf(root, name))))
|
|
101
|
+
return report;
|
|
102
|
+
const file = `${MIGRATIONS_DIR}/${stamp}_${CONTRACT_NAME}.sql`;
|
|
103
|
+
report.changes.push({ file, phase, why: 'revokes the direct grants on schema auth, auth.users and auth.identities from the application role' });
|
|
104
|
+
report.next.push('the deploy applies it right after the accept; the release before the doors cannot sign in after a rollback unless the grants are given back');
|
|
105
|
+
if (write)
|
|
106
|
+
fs.writeFileSync(path.join(root, file), contractText(appRoleOf(root)), { flag: 'wx' });
|
|
107
|
+
return report;
|
|
108
|
+
}
|
|
109
|
+
const cell = (value) => String(value).replaceAll('|', '\\|').replaceAll('\n', ' ');
|
|
110
|
+
/** Render the migrations and what is left for a person as Markdown; JSON consumers read the same report object. */
|
|
111
|
+
export function accountDoorsMarkdown(report) {
|
|
112
|
+
const out = ['# account-doors', '', `Step ${report.phase}: ${report.changes.length} migration${report.changes.length === 1 ? '' : 's'} ${report.write ? 'written' : 'to write'}, ${report.findings.length} left for a person.`, ''];
|
|
113
|
+
if (report.findings.length) {
|
|
114
|
+
out.push('## Left for a person', '', '| location | reason |', '|---|---|');
|
|
115
|
+
for (const finding of report.findings)
|
|
116
|
+
out.push(`| ${cell(finding.file)} | ${cell(finding.reason)} |`);
|
|
117
|
+
out.push('');
|
|
118
|
+
}
|
|
119
|
+
if (report.changes.length) {
|
|
120
|
+
out.push('## Migrations', '', '| file | step | why |', '|---|---|---|');
|
|
121
|
+
for (const change of report.changes)
|
|
122
|
+
out.push(`| ${cell(change.file)} | ${change.phase} | ${cell(change.why)} |`);
|
|
123
|
+
out.push('');
|
|
124
|
+
}
|
|
125
|
+
if (report.next.length)
|
|
126
|
+
out.push('## Next', '', ...report.next.map((line) => `- ${line}`), '');
|
|
127
|
+
return `${out.join('\n')}\n`;
|
|
128
|
+
}
|
package/codemod/main.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { accountDoorsMarkdown, migrateAccountDoors } from './account-doors.mjs';
|
|
3
4
|
import { codemodMain as buttonIntentMain } from './button-intent.mjs';
|
|
4
5
|
import { migratePageNavigation, pageNavigationMarkdown } from './page-navigation.mjs';
|
|
5
6
|
import { loadingStatesMarkdown, migrateLoadingStates } from './loading-states.mjs';
|
|
@@ -9,10 +10,14 @@ import { migrateSpacingRoles, spacingRolesMarkdown } from './spacing-roles.mjs';
|
|
|
9
10
|
import { migrateSurfaceCardKinds, surfaceCardKindsMarkdown } from './surface-card-kinds.mjs';
|
|
10
11
|
import { explicitSurfaceMarkdown, migrateExplicitSurface } from './explicit-surface.mjs';
|
|
11
12
|
import { directiveFirstMarkdown, migrateDirectiveFirst } from './directive-first.mjs';
|
|
13
|
+
import { migrateModuleDefinition, moduleDefinitionMarkdown } from './module-definition.mjs';
|
|
14
|
+
import { migrateQualityBinding, qualityBindingMarkdown } from './quality-binding.mjs';
|
|
15
|
+
import { migrateSopsRules, sopsRulesMarkdown } from './sops-rules.mjs';
|
|
16
|
+
import { migrateSecretsCache, secretsCacheMarkdown } from './secrets-cache.mjs';
|
|
12
17
|
import { migrateThemeTokens, themeTokensMarkdown } from './theme-tokens.mjs';
|
|
13
18
|
import { migrateTierBinding, tierBindingMarkdown } from './tier-binding.mjs';
|
|
14
19
|
/** The codemods `starci app codemod` runs, by name. */
|
|
15
|
-
export const CODEMODS = Object.freeze(['button-intent', 'spacing-roles', 'theme-tokens', 'small-renames', 'page-navigation', 'section-frame', 'surface-card-kinds', 'tier-binding', 'loading-states', 'explicit-surface', 'directive-first']);
|
|
20
|
+
export const CODEMODS = Object.freeze(['button-intent', 'spacing-roles', 'theme-tokens', 'small-renames', 'page-navigation', 'section-frame', 'surface-card-kinds', 'tier-binding', 'loading-states', 'explicit-surface', 'directive-first', 'secrets-cache', 'module-definition', 'sops-rules', 'quality-binding', 'account-doors']);
|
|
16
21
|
/** The arguments of every report-style `starci app codemod`: --execute, --json, --format and --out; any other is refused. */
|
|
17
22
|
export function spacingRolesArguments(argv) {
|
|
18
23
|
const options = { write: false, format: 'markdown' };
|
|
@@ -46,6 +51,22 @@ export function spacingRolesArguments(argv) {
|
|
|
46
51
|
}
|
|
47
52
|
/** One of the report-style codemods run at an app root: its report object and the Markdown of it. */
|
|
48
53
|
function runCodemod(name, root, write) {
|
|
54
|
+
if (name === 'quality-binding') {
|
|
55
|
+
const report = migrateQualityBinding({ root, write });
|
|
56
|
+
return { report, markdown: qualityBindingMarkdown(report) };
|
|
57
|
+
}
|
|
58
|
+
if (name === 'sops-rules') {
|
|
59
|
+
const report = migrateSopsRules({ root, write });
|
|
60
|
+
return { report, markdown: sopsRulesMarkdown(report) };
|
|
61
|
+
}
|
|
62
|
+
if (name === 'module-definition') {
|
|
63
|
+
const report = migrateModuleDefinition({ root, write });
|
|
64
|
+
return { report, markdown: moduleDefinitionMarkdown(report) };
|
|
65
|
+
}
|
|
66
|
+
if (name === 'secrets-cache') {
|
|
67
|
+
const report = migrateSecretsCache({ root, write });
|
|
68
|
+
return { report, markdown: secretsCacheMarkdown(report) };
|
|
69
|
+
}
|
|
49
70
|
if (name === 'directive-first') {
|
|
50
71
|
const report = migrateDirectiveFirst({ root, write });
|
|
51
72
|
return { report, markdown: directiveFirstMarkdown(report) };
|
|
@@ -105,6 +126,13 @@ async function explicitSurfaceMain(argv, io) {
|
|
|
105
126
|
const report = await migrateExplicitSurface({ root: path.resolve(io.cwd), write: options.write });
|
|
106
127
|
return emit(options, { report, markdown: explicitSurfaceMarkdown(report) }, io);
|
|
107
128
|
}
|
|
129
|
+
/** `starci app codemod account-doors [--contract]`: the expand migration of the account doors, or (a release later) the contract migration that revokes the old grants. */
|
|
130
|
+
function accountDoorsMain(argv, io) {
|
|
131
|
+
const contract = argv.includes('--contract');
|
|
132
|
+
const options = reportArguments(argv.filter((arg) => arg !== '--contract'));
|
|
133
|
+
const report = migrateAccountDoors({ root: path.resolve(io.cwd), write: options.write, contract });
|
|
134
|
+
return emit(options, { report, markdown: accountDoorsMarkdown(report) }, io);
|
|
135
|
+
}
|
|
108
136
|
/**
|
|
109
137
|
* CLI boundary of `starci app codemod <name>`. Dry run by default; --execute writes only the safe rewrites. A codemod
|
|
110
138
|
* that leaves a case for a person exits 1 with the complete report.
|
|
@@ -115,6 +143,8 @@ export function codemodMain(argv, io) {
|
|
|
115
143
|
return buttonIntentMain(argv, io);
|
|
116
144
|
if (name === 'explicit-surface')
|
|
117
145
|
return explicitSurfaceMain(rest, io);
|
|
146
|
+
if (name === 'account-doors')
|
|
147
|
+
return accountDoorsMain(rest, io);
|
|
118
148
|
if (name === undefined || !CODEMODS.includes(name))
|
|
119
149
|
throw new Error(`starci app codemod takes ${CODEMODS.join(' or ')}`);
|
|
120
150
|
const options = reportArguments(rest);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** The TypeScript compiler API of the app's own install (loadTypescript), typed by the version this package develops against. */
|
|
2
|
+
type Ts = typeof import('typescript');
|
|
3
|
+
/** One definition the migration rewrites: which form it was written in, the helper it became and the names it exports. */
|
|
4
|
+
export interface ModuleDefinitionChange {
|
|
5
|
+
file: string;
|
|
6
|
+
from: 'destructured builder' | 'typed builder';
|
|
7
|
+
helper: string;
|
|
8
|
+
names: string[];
|
|
9
|
+
}
|
|
10
|
+
/** One case left for a person. */
|
|
11
|
+
export interface ModuleDefinitionFinding {
|
|
12
|
+
file: string;
|
|
13
|
+
line: number;
|
|
14
|
+
code: string;
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
/** The report of a migration (`starci app codemod module-definition`). */
|
|
18
|
+
export interface ModuleDefinitionReport {
|
|
19
|
+
schema: string;
|
|
20
|
+
root: string;
|
|
21
|
+
write: boolean;
|
|
22
|
+
filesScanned: number;
|
|
23
|
+
filesChanged: string[];
|
|
24
|
+
changes: ModuleDefinitionChange[];
|
|
25
|
+
findings: ModuleDefinitionFinding[];
|
|
26
|
+
/** The `@starci/nest-kit` requirement of the app's root package.json: added by `--execute` when a definition changed, then `npm install` resolves it. */
|
|
27
|
+
dependency: {
|
|
28
|
+
name: string;
|
|
29
|
+
version: string;
|
|
30
|
+
declared: boolean;
|
|
31
|
+
added: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Rewrite one definition file's text to the helper form; `text` unchanged when the file holds no definition the codemod can rewrite. */
|
|
35
|
+
export declare function transformModuleDefinition({ ts, text, file }: {
|
|
36
|
+
ts: Ts;
|
|
37
|
+
text: string;
|
|
38
|
+
file?: string;
|
|
39
|
+
}): {
|
|
40
|
+
text: string;
|
|
41
|
+
changes: ModuleDefinitionChange[];
|
|
42
|
+
findings: ModuleDefinitionFinding[];
|
|
43
|
+
};
|
|
44
|
+
/** Preview or apply the rewrite in one app: every `*.module-definition.ts` under its sides, then the dependency in the root package.json. */
|
|
45
|
+
export declare function migrateModuleDefinition({ root, write, ts }: {
|
|
46
|
+
root: string;
|
|
47
|
+
write?: boolean;
|
|
48
|
+
ts?: Ts | null;
|
|
49
|
+
}): ModuleDefinitionReport;
|
|
50
|
+
/** Render the rewrite and what is left for a person as Markdown; JSON consumers read the same report object. */
|
|
51
|
+
export declare function moduleDefinitionMarkdown(report: ModuleDefinitionReport): string;
|
|
52
|
+
export {};
|