@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/sync/index.mjs
CHANGED
|
@@ -34,6 +34,7 @@ import { DB_DOCKERFILE, DB_IMAGE_APP, RETIRED_DB_DOCKERFILE } from '../db/image.
|
|
|
34
34
|
import { byCodeUnit } from '../report/order.mjs';
|
|
35
35
|
import { isRuntimeExampleApp } from '../runtime/scripts/lib/runtime-example.mjs';
|
|
36
36
|
import { ciWorkflowCommands } from './ci-commands.mjs';
|
|
37
|
+
import { MODULE_PREFIX, moduleNames, registryFiles } from '../scaffold/module-registry.mjs';
|
|
37
38
|
import { dependabotUpdates, hasDeclaredServices, readDeclaredQuality, readDeclaredBadges, sonarHostLine } from './quality-services.mjs';
|
|
38
39
|
import { qualityServices } from '../runtime/scripts/hfs/ci-declaration.mjs';
|
|
39
40
|
import { grammarLintFor } from '../runtime/scripts/lib/grammar-lint.mjs';
|
|
@@ -236,6 +237,9 @@ export function sonarScope(presets) {
|
|
|
236
237
|
const tests = preset.filter(glob => !glob.endsWith('/**'));
|
|
237
238
|
return { tests, exclusions: [...new Set([...preset, '**/.next/**', '**/node_modules/**'])] };
|
|
238
239
|
}
|
|
240
|
+
/** The managed ESLint config at the app root that lints the root workspace packages. */
|
|
241
|
+
export const PACKAGES_LINT_FILE = 'eslint.config.mjs';
|
|
242
|
+
const LINE = '\n';
|
|
239
243
|
const SOURCE_FILE = /\.[cm]?ts$/;
|
|
240
244
|
const SOURCE_SKIPPED = new Set(['node_modules', 'dist', 'coverage']);
|
|
241
245
|
/**
|
|
@@ -244,6 +248,16 @@ const SOURCE_SKIPPED = new Set(['node_modules', 'dist', 'coverage']);
|
|
|
244
248
|
* lite brand tree exists, so sync can preserve its absence. `hasImage` identifies authored optional Dockerfiles without reading
|
|
245
249
|
* their content. An app with no be folder has no source files.
|
|
246
250
|
*/
|
|
251
|
+
/** The folders of `packages/` that are neither a module package nor the registry. */
|
|
252
|
+
function otherPackages(root) {
|
|
253
|
+
try {
|
|
254
|
+
return fs.readdirSync(path.join(root, 'packages'), { withFileTypes: true })
|
|
255
|
+
.filter(entry => entry.isDirectory() && !entry.name.startsWith(MODULE_PREFIX) && entry.name !== 'modules').map(entry => entry.name).sort(byCodeUnit);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
247
261
|
export function appSource(root, { parseYaml, notices } = {}) {
|
|
248
262
|
const files = [];
|
|
249
263
|
const walk = (dir) => {
|
|
@@ -267,6 +281,7 @@ export function appSource(root, { parseYaml, notices } = {}) {
|
|
|
267
281
|
for (const dir of ['apps', 'src'])
|
|
268
282
|
walk(dir);
|
|
269
283
|
return { runtimeExample: isRuntimeExampleApp(root), files: files.sort(byCodeUnit), read: (file) => fs.readFileSync(path.join(root, 'be', file), 'utf8'),
|
|
284
|
+
modules: moduleNames(root), otherPackages: otherPackages(root),
|
|
270
285
|
workPresent: fs.existsSync(path.join(root, '.starciwork')), hasImage: (file) => isFile(path.join(root, file), { followLinks: false }),
|
|
271
286
|
stack: readDeclaredStack(root, { parseYaml }), quality: readDeclaredQuality(root, { parseYaml }),
|
|
272
287
|
badges: readDeclaredBadges(root, { parseYaml, notices }),
|
|
@@ -321,6 +336,7 @@ export function appScripts(app, source) {
|
|
|
321
336
|
}),
|
|
322
337
|
...fe.map(entry => line(`start:${entry.name}`, `npm run start -w ${workspace(entry)}`)),
|
|
323
338
|
...(app.edition !== 'lite' && app.optionalSlots.includes('app.browser') ? [line('test:browser', 'playwright test -c browser/playwright.config.ts')] : []),
|
|
339
|
+
...(source?.modules?.length ? [line('build:modules', 'turbo run build --filter=./packages/*'), line('test:modules', 'turbo run test --filter=./packages/module-*')] : []),
|
|
324
340
|
line('docker:build', images.map(image => `npm run docker:build:${image.entry.name}`).join(' && ')),
|
|
325
341
|
...images.map(image => line(`docker:build:${image.entry.name}`, imageCommand(image))),
|
|
326
342
|
].join('\n ');
|
|
@@ -377,6 +393,36 @@ export function imageFiles(app, source) {
|
|
|
377
393
|
});
|
|
378
394
|
}
|
|
379
395
|
/** Every value a template of `scope` can name, derived from hfs.json and the presets. */
|
|
396
|
+
/** What a module package keeps out of the measured scope: its workbench (drawn with Grammar, a front end has no tests) and its specs. The logic of its template and manifest is measured. */
|
|
397
|
+
const moduleExclusions = (dirs) => dirs.flatMap(dir => [`${dir}/src/workbench/**`, `${dir}/src/**/index.ts`]);
|
|
398
|
+
/** The managed files of the registry package for the module packages the app has (empty when it has none). */
|
|
399
|
+
function registryTargets(app, source) {
|
|
400
|
+
return registryFiles(app.project, source?.modules ?? []).map((file) => ({ path: file.path, scope: APP_SCOPE, mode: 'file', content: file.content, hash: hashOf(file.content) }));
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* The root `eslint.config.mjs` of an app that has root workspace packages (packages/agent-sdk, packages/module-<name>, the registry): no side config
|
|
404
|
+
* reaches `packages/`, so this managed file lints their source by subpath. The back-end canon judges everything but the front-end subpaths
|
|
405
|
+
* (`src/workbench/` outside its contract, `src/public/`), the front-end canon and the selected Grammar family those, each over the package view of
|
|
406
|
+
* the app root. An app without a root package has no such file; the family factory for packages is the family's `<name>GrammarPackagesConfig`.
|
|
407
|
+
*/
|
|
408
|
+
function packagesLintTargets(app, source) {
|
|
409
|
+
if (!(source?.modules?.length || source?.otherPackages?.length))
|
|
410
|
+
return [];
|
|
411
|
+
const grammar = grammarLintFor(app.sides.fe.grammar);
|
|
412
|
+
const factory = grammar.package ? grammar.factory.replace(/Config$/, 'PackagesConfig') : null;
|
|
413
|
+
const content = [
|
|
414
|
+
'import { loadPackagesHfs as loadBePackages, starciBePackagesConfig } from "@starci/eslint-canon-be"',
|
|
415
|
+
'import { loadPackagesHfs as loadFePackages, starciFePackagesConfig } from "@starci/eslint-canon-fe"',
|
|
416
|
+
...(grammar.package ? [`import { ${factory} } from "${grammar.package}"`] : []),
|
|
417
|
+
'',
|
|
418
|
+
'const be = loadBePackages(import.meta.url)',
|
|
419
|
+
'const fe = loadFePackages(import.meta.url)',
|
|
420
|
+
'',
|
|
421
|
+
`export default [...(await starciBePackagesConfig({ hfs: be })), ...starciFePackagesConfig({ hfs: fe })${grammar.package ? `, ...${factory}({ hfs: fe })` : ''}]`,
|
|
422
|
+
'',
|
|
423
|
+
].join(LINE);
|
|
424
|
+
return [{ path: PACKAGES_LINT_FILE, scope: APP_SCOPE, mode: 'file', content, hash: hashOf(content) }];
|
|
425
|
+
}
|
|
380
426
|
export function variables(app, scope, presets, sonarKey, manifest = loadSlotManifest(), source = { files: [], read: () => '' }) {
|
|
381
427
|
const stack = source.stack ?? { images: [], passEnv: [] };
|
|
382
428
|
const fe = app.sides.fe;
|
|
@@ -384,6 +430,10 @@ export function variables(app, scope, presets, sonarKey, manifest = loadSlotMani
|
|
|
384
430
|
const lite = app.edition === 'lite';
|
|
385
431
|
const providers = (app.sides.be.connections ?? []).map(connection => connection.provider);
|
|
386
432
|
const quality = source.quality ?? qualityServices({});
|
|
433
|
+
const modules = source.modules ?? [];
|
|
434
|
+
const moduleDirs = modules.map(name => `packages/${MODULE_PREFIX}${name}`);
|
|
435
|
+
// The module packages are built before anything imports them (typecheck, lint, the fe build and the be run all follow the registry).
|
|
436
|
+
const modulesFirst = modules.length ? 'npm run build:modules --silent && ' : '';
|
|
387
437
|
const grammarLint = scope === 'fe' ? grammarLintFor(fe.grammar) : null;
|
|
388
438
|
return {
|
|
389
439
|
header: HEADER(scope),
|
|
@@ -401,11 +451,14 @@ export function variables(app, scope, presets, sonarKey, manifest = loadSlotMani
|
|
|
401
451
|
// A lite app's declared Redis (sides.be.backing, ruling:R212/case-10) runs in development as one container of its pinned image.
|
|
402
452
|
backingScripts: lite && (app.sides.be.backing ?? []).includes('redis') ? redisDevText(app.project) : '',
|
|
403
453
|
// turbo builds every fe app workspace; ^build (turbo.json) builds the packages each app imports first, and caches both.
|
|
404
|
-
buildFe:
|
|
454
|
+
buildFe: `${modulesFirst}npm run codegen --silent && turbo run build --filter=./fe/apps/*`,
|
|
405
455
|
// The be side with its tsc; every fe workspace with its own typecheck script through turbo (the packages are built first: ^build).
|
|
406
|
-
typecheck:
|
|
456
|
+
typecheck: `${modulesFirst}npm run codegen --silent && tsc -p be/tsconfig.json && turbo run typecheck`,
|
|
407
457
|
// The typed lint reads the types the fe packages declare (their dist), so the packages are built first.
|
|
408
|
-
lintPrefix: packages ? 'npm run codegen --silent && turbo run build --filter=./fe/packages/* && ' : 'npm run codegen --silent && '
|
|
458
|
+
lintPrefix: `${modulesFirst}${packages ? 'npm run codegen --silent && turbo run build --filter=./fe/packages/* && ' : 'npm run codegen --silent && '}`,
|
|
459
|
+
// The unit run, the back-end build and every CI job that imports a module package find it built (build:modules precedes them); the logic of a package is held at 100 per file by its own jest run, a step of the fast job.
|
|
460
|
+
modulesFirst,
|
|
461
|
+
moduleTestStep: modules.length ? ' - name: module packages\n run: npm run test:modules\n' : '',
|
|
409
462
|
nodeMajor: String(NODE_MAJOR),
|
|
410
463
|
// The deploy server's age identity the restore workflow writes (stack/server-key.mjs owns the path).
|
|
411
464
|
serverAgeIdentity: SERVER_AGE_IDENTITY,
|
|
@@ -415,19 +468,19 @@ export function variables(app, scope, presets, sonarKey, manifest = loadSlotMani
|
|
|
415
468
|
sonarHostLine: sonarHostLine(quality.sonar),
|
|
416
469
|
sonarExclusions: sonarScope(presets).exclusions.join(','),
|
|
417
470
|
sonarTestInclusions: sonarScope(presets).tests.join(','),
|
|
418
|
-
sonarSources: ['be/apps', 'be/src', 'fe/apps', ...(packages ? ['fe/packages'] : [])].join(','),
|
|
471
|
+
sonarSources: ['be/apps', 'be/src', 'fe/apps', ...(packages ? ['fe/packages'] : []), ...moduleDirs.map(dir => `${dir}/src`)].join(','),
|
|
419
472
|
lcovReport: LCOV_REPORT,
|
|
420
473
|
cpdExclusions: SONAR_CPD_EXCLUSIONS.join(','),
|
|
421
474
|
...ciWorkflowCommands(source, LCOV_REPORT, source.qualityDeclared === false ? undefined : source.quality, { app, packages }),
|
|
422
475
|
// The coverage scope is the same in both editions: lite measures exactly what full measures (ruling:R212/case-1, amended by ruling:R212/case-4).
|
|
423
476
|
jestCoverage: scope === 'be' ? jestCoverageSource(jestCoverage(manifest, providers)) : '',
|
|
424
|
-
coverageExclusions: scope === APP_SCOPE ? sonarCoverageExclusions(manifest, providers).join(',') : '',
|
|
425
|
-
codecovExclusions: scope === APP_SCOPE ? sonarCoverageExclusions(manifest, providers).map(glob => ` - ${JSON.stringify(glob)}`).join('\n') : '',
|
|
426
|
-
codecovPaths: scope === APP_SCOPE ? codecovPaths(manifest, providers).map(glob => ` - ${JSON.stringify(glob)}`).join('\n') : '',
|
|
477
|
+
coverageExclusions: scope === APP_SCOPE ? [...sonarCoverageExclusions(manifest, providers), ...moduleExclusions(moduleDirs)].join(',') : '',
|
|
478
|
+
codecovExclusions: scope === APP_SCOPE ? [...sonarCoverageExclusions(manifest, providers), ...moduleExclusions(moduleDirs)].map(glob => ` - ${JSON.stringify(glob)}`).join('\n') : '',
|
|
479
|
+
codecovPaths: scope === APP_SCOPE ? [...codecovPaths(manifest, providers), ...moduleDirs.flatMap(dir => [`${dir}/src/manifest/**`, `${dir}/src/template/**`])].map(glob => ` - ${JSON.stringify(glob)}`).join('\n') : '',
|
|
427
480
|
codecovComponents: scope === APP_SCOPE ? componentsYaml(coverageComponents(manifest, { ...source, apps: app.sides.be.apps, providers })) : '',
|
|
428
481
|
codecovTarget: quality.codecov.target,
|
|
429
482
|
dependabotUpdates: dependabotUpdates(quality.dependabot),
|
|
430
|
-
tsconfigPaths: ['be/tsconfig.json', ...fe.apps.map(entry => `fe/apps/${entry.name}/tsconfig.json`), ...(packages ? ['fe/packages/*/tsconfig.json'] : [])].join(','),
|
|
483
|
+
tsconfigPaths: ['be/tsconfig.json', ...fe.apps.map(entry => `fe/apps/${entry.name}/tsconfig.json`), ...(packages ? ['fe/packages/*/tsconfig.json'] : []), ...moduleDirs.map(dir => `${dir}/tsconfig.json`)].join(','),
|
|
431
484
|
styleGlob: STYLE_GLOB,
|
|
432
485
|
grammarLintImport: grammarLint?.package ? `import { ${grammarLint.factory} } from "${grammarLint.package}"` : '',
|
|
433
486
|
grammarLintExport: grammarLint?.package ? `export default [...starciFeConfig({ hfs }), ...${grammarLint.factory}({ hfs })]` : 'export default starciFeConfig({ hfs })',
|
|
@@ -446,12 +499,24 @@ function parseScripts(text) {
|
|
|
446
499
|
return null;
|
|
447
500
|
}
|
|
448
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* The rendered targets of an app declaration. Without source facts the app counts the back-end source files its own targets
|
|
504
|
+
* render (the managed sign-in glue), as a check reading the disk after the targets are written does, so a render of a declaration
|
|
505
|
+
* alone is already in sync with itself.
|
|
506
|
+
*/
|
|
507
|
+
export function renderTargets(hfs, presets, options = {}) {
|
|
508
|
+
const first = renderOnce(hfs, presets, options);
|
|
509
|
+
if (options.source !== undefined)
|
|
510
|
+
return first;
|
|
511
|
+
const settled = settleSource({ files: [], read: () => '' }, first); // reason: the settled facts name only the rendered source files; every other fact stays absent as without source
|
|
512
|
+
return settled.files.length === 0 ? first : renderOnce(hfs, presets, { ...options, source: settled });
|
|
513
|
+
}
|
|
449
514
|
/**
|
|
450
515
|
* The managed content of every target of the app: [{ path, mode, content, hash, scope }], paths app-relative (a side's targets under
|
|
451
516
|
* its folder); a scripts target also carries `scripts`, the parsed block. `sonarKey` is the key the be side's stack declaration
|
|
452
517
|
* names; without one the key is the project.
|
|
453
518
|
*/
|
|
454
|
-
|
|
519
|
+
function renderOnce(hfs, presets, { sonarKey, readTemplate = readBundled, manifest = loadSlotManifest(), source } = {}) {
|
|
455
520
|
const app = validateHfs(hfs, manifest);
|
|
456
521
|
const hasTemplate = (name) => { try {
|
|
457
522
|
return readTemplate(name) !== undefined;
|
|
@@ -467,24 +532,24 @@ export function renderTargets(hfs, presets, { sonarKey, readTemplate = readBundl
|
|
|
467
532
|
declaredTargets.push({ path: 'README.md', mode: 'block', markdown: true, template: 'app/readme-badges/README.md' });
|
|
468
533
|
const targets = declaredTargets
|
|
469
534
|
.filter(target => !(app.edition === 'lite' && source?.workPresent === false && target.path === '.starciwork/.gitignore'));
|
|
470
|
-
return targets.map((target) => {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
535
|
+
return [...(scope === APP_SCOPE ? [...registryTargets(app, source), ...packagesLintTargets(app, source)] : []), ...targets.map((target) => {
|
|
536
|
+
const template = target.path === 'codecov.yml' && source?.quality?.codecov.enabled === false
|
|
537
|
+
? '{{header}}\n# Codecov is disabled in the manifest.\ncodecov:\n require_ci_to_pass: false\n'
|
|
538
|
+
: readTemplate(target.template);
|
|
539
|
+
const body = trailingNewline(render(template, vars, readTemplate));
|
|
540
|
+
const where = onSide(scope, target.path);
|
|
541
|
+
if (target.mode === 'scripts') {
|
|
542
|
+
const scripts = parseScripts(body);
|
|
543
|
+
if (!scripts)
|
|
544
|
+
throw new SyncError('HFS_SYNC_TEMPLATE_VARIABLE', `the rendered ${target.template} has no scripts object`);
|
|
545
|
+
const content = scriptsText(scripts);
|
|
546
|
+
return { path: where, scope, mode: 'scripts', content, hash: hashOf(content), scripts };
|
|
547
|
+
}
|
|
548
|
+
const markers = target.markdown ? { begin: '<!-- >>> starci app sync (managed block; do not edit) >>> -->', end: '<!-- <<< starci app sync <<< -->' } : { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
549
|
+
const spacing = target.markdown ? '\n' : '';
|
|
550
|
+
const managed = target.mode === 'block' ? `${markers.begin}\n${spacing}${trailingNewline(body)}${spacing}${markers.end}\n` : body;
|
|
551
|
+
return { path: where, scope, mode: target.mode, markdown: target.markdown, markers, content: managed, hash: hashOf(managed) };
|
|
552
|
+
})];
|
|
488
553
|
});
|
|
489
554
|
}
|
|
490
555
|
// The part of a file sync manages: all of it, all of it but the verb's section, the marked block, or the scripts block as text.
|
|
@@ -591,17 +656,38 @@ export function loadHfs(root, manifest = loadSlotManifest()) {
|
|
|
591
656
|
validateHfs(hfs, manifest);
|
|
592
657
|
return hfs;
|
|
593
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* The source facts as they stand once the rendered back-end source files are on disk: a managed `.ts` target of the be side counts as a
|
|
661
|
+
* file of the app (and reads as its render), so the CI shard count and the coverage scope that count or read the source name the same
|
|
662
|
+
* files before and after the targets are written, and one sync leaves the app in sync.
|
|
663
|
+
*/
|
|
664
|
+
export function settleSource(source, targets) {
|
|
665
|
+
const added = new Map();
|
|
666
|
+
for (const target of targets) {
|
|
667
|
+
const rel = target.scope === 'be' && target.path.startsWith('be/') ? target.path.slice('be/'.length) : '';
|
|
668
|
+
if (SOURCE_FILE.test(rel) && (rel.startsWith('apps/') || rel.startsWith('src/')) && !source.files.includes(rel))
|
|
669
|
+
added.set(rel, target.content);
|
|
670
|
+
}
|
|
671
|
+
if (added.size === 0)
|
|
672
|
+
return source;
|
|
673
|
+
return { ...source, files: [...source.files, ...added.keys()].sort(byCodeUnit), read: (file) => added.get(file) ?? source.read(file) };
|
|
674
|
+
}
|
|
594
675
|
/** The rendered targets of the app at `root`: its hfs.json, the Sonar key the be side's stack declaration names, the presets it installs. */
|
|
595
676
|
export async function renderRepo(root, { presets, parseYaml, notices, manifest = loadSlotManifest() } = {}) {
|
|
596
677
|
const hfs = loadHfs(root, manifest);
|
|
597
678
|
const sonarKey = await readDeclaredSonarKey(root, { parseYaml, fail: (message) => { throw new SyncError('HFS_SYNC_SONAR_KEY', message); } });
|
|
598
|
-
|
|
679
|
+
const installed = presets ?? await loadPresets(root);
|
|
680
|
+
const source = appSource(root, { parseYaml, notices });
|
|
681
|
+
const settled = settleSource(source, renderTargets(hfs, installed, { sonarKey, manifest, source }));
|
|
682
|
+
return { hfs, targets: renderTargets(hfs, installed, { sonarKey, manifest, source: settled }) };
|
|
599
683
|
}
|
|
600
684
|
/** The folders a lite app's sync retires (ruling lite-self-hosted-postgres-passport-minio): the generated Supabase row types nothing reads any more. */
|
|
601
685
|
const LITE_RETIRED_DIRS = Object.freeze(['supabase/types']);
|
|
602
686
|
/** The files a lite app's sync retires, each with the reason the dry run prints. */
|
|
603
687
|
const LITE_RETIRED_FILES = Object.freeze({
|
|
604
688
|
[RETIRED_DB_DOCKERFILE]: `the db image moved to ${DB_DOCKERFILE} (ruling lite-db-image-in-stacks)`,
|
|
689
|
+
'be/src/modules/domain/identity/persistence/entities/auth-user.entity.ts': 'the sign-in reaches accounts through the account doors (be/src/modules/domain/identity/persistence/account-doors.port.ts); the application role holds no grant on schema auth',
|
|
690
|
+
'be/src/modules/domain/identity/persistence/entities/auth-identity.entity.ts': 'the sign-in reaches accounts through the account doors (be/src/modules/domain/identity/persistence/account-doors.port.ts); the application role holds no grant on schema auth',
|
|
605
691
|
'.github/workflows/db-deploy.yml': 'the stack deploy migrates the database (a verified backup, the expand migrations before the services, the contract migrations after the accept; ruling app-cicd-auto-deploy)',
|
|
606
692
|
});
|
|
607
693
|
/** Why a retired file goes: a retired file's own reason, else the folder's. */
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
-- The account doors of a lite app: the only way app_be reaches auth.users and auth.identities (it holds no grant on schema auth). Eight
|
|
2
|
+
-- SECURITY DEFINER functions with a pinned search_path, EXECUTE revoked from public and the browser roles and granted to app_be alone: an
|
|
3
|
+
-- account comes back only for an exact address or id, a hash only for an exact address, and no door writes role or app metadata; the hash
|
|
4
|
+
-- is written only by a new account and by be_auth_set_password.
|
|
5
|
+
-- The text is the canon owner of the doors (templates/app/auth-doors.sql.partial); the back end calls them through persistence/account-doors.port.ts.
|
|
6
|
+
|
|
7
|
+
-- Looks up an address in lower case; the bootstrap index serves email as written, not lower(email).
|
|
8
|
+
create index if not exists users_email_lower_idx on auth.users (lower(email)) where is_anonymous = false;
|
|
9
|
+
|
|
10
|
+
-- A person by sign-in address (lower-cased), with the stored bcrypt hash so the back end can verify it.
|
|
11
|
+
CREATE FUNCTION public.be_auth_user_by_email(p_email text)
|
|
12
|
+
RETURNS TABLE(id uuid, email text, encrypted_password text, email_confirmed_at timestamp with time zone, disabled boolean)
|
|
13
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
14
|
+
SET search_path TO ''
|
|
15
|
+
AS $$
|
|
16
|
+
select u.id, u.email::text, u.encrypted_password::text, u.email_confirmed_at,
|
|
17
|
+
(u.deleted_at is not null or (u.banned_until is not null and u.banned_until > now()))
|
|
18
|
+
from auth.users u
|
|
19
|
+
where lower(u.email) = p_email and u.is_anonymous = false
|
|
20
|
+
order by u.created_at asc nulls last
|
|
21
|
+
limit 1;
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
-- One person by id.
|
|
25
|
+
CREATE FUNCTION public.be_auth_user_by_id(p_user uuid)
|
|
26
|
+
RETURNS TABLE(id uuid, email text, last_sign_in_at timestamp with time zone, meta jsonb, disabled boolean)
|
|
27
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
28
|
+
SET search_path TO ''
|
|
29
|
+
AS $$
|
|
30
|
+
select u.id, u.email::text, u.last_sign_in_at, coalesce(u.raw_user_meta_data, '{}'::jsonb),
|
|
31
|
+
(u.deleted_at is not null or (u.banned_until is not null and u.banned_until > now()))
|
|
32
|
+
from auth.users u where u.id = p_user;
|
|
33
|
+
$$;
|
|
34
|
+
|
|
35
|
+
-- The person a Google account is linked to, if any.
|
|
36
|
+
CREATE FUNCTION public.be_auth_user_by_google(p_provider_id text) RETURNS uuid
|
|
37
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
38
|
+
SET search_path TO ''
|
|
39
|
+
AS $$
|
|
40
|
+
select i.user_id from auth.identities i where i.provider = 'google' and i.provider_id = p_provider_id limit 1;
|
|
41
|
+
$$;
|
|
42
|
+
|
|
43
|
+
-- Creates a person; a null hash is an invited or Google-only account that has no password yet.
|
|
44
|
+
CREATE FUNCTION public.be_auth_create_user(p_id uuid, p_email text, p_hash text, p_confirmed boolean, p_meta jsonb, p_now timestamp with time zone)
|
|
45
|
+
RETURNS uuid
|
|
46
|
+
LANGUAGE sql VOLATILE SECURITY DEFINER
|
|
47
|
+
SET search_path TO ''
|
|
48
|
+
AS $$
|
|
49
|
+
insert into auth.users (instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, invited_at,
|
|
50
|
+
raw_app_meta_data, raw_user_meta_data, created_at, updated_at)
|
|
51
|
+
values ('00000000-0000-0000-0000-000000000000', p_id, 'authenticated', 'authenticated', p_email, p_hash,
|
|
52
|
+
case when p_confirmed then p_now end, case when p_hash is null and not p_confirmed then p_now end,
|
|
53
|
+
'{"provider":"email","providers":["email"]}'::jsonb, p_meta, p_now, p_now)
|
|
54
|
+
returning id;
|
|
55
|
+
$$;
|
|
56
|
+
|
|
57
|
+
-- Links a Google account to a person.
|
|
58
|
+
CREATE FUNCTION public.be_auth_link_google(p_user uuid, p_provider_id text, p_data jsonb, p_now timestamp with time zone) RETURNS void
|
|
59
|
+
LANGUAGE sql VOLATILE SECURITY DEFINER
|
|
60
|
+
SET search_path TO ''
|
|
61
|
+
AS $$
|
|
62
|
+
insert into auth.identities (provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at)
|
|
63
|
+
values (p_provider_id, p_user, p_data, 'google', p_now, p_now, p_now)
|
|
64
|
+
on conflict (provider_id, provider) do update set last_sign_in_at = p_now, updated_at = p_now;
|
|
65
|
+
$$;
|
|
66
|
+
|
|
67
|
+
-- Records a sign-in; a verified Google address also confirms the mailbox; profile keys only fill what the person has not set.
|
|
68
|
+
CREATE FUNCTION public.be_auth_touch_user(p_user uuid, p_now timestamp with time zone, p_confirm boolean, p_meta jsonb) RETURNS void
|
|
69
|
+
LANGUAGE sql VOLATILE SECURITY DEFINER
|
|
70
|
+
SET search_path TO ''
|
|
71
|
+
AS $$
|
|
72
|
+
update auth.users
|
|
73
|
+
set last_sign_in_at = p_now,
|
|
74
|
+
updated_at = p_now,
|
|
75
|
+
email_confirmed_at = case when p_confirm then coalesce(email_confirmed_at, p_now) else email_confirmed_at end,
|
|
76
|
+
raw_user_meta_data = p_meta || coalesce(raw_user_meta_data, '{}'::jsonb)
|
|
77
|
+
where id = p_user;
|
|
78
|
+
$$;
|
|
79
|
+
|
|
80
|
+
-- Stamps the confirmation of a mailbox (a spent confirmation link); a mailbox that is already confirmed keeps its date and the sign-in record is untouched.
|
|
81
|
+
CREATE FUNCTION public.be_auth_confirm_user(p_user uuid, p_now timestamp with time zone) RETURNS void
|
|
82
|
+
LANGUAGE sql VOLATILE SECURITY DEFINER
|
|
83
|
+
SET search_path TO ''
|
|
84
|
+
AS $$
|
|
85
|
+
update auth.users set email_confirmed_at = coalesce(email_confirmed_at, p_now), updated_at = p_now where id = p_user;
|
|
86
|
+
$$;
|
|
87
|
+
|
|
88
|
+
-- Sets a new password hash; when p_confirm is true (a spent reset link proved the mailbox) the address is confirmed too.
|
|
89
|
+
CREATE FUNCTION public.be_auth_set_password(p_user uuid, p_hash text, p_confirm boolean, p_now timestamp with time zone) RETURNS void
|
|
90
|
+
LANGUAGE sql VOLATILE SECURITY DEFINER
|
|
91
|
+
SET search_path TO ''
|
|
92
|
+
AS $$
|
|
93
|
+
update auth.users
|
|
94
|
+
set encrypted_password = p_hash,
|
|
95
|
+
email_confirmed_at = case when p_confirm then coalesce(email_confirmed_at, p_now) else email_confirmed_at end,
|
|
96
|
+
updated_at = p_now
|
|
97
|
+
where id = p_user;
|
|
98
|
+
$$;
|
|
99
|
+
|
|
100
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_user_by_email(text) FROM PUBLIC, anon, authenticated;
|
|
101
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_user_by_email(text) TO app_be;
|
|
102
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_user_by_id(uuid) FROM PUBLIC, anon, authenticated;
|
|
103
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_user_by_id(uuid) TO app_be;
|
|
104
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_user_by_google(text) FROM PUBLIC, anon, authenticated;
|
|
105
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_user_by_google(text) TO app_be;
|
|
106
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_create_user(uuid, text, text, boolean, jsonb, timestamp with time zone) FROM PUBLIC, anon, authenticated;
|
|
107
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_create_user(uuid, text, text, boolean, jsonb, timestamp with time zone) TO app_be;
|
|
108
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_link_google(uuid, text, jsonb, timestamp with time zone) FROM PUBLIC, anon, authenticated;
|
|
109
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_link_google(uuid, text, jsonb, timestamp with time zone) TO app_be;
|
|
110
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_touch_user(uuid, timestamp with time zone, boolean, jsonb) FROM PUBLIC, anon, authenticated;
|
|
111
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_touch_user(uuid, timestamp with time zone, boolean, jsonb) TO app_be;
|
|
112
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_confirm_user(uuid, timestamp with time zone) FROM PUBLIC, anon, authenticated;
|
|
113
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_confirm_user(uuid, timestamp with time zone) TO app_be;
|
|
114
|
+
REVOKE EXECUTE ON FUNCTION public.be_auth_set_password(uuid, text, boolean, timestamp with time zone) FROM PUBLIC, anon, authenticated;
|
|
115
|
+
GRANT EXECUTE ON FUNCTION public.be_auth_set_password(uuid, text, boolean, timestamp with time zone) TO app_be;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Unit run of the {{pascal}} module package: the logic of its template and manifest is measured at 100 per file. */
|
|
2
|
+
module.exports = {
|
|
3
|
+
testEnvironment: "node",
|
|
4
|
+
transform: { "^.+\.tsx?$": ["ts-jest", { tsconfig: "tsconfig.json", isolatedModules: true }] },
|
|
5
|
+
testMatch: ["<rootDir>/src/**/*.spec.ts"],
|
|
6
|
+
collectCoverageFrom: ["src/manifest/**/*.ts", "src/template/**/*.ts", "!src/**/*.spec.ts", "!src/**/index.ts"],
|
|
7
|
+
coverageThreshold: { "./src/manifest/**/*.ts": { lines: 100, branches: 100, functions: 100, statements: 100 }, "./src/template/**/*.ts": { lines: 100, branches: 100, functions: 100, statements: 100 } },
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { {{upper}}_VIEW_IDS, {{upper}}_WORKBENCH } from "./declaration"
|
|
2
|
+
|
|
3
|
+
describe("the {{pascal}} workbench declaration", () => {
|
|
4
|
+
it("declares one view per id, in strip order", () => {
|
|
5
|
+
expect({{upper}}_WORKBENCH.views.map((view) => view.id)).toEqual([...{{upper}}_VIEW_IDS])
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it("is keyed like its module", () => {
|
|
9
|
+
expect({{upper}}_WORKBENCH).toMatchObject({ key: "{{name}}", namespace: "{{name}}" })
|
|
10
|
+
})
|
|
11
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WorkbenchDeclaration } from "@{{project}}/agent-sdk/workbench/contract"
|
|
2
|
+
|
|
3
|
+
/** The views of the {{pascal}} workbench in strip order (`?tab=`), each labelled from the module's namespace. */
|
|
4
|
+
export const {{upper}}_VIEW_IDS: readonly ["overview"] = ["overview"]
|
|
5
|
+
|
|
6
|
+
/** The {{pascal}} workbench: one overview every member of the workspace opens; grow its views, permissions, operations and exposed reads here. */
|
|
7
|
+
export const {{upper}}_WORKBENCH: WorkbenchDeclaration = {
|
|
8
|
+
key: "{{name}}",
|
|
9
|
+
namespace: "{{name}}",
|
|
10
|
+
views: [{ id: "overview", labelKey: "tab_overview" }],
|
|
11
|
+
permissions: [],
|
|
12
|
+
operations: [],
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { {{upper}}_VIEW_IDS, {{upper}}_WORKBENCH } from "./declaration"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { moduleListProblems } from "@{{project}}/agent-sdk/module"
|
|
2
|
+
import { {{upper}}_SERVER, {{upper}}_TEMPLATE } from "./{{name}}.template"
|
|
3
|
+
|
|
4
|
+
describe("{{upper}}_SERVER", () => {
|
|
5
|
+
it("is a module list the host may load", () => {
|
|
6
|
+
expect(moduleListProblems([{{upper}}_SERVER])).toEqual([])
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it("serves its template under the key of the module", () => {
|
|
10
|
+
expect({{upper}}_SERVER.key).toBe({{upper}}_TEMPLATE.key)
|
|
11
|
+
expect({{upper}}_TEMPLATE).toMatchObject({ key: "{{name}}", workbench: "{{name}}" })
|
|
12
|
+
})
|
|
13
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ModuleServer, ModuleTemplate } from "@{{project}}/agent-sdk/module"
|
|
2
|
+
|
|
3
|
+
/** The {{pascal}} module's template: its copy, agent, setup gates, authority actions and accepted commands. */
|
|
4
|
+
export const {{upper}}_TEMPLATE: ModuleTemplate = {
|
|
5
|
+
key: "{{name}}",
|
|
6
|
+
order: 100,
|
|
7
|
+
name: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
8
|
+
shortName: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
9
|
+
description: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
10
|
+
points: { vi: [], en: [] },
|
|
11
|
+
purpose: "{{pascal}}",
|
|
12
|
+
icon: "module",
|
|
13
|
+
glyph: "M12 3l9 5v8l-9 5-9-5V8z",
|
|
14
|
+
mascot: "",
|
|
15
|
+
category: "operations",
|
|
16
|
+
status: "early",
|
|
17
|
+
audience: "customer",
|
|
18
|
+
includedWithWorkspace: false,
|
|
19
|
+
available: false,
|
|
20
|
+
knowledgeFolder: "{{name}}",
|
|
21
|
+
workbench: "{{name}}",
|
|
22
|
+
settingsExtras: null,
|
|
23
|
+
requires: { capabilities: [], connections: [] },
|
|
24
|
+
connectionProviders: [],
|
|
25
|
+
defaultOperatingMode: "assist",
|
|
26
|
+
multiInstance: false,
|
|
27
|
+
agent: {
|
|
28
|
+
label: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
29
|
+
handle: "{{name}}",
|
|
30
|
+
summary: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
31
|
+
capabilities: { vi: [], en: [] },
|
|
32
|
+
role: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
33
|
+
instructions: { vi: "{{pascal}}", en: "{{pascal}}" },
|
|
34
|
+
},
|
|
35
|
+
gates: [],
|
|
36
|
+
authorityActions: [],
|
|
37
|
+
accepts: [],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The server half of the {{pascal}} module: its template and the words it adds to a graph. */
|
|
41
|
+
export const {{upper}}_SERVER: ModuleServer = { key: "{{name}}", template: {{upper}}_TEMPLATE }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { {{upper}}_SERVER, {{upper}}_TEMPLATE } from "./{{name}}.template"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
import { EmptyNotice, Page } from "@starci/grammar/common"
|
|
3
|
+
|
|
4
|
+
/** What the {{pascal}} workbench draws: its title and the words of its empty state. */
|
|
5
|
+
export interface {{pascal}}WorkbenchViewProps {
|
|
6
|
+
readonly title: string
|
|
7
|
+
readonly emptyTitle: string
|
|
8
|
+
readonly emptyBody: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Draws the {{pascal}} workbench with Grammar alone; it owns no class string, colour or visual of its own. */
|
|
12
|
+
export const {{pascal}}WorkbenchView = (props: {{pascal}}WorkbenchViewProps): ReactNode => (
|
|
13
|
+
<Page header={{ title: props.title }}>
|
|
14
|
+
<EmptyNotice message={props.emptyTitle} description={props.emptyBody} />
|
|
15
|
+
</Page>
|
|
16
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"build:be": "cd be && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
1
|
+
"build:be": "{{modulesFirst}}cd be && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
2
2
|
"build:fe": "{{buildFe}}",
|
|
3
3
|
"codegen": "node scripts/codegen.mjs",
|
|
4
4
|
"codegen:graphql": "node scripts/codegen.mjs --graphql",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{{appScripts}}
|
|
5
5
|
{{> app/package-scripts/core.json}}
|
|
6
6
|
"typecheck:tests": "tsc -p be/src/tests/tsconfig.json",
|
|
7
|
-
"test": "cd be && jest --selectProjects unit --coverage",
|
|
7
|
+
"test": "{{modulesFirst}}cd be && jest --selectProjects unit --coverage",
|
|
8
8
|
"test:integration": "npm run typecheck:tests && cd be && jest --selectProjects integration",
|
|
9
9
|
"test:e2e": "npm run typecheck:tests && cd be && jest --selectProjects e2e",
|
|
10
10
|
"test:contract": "npm run typecheck:tests && cd be && jest --selectProjects contract",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{{appScripts}}
|
|
5
5
|
{{> app/package-scripts/core.json}}
|
|
6
6
|
"typecheck:tests": "tsc -p be/src/tests/tsconfig.json",
|
|
7
|
-
"test": "cd be && jest --selectProjects unit --coverage",
|
|
7
|
+
"test": "{{modulesFirst}}cd be && jest --selectProjects unit --coverage",
|
|
8
8
|
"test:integration": "npm run typecheck:tests && cd be && jest --selectProjects integration",
|
|
9
9
|
{{> app/package-scripts/db.json}}
|
|
10
10
|
{{backingScripts}}
|