@starci/hfs 5.0.8 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +10 -1
- package/cache/import-graph.mjs +6 -61
- package/cache/lint-family.d.mts +10 -1
- package/cache/lint-family.mjs +12 -5
- package/codemod/directive-first.d.mts +48 -0
- package/codemod/directive-first.mjs +136 -0
- package/codemod/explicit-surface-edit.d.mts +14 -0
- package/codemod/explicit-surface-edit.mjs +57 -0
- package/codemod/explicit-surface-names.d.mts +30 -0
- package/codemod/explicit-surface-names.mjs +135 -0
- package/codemod/explicit-surface-render.d.mts +29 -0
- package/codemod/explicit-surface-render.mjs +197 -0
- package/codemod/explicit-surface.d.mts +50 -0
- package/codemod/explicit-surface.mjs +374 -0
- package/codemod/main.d.mts +7 -5
- package/codemod/main.mjs +37 -16
- package/codemod/source-tree.d.mts +8 -0
- package/codemod/source-tree.mjs +23 -0
- package/db/postgres.mjs +9 -1
- package/lint/eslint-list.cjs +29 -0
- package/lint/run.d.mts +19 -5
- package/lint/run.mjs +47 -14
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +31 -10
- package/runtime/knowledge/hfs/fe-stack.yaml +7 -7
- package/runtime/knowledge/hfs/fork-images.yaml +7 -0
- package/runtime/knowledge/hfs/rules.yaml +107 -8
- package/runtime/knowledge/hfs/slots.yaml +75 -24
- package/runtime/modules/kernel/failure-codes.yaml +62 -0
- package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
- package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
- package/runtime/scripts/hfs/architecture/test-world-files.d.mts +4 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +26 -7
- package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
- package/runtime/scripts/hfs/explicit-surface-rule.d.mts +58 -0
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +59 -0
- package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
- package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
- package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
- package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
- package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
- package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
- package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
- package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
- package/runtime/scripts/hfs/secret.mjs +10 -7
- package/runtime/scripts/hfs/slots.mjs +1 -1
- package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
- package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
- package/runtime/scripts/lib/declaration-surface.d.mts +35 -0
- package/runtime/scripts/lib/declaration-surface.mjs +112 -0
- package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
- package/runtime/scripts/lib/swarm-stack.mjs +2 -2
- package/scaffold/add-module.d.mts +16 -0
- package/scaffold/add-module.mjs +232 -0
- package/scaffold/add-queue-lite.mjs +49 -0
- package/scaffold/add.mjs +5 -0
- package/scaffold/app.mjs +7 -7
- package/scaffold/module-registry.d.mts +19 -0
- package/scaffold/module-registry.mjs +84 -0
- package/src/main.mjs +23 -2
- package/stack/cli.mjs +6 -1
- package/stack/context.d.mts +15 -0
- package/stack/context.mjs +1 -0
- package/stack/local-cli.d.mts +25 -0
- package/stack/local-cli.mjs +89 -0
- package/stack/local-config.d.mts +17 -0
- package/stack/local-config.mjs +37 -0
- package/stack/local-custody.d.mts +24 -0
- package/stack/local-custody.mjs +109 -0
- package/stack/local-doctor.d.mts +6 -0
- package/stack/local-doctor.mjs +49 -0
- package/stack/local-down.d.mts +8 -0
- package/stack/local-down.mjs +20 -0
- package/stack/local-host.d.mts +42 -0
- package/stack/local-host.mjs +163 -0
- package/stack/local-logs.d.mts +8 -0
- package/stack/local-logs.mjs +32 -0
- package/stack/local-ports.d.mts +7 -0
- package/stack/local-ports.mjs +39 -0
- package/stack/local-prepare.d.mts +22 -0
- package/stack/local-prepare.mjs +62 -0
- package/stack/local-run.d.mts +71 -0
- package/stack/local-run.mjs +200 -0
- package/stack/local-state.d.mts +35 -0
- package/stack/local-state.mjs +29 -0
- package/stack/local-status.d.mts +6 -0
- package/stack/local-status.mjs +36 -0
- package/stack/local-up.d.mts +11 -0
- package/stack/local-up.mjs +166 -0
- package/stack/local-verify.d.mts +6 -0
- package/stack/local-verify.mjs +21 -0
- package/sync/index.d.mts +13 -12
- package/sync/index.mjs +83 -28
- package/templates/app/module/jest.config.js.tpl +8 -0
- package/templates/app/module/src/index.ts.tpl +2 -0
- package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
- package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
- package/templates/app/module/src/manifest/index.ts.tpl +1 -0
- package/templates/app/module/src/public/index.ts.tpl +2 -0
- package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
- package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
- package/templates/app/module/src/template/index.ts.tpl +1 -0
- package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
- package/templates/app/module/src/workbench/index.ts.tpl +2 -0
- package/templates/app/package-scripts/package.json +1 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +76 -70
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +23 -13
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +25 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -26
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +1 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +2 -1
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +57 -14
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +13 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +13 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +90 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +28 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +145 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +95 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-identity.entity.ts +38 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-user.entity.ts +54 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +17 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +78 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +87 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +44 -0
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +2 -2
- package/templates/be/inbox/entities.index.ts.tpl +1 -1
- package/templates/be/inbox/module-definition.ts.tpl +8 -2
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +10 -2
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +8 -2
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -3
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +7 -6
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/job.processor.ts.tpl +1 -1
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +5 -4
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -3
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/consumer.ts.tpl +1 -1
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +9 -3
- package/templates/be/platform/graphql/graphql.module-definition.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +9 -3
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +2 -1
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +8 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/rate-limit.sql.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +8 -3
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +10 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +23 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +47 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
- package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/persistence/entities/index.ts +1 -1
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +18 -5
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +7 -1
- package/templates/be/table/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/table/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/table/entities.index.ts.tpl +1 -1
- package/templates/be/table/module-definition.ts.tpl +8 -3
- package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
- package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -3
- package/templates/be/test-world/world/test-world.config.ts.tpl +8 -1
- package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
- package/templates/fe/common/app-layout.tsx.partial +1 -1
- package/templates/fe/common/site-shell.tsx.partial +3 -3
- package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignIn.ts +11 -3
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +8 -2
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/hooks.ts +1 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +18 -12
- package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/hooks.ts +3 -2
- package/templates/fe/skeleton/apps/__app__/src/redux/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +12 -4
- package/templates/fe/skeleton/apps/__app__/src/redux/store.ts +8 -3
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +1 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +20 -2
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +5 -1
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +3 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +5 -1
- package/templates/fe/table/graphql/useMutateSwr.ts.tpl +2 -1
- package/templates/fe/table/graphql/useQuerySwr.ts.tpl +2 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/no-auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/attach-access-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/error.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/http.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/refresh-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/retry.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/timeout.ts +1 -1
- package/templates/fe/units/page/index.tsx.tpl +3 -2
- package/templates/fe/units/slot/SlotView.tsx.tpl +1 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -37
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -36
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.2.0 - 2026-11-03
|
|
4
|
+
|
|
5
|
+
- 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`.
|
|
6
|
+
- Added: the custody kind `ed25519-pem` of `local.custody` (the dev sign-in key is EdDSA); the lite dev environment names `AUTH_WEB_ORIGIN`.
|
|
7
|
+
- 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.
|
|
8
|
+
- 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).
|
|
9
|
+
- 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).
|
|
10
|
+
- 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.
|
|
11
|
+
- 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`.
|
|
12
|
+
- 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.
|
|
13
|
+
- 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.
|
|
14
|
+
- Added: the slot field `managedOnly` (a slot that only lists files sync renders; the slot owning the path judges them). Manifest 3.16.0.
|
|
15
|
+
- 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).
|
|
16
|
+
- Added: the canon pin of `@starci/auth` 0.1.0.
|
|
17
|
+
- 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.
|
|
18
|
+
- 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.
|
|
19
|
+
- 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.
|
|
20
|
+
- 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).
|
|
21
|
+
- 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.
|
|
22
|
+
|
|
23
|
+
## 5.1.0
|
|
24
|
+
|
|
25
|
+
- 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.
|
|
26
|
+
- Added: `starci app codemod directive-first` moves a `"use client"` or `"use server"` below another statement, or in the form `("use client")`, to the first statement of its module (R369).
|
|
27
|
+
- Changed: the test-world declaration (R47, R89) is read in its typed form, `const world: TestWorldApi = defineTestWorld({ ... })` with `useTestWorld` and `useSandbox` exported typed from it, as well as in the destructured form; the template writes the typed form, and a config with neither is still refused.
|
|
28
|
+
- Added: `template-surface.spec.mts` holds every TypeScript template to R368 and R369 (the front-end units are judged through their generators).
|
|
29
|
+
- Changed: the scaffold templates write the type of every export (R368): Nest module definitions export each `ConfigurableModuleHost` member with its type, hooks name their result interface, async pages and layouts return `Promise<ReactElement>`, redux, zustand, next-intl and Apollo exports are typed, SQL texts are `SqlText`, entity and migration lists are `DatabaseConnectionOptions` members. A new app scaffolds clean under `explicit-export-surface`.
|
|
30
|
+
- Added: `runtime/scripts/lib/declaration-surface.mjs`, the one classifier of a TypeScript file's exported type surface (the isolated declaration emit and the exports it cannot write without inference, with the JSX-only component exemption), and `runtime/scripts/hfs/explicit-surface-rule.mjs`, the factory of the `explicit-export-surface` rule both lint canons build (R368). The app-check ESLint cache reads the same classifier for `open`, so the rule reports exactly the files the cache counts as open.
|
|
31
|
+
- Changed: the ESLint child of the cached lint hands its project-graph snapshot out. The list runner (`lint/eslint-list.cjs`, now also the runner of a whole-side run, as the list `.`) asks each installed canon for `projectGraphSnapshot` after the lint and writes the findings beside its results; `lintSide` writes the `graph` records from them and builds the graph itself only for a canon the child did not answer for. A whole-side lint no longer builds the project graph a second time in the CLI process.
|
|
32
|
+
|
|
3
33
|
## 5.0.8
|
|
4
34
|
|
|
5
35
|
- Removed: the `starci app lint` verb. `starci app check` is the one verb for app quality and carries what the lint verb carried: `--side be|fe` limits the lint gate to one side (reading and writing that side's cache records), `--out <file>` writes the merged Sonar Generic Issue Import document of the run (the findings of the selected gates under `starci-hfs`, then ESLint and stylelint), and `--fix` runs ESLint and stylelint with `--fix` before the check (with `--gate lint`). There is no `--changed` and no `--workspace`: the cache makes a whole-app check of an unchanged tree take seconds. The rendered `lint` and `lint:fix` scripts, the managed CI lint step and the release gate's Sonar row call `app check --gate lint`, and rule R13 requires that form.
|
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
|
|
package/cache/import-graph.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// added or removed file and every file that reaches one through imports, in the new graph and in the recorded one. Everything here is pure over
|
|
7
7
|
// its inputs (the compiler, a file reader, the hashes), so a spec drives it with fixtures and the cache itself stays in lint-family.mts.
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
import { DECLARATION, SCRIPT, TYPESCRIPT, declarationSurface } from '../runtime/scripts/lib/declaration-surface.mjs';
|
|
9
10
|
import { hashText } from './hash.mjs';
|
|
10
11
|
export const posix = (file) => file.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
11
12
|
/** The compiler options of the side's own tsconfig (the file the typed ESLint run reads), or the permissive defaults when the side holds none. */
|
|
@@ -120,58 +121,6 @@ export function resolveImports(host, options, file, text) {
|
|
|
120
121
|
}
|
|
121
122
|
return [...found].sort();
|
|
122
123
|
}
|
|
123
|
-
/** Whether `expression` is a JSX element, fragment, `null` or `undefined`, or a conditional of those: its type does not depend on any other file. */
|
|
124
|
-
function isJsxValue(ts, expression) {
|
|
125
|
-
if (expression === undefined)
|
|
126
|
-
return false;
|
|
127
|
-
let node = expression;
|
|
128
|
-
while (ts.isParenthesizedExpression(node))
|
|
129
|
-
node = node.expression;
|
|
130
|
-
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node) || node.kind === ts.SyntaxKind.NullKeyword)
|
|
131
|
-
return true;
|
|
132
|
-
if (ts.isIdentifier(node))
|
|
133
|
-
return node.text === 'undefined';
|
|
134
|
-
return ts.isConditionalExpression(node) && isJsxValue(ts, node.whenTrue) && isJsxValue(ts, node.whenFalse);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Whether the function (or method) the declaration diagnostic at `start` points at has the type `JSX.Element | null | undefined` whatever its body
|
|
138
|
-
* reads: not async, not a generator, and every `return` of its own (and its concise body) a JSX value. A React component written without a return
|
|
139
|
-
* type is the common case; its inferred type is one the JSX namespace of the installed packages gives, which a lockfile change dirties as a whole.
|
|
140
|
-
*/
|
|
141
|
-
function returnsOnlyJsx(ts, source, start) {
|
|
142
|
-
let found;
|
|
143
|
-
const find = (node) => {
|
|
144
|
-
if (found !== undefined || start < node.getFullStart() || start >= node.end)
|
|
145
|
-
return;
|
|
146
|
-
const named = (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && node.name !== undefined && node.name.getStart(source) === start;
|
|
147
|
-
if (named || ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && node.getStart(source) === start)) {
|
|
148
|
-
found = node;
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
ts.forEachChild(node, find);
|
|
152
|
-
};
|
|
153
|
-
find(source);
|
|
154
|
-
const fn = found;
|
|
155
|
-
if (fn === undefined || fn.type !== undefined || fn.body === undefined)
|
|
156
|
-
return false;
|
|
157
|
-
if (fn.asteriskToken !== undefined || (ts.getCombinedModifierFlags(fn) & ts.ModifierFlags.Async) !== 0)
|
|
158
|
-
return false;
|
|
159
|
-
if (!ts.isBlock(fn.body))
|
|
160
|
-
return isJsxValue(ts, fn.body);
|
|
161
|
-
let only = true;
|
|
162
|
-
const visit = (node) => {
|
|
163
|
-
if (!only || ts.isFunctionLike(node) || ts.isClassLike(node))
|
|
164
|
-
return;
|
|
165
|
-
if (ts.isReturnStatement(node) && node.expression !== undefined && !isJsxValue(ts, node.expression))
|
|
166
|
-
only = false;
|
|
167
|
-
ts.forEachChild(node, visit);
|
|
168
|
-
};
|
|
169
|
-
ts.forEachChild(fn.body, visit);
|
|
170
|
-
return only;
|
|
171
|
-
}
|
|
172
|
-
const SCRIPT = /\.(?:[cm]?jsx?)$/;
|
|
173
|
-
const DECLARATION = /\.d\.[cm]?ts$/;
|
|
174
|
-
const TYPESCRIPT = /\.(?:[cm]?tsx?)$/;
|
|
175
124
|
/**
|
|
176
125
|
* The type surface of one source file: the digest of its isolated declaration emit (`ts.transpileDeclaration`, TypeScript 5.5 and later, which reads the
|
|
177
126
|
* file alone) and whether it is open. A body-only edit leaves the emit, and so the digest, as it was; a changed signature, export list or doc comment
|
|
@@ -184,16 +133,12 @@ export function surfaceOf(host, options, file, text) {
|
|
|
184
133
|
return { api: hashText(text), open: true, refs: undefined };
|
|
185
134
|
if (!TYPESCRIPT.test(file))
|
|
186
135
|
return { api: hashText(text), open: false, refs: [] };
|
|
187
|
-
const emit = ts.transpileDeclaration;
|
|
188
|
-
if (emit === undefined)
|
|
189
|
-
return { api: undefined, open: true, refs: undefined };
|
|
190
136
|
try {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return { api: hashText(declared.outputText), open: inferred, refs: resolveImports(host, options, file, declared.outputText) };
|
|
137
|
+
const surface = declarationSurface(ts, file, text, options.module);
|
|
138
|
+
if (surface === null)
|
|
139
|
+
return { api: undefined, open: true, refs: undefined };
|
|
140
|
+
// the classifier is the one the explicit-export-surface rule of the canons reports with: a file is open exactly when it has a problem
|
|
141
|
+
return { api: hashText(surface.outputText), open: surface.problems.length > 0, refs: resolveImports(host, options, file, surface.outputText) };
|
|
197
142
|
}
|
|
198
143
|
catch {
|
|
199
144
|
return { api: undefined, open: true, refs: undefined };
|
package/cache/lint-family.d.mts
CHANGED
|
@@ -78,10 +78,14 @@ export interface StylelintResult {
|
|
|
78
78
|
warnings: StylelintWarning[];
|
|
79
79
|
[key: string]: unknown;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* The answer of a linter seam: validated results of the files it was asked about, or why it could not complete. `graph` is, for ESLint, the project-graph
|
|
83
|
+
* snapshot each canon's graph rules built in the child, by canon name: the records of the `graph` step are written from it instead of building the graph again.
|
|
84
|
+
*/
|
|
82
85
|
export interface LinterOutcome<R> {
|
|
83
86
|
results: R[];
|
|
84
87
|
errors: string[];
|
|
88
|
+
graph?: Readonly<Record<string, GraphSnapshot>> | undefined;
|
|
85
89
|
}
|
|
86
90
|
/** Everything one side's cached lint needs. */
|
|
87
91
|
export interface LintSideInput {
|
|
@@ -147,6 +151,11 @@ export declare function crossDigestFactory(options: {
|
|
|
147
151
|
}): (file: string) => string | undefined;
|
|
148
152
|
/** The TypeScript the app installed, resolved from the side folder like the linters are (not this package's own copy). */
|
|
149
153
|
export declare function loadAppTypeScript(sideRoot: string): Ts;
|
|
154
|
+
/**
|
|
155
|
+
* The graph findings of every canon that publishes graph rules, merged by file and sorted. A canon whose snapshot the ESLint child handed out (`handed`, by
|
|
156
|
+
* canon name) is not asked to build its graph again; any other canon builds it through its `projectGraph`.
|
|
157
|
+
*/
|
|
158
|
+
export declare function mergedSnapshot(sideRoot: string, canons: readonly CanonDeclarations[], handed?: Readonly<Record<string, GraphSnapshot>>): Record<string, GraphFinding[]> | null;
|
|
150
159
|
/**
|
|
151
160
|
* Judges one side: ESLint (and, for fe, stylelint) over what the records cannot vouch for, the records of the rest, and the new records.
|
|
152
161
|
* Never throws for a tool failure: those come back in `errors`, and a side with an error stores nothing.
|
package/cache/lint-family.mjs
CHANGED
|
@@ -164,15 +164,20 @@ function graphHost(ts, sideRoot, hashOf) {
|
|
|
164
164
|
}
|
|
165
165
|
/** A record's `data` object, or an empty one. */
|
|
166
166
|
const dataOf = (record) => (record.data !== null && typeof record.data === 'object' && !Array.isArray(record.data) ? record.data : {});
|
|
167
|
-
/**
|
|
168
|
-
|
|
167
|
+
/**
|
|
168
|
+
* The graph findings of every canon that publishes graph rules, merged by file and sorted. A canon whose snapshot the ESLint child handed out (`handed`, by
|
|
169
|
+
* canon name) is not asked to build its graph again; any other canon builds it through its `projectGraph`.
|
|
170
|
+
*/
|
|
171
|
+
export function mergedSnapshot(sideRoot, canons, handed) {
|
|
169
172
|
const merged = {};
|
|
170
173
|
for (const canon of canons) {
|
|
171
174
|
if (!canon.graphRules)
|
|
172
175
|
continue;
|
|
173
|
-
|
|
176
|
+
const given = handed?.[canon.name];
|
|
177
|
+
if (given === undefined && canon.projectGraph === null)
|
|
174
178
|
return null;
|
|
175
|
-
|
|
179
|
+
const { byFile } = given ?? canon.projectGraph({ repoRoot: sideRoot });
|
|
180
|
+
for (const [file, findings] of Object.entries(byFile))
|
|
176
181
|
merged[posix(file)] = [...(merged[posix(file)] ?? []), ...findings];
|
|
177
182
|
}
|
|
178
183
|
for (const list of Object.values(merged))
|
|
@@ -331,12 +336,14 @@ export async function lintSide(input) {
|
|
|
331
336
|
// ------------------------------------------------------------------ the run
|
|
332
337
|
const eslintList = [...dirtyEslint].sort();
|
|
333
338
|
const styleList = [...dirtyStyle].sort();
|
|
339
|
+
let handedGraph;
|
|
334
340
|
const freshEslint = new Map();
|
|
335
341
|
const freshStyle = new Map();
|
|
336
342
|
const rootOf = (file) => path.relative(sideRoot, file).replace(/\\/g, '/');
|
|
337
343
|
if (eslintList.length > 0) {
|
|
338
344
|
const run = await input.eslint(whole !== null ? null : eslintList);
|
|
339
345
|
errors.push(...run.errors);
|
|
346
|
+
handedGraph = run.graph;
|
|
340
347
|
for (const result of run.results)
|
|
341
348
|
freshEslint.set(posix(rootOf(result.filePath)), result);
|
|
342
349
|
}
|
|
@@ -408,7 +415,7 @@ export async function lintSide(input) {
|
|
|
408
415
|
if (!moved.has(file))
|
|
409
416
|
records.push({ path: file, step: LINT_STEPS.imports, hash: node.hash, findings: [], data: { globals: node.declaresGlobals, imports: node.imports, ...(node.api === undefined ? {} : { api: node.api }), open: node.open !== false, ...(node.refs === undefined ? {} : { refs: node.refs }) } });
|
|
410
417
|
}
|
|
411
|
-
snapshot ??= mergedSnapshot(sideRoot, input.canons);
|
|
418
|
+
snapshot ??= mergedSnapshot(sideRoot, input.canons, handedGraph);
|
|
412
419
|
for (const [file, findings] of Object.entries(snapshot ?? {})) {
|
|
413
420
|
if (scopeSet.has(file) && !moved.has(file) && findings.length > 0)
|
|
414
421
|
records.push({ path: file, step: LINT_STEPS.graph, hash: planHash(file), findings: graphRecordFindings(side, file, findings) });
|
|
@@ -0,0 +1,48 @@
|
|
|
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 move of the report: a directive taken from `line` to the top of its module. */
|
|
4
|
+
export interface DirectiveFirstChange {
|
|
5
|
+
file: string;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
code: string;
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
}
|
|
12
|
+
/** One case left for a person. */
|
|
13
|
+
export interface DirectiveFirstFinding {
|
|
14
|
+
file: string;
|
|
15
|
+
line: number;
|
|
16
|
+
column: number;
|
|
17
|
+
code: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
/** The report of a migration (`starci app codemod directive-first`). */
|
|
21
|
+
export interface DirectiveFirstReport {
|
|
22
|
+
schema: string;
|
|
23
|
+
root: string;
|
|
24
|
+
write: boolean;
|
|
25
|
+
filesScanned: number;
|
|
26
|
+
filesChanged: string[];
|
|
27
|
+
changes: DirectiveFirstChange[];
|
|
28
|
+
findings: DirectiveFirstFinding[];
|
|
29
|
+
}
|
|
30
|
+
/** Move every `"use client"` / `"use server"` that is not in the prologue of the module to the top of it, as its first statement, with one blank line after. */
|
|
31
|
+
export declare function transformDirectiveFirst({ ts, text, file }: {
|
|
32
|
+
ts: Ts;
|
|
33
|
+
text: string;
|
|
34
|
+
file?: string;
|
|
35
|
+
}): {
|
|
36
|
+
text: string;
|
|
37
|
+
changes: DirectiveFirstChange[];
|
|
38
|
+
findings: DirectiveFirstFinding[];
|
|
39
|
+
};
|
|
40
|
+
/** Preview or apply the moves in one app (`be/`, `fe/`, or `src/` of a single-side app); a conflicting module is kept in the returned report. */
|
|
41
|
+
export declare function migrateDirectiveFirst({ root, write, ts }: {
|
|
42
|
+
root: string;
|
|
43
|
+
write?: boolean;
|
|
44
|
+
ts?: Ts | null;
|
|
45
|
+
}): DirectiveFirstReport;
|
|
46
|
+
/** Render the moves and what is left for a person as Markdown; JSON consumers read the same report object. */
|
|
47
|
+
export declare function directiveFirstMarkdown(report: DirectiveFirstReport): string;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { loadTypescript } from '../runtime/scripts/lib/package-at.mjs';
|
|
5
|
+
import { sourceLocation } from '../runtime/scripts/lib/ts-ast.mjs';
|
|
6
|
+
import { filesUnder, relativePosix, sideFolders } from './source-tree.mjs';
|
|
7
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
|
+
/** The directives Next.js reads from the prologue of a module (R369, FE_DIRECTIVE_FIRST). */
|
|
9
|
+
const DIRECTIVES = new Set(['use client', 'use server']);
|
|
10
|
+
const SOURCE = /\.(?:[cm]?[jt]s|[jt]sx)$/i;
|
|
11
|
+
const DECLARATION = /\.d\.[cm]?ts$/i;
|
|
12
|
+
/** The string literal a statement is made of, through the parentheses Prettier wraps it in, or null. */
|
|
13
|
+
function stringStatement(ts, statement) {
|
|
14
|
+
if (!ts.isExpressionStatement(statement))
|
|
15
|
+
return null;
|
|
16
|
+
let expression = statement.expression;
|
|
17
|
+
while (ts.isParenthesizedExpression(expression))
|
|
18
|
+
expression = expression.expression;
|
|
19
|
+
return ts.isStringLiteral(expression) ? expression : null;
|
|
20
|
+
}
|
|
21
|
+
/** The span to delete for `statement`: its own line when nothing else is on it (with a `;` Prettier put before a parenthesized statement), else the statement alone. */
|
|
22
|
+
function removalSpan(text, statement, source) {
|
|
23
|
+
let start = statement.getStart(source);
|
|
24
|
+
let end = statement.end;
|
|
25
|
+
let from = start;
|
|
26
|
+
while (from > 0 && (text[from - 1] === ' ' || text[from - 1] === '\t'))
|
|
27
|
+
from -= 1;
|
|
28
|
+
if (text[from - 1] === ';' && (from - 1 === 0 || text[from - 2] === '\n'))
|
|
29
|
+
from -= 1;
|
|
30
|
+
const startsLine = from === 0 || text[from - 1] === '\n';
|
|
31
|
+
let to = end;
|
|
32
|
+
while (to < text.length && (text[to] === ' ' || text[to] === '\t'))
|
|
33
|
+
to += 1;
|
|
34
|
+
const endsLine = to >= text.length || text[to] === '\n' || text[to] === '\r';
|
|
35
|
+
if (startsLine && endsLine) {
|
|
36
|
+
start = from;
|
|
37
|
+
end = text[to] === '\r' ? to + 2 : to + 1;
|
|
38
|
+
}
|
|
39
|
+
return { start, end: Math.min(end, text.length) };
|
|
40
|
+
}
|
|
41
|
+
/** Move every `"use client"` / `"use server"` that is not in the prologue of the module to the top of it, as its first statement, with one blank line after. */
|
|
42
|
+
export function transformDirectiveFirst({ ts, text, file = 'module.tsx' }) {
|
|
43
|
+
const none = { text, changes: [], findings: [] };
|
|
44
|
+
if (!text.includes('use client') && !text.includes('use server'))
|
|
45
|
+
return none;
|
|
46
|
+
const kind = /\.tsx$/i.test(file) ? ts.ScriptKind.TSX : /\.jsx$/i.test(file) ? ts.ScriptKind.JSX : /\.[cm]?js$/i.test(file) ? ts.ScriptKind.JS : ts.ScriptKind.TS;
|
|
47
|
+
const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, kind);
|
|
48
|
+
const first = source.statements[0];
|
|
49
|
+
if (first === undefined)
|
|
50
|
+
return none;
|
|
51
|
+
// The prologue is the run of plain string statements at the top; a parenthesized one is an expression, not a directive.
|
|
52
|
+
let prologueEnd = 0;
|
|
53
|
+
const kept = new Set();
|
|
54
|
+
while (prologueEnd < source.statements.length) {
|
|
55
|
+
const statement = source.statements[prologueEnd];
|
|
56
|
+
if (!ts.isExpressionStatement(statement) || !ts.isStringLiteral(statement.expression))
|
|
57
|
+
break;
|
|
58
|
+
kept.add(statement.expression.text);
|
|
59
|
+
prologueEnd += 1;
|
|
60
|
+
}
|
|
61
|
+
const misplaced = source.statements.slice(prologueEnd).flatMap((statement, offset) => {
|
|
62
|
+
const literal = stringStatement(ts, statement);
|
|
63
|
+
return literal !== null && DIRECTIVES.has(literal.text) ? [{ statement, literal, before: prologueEnd + offset }] : [];
|
|
64
|
+
});
|
|
65
|
+
if (!misplaced.length)
|
|
66
|
+
return none;
|
|
67
|
+
const changes = [];
|
|
68
|
+
const findings = [];
|
|
69
|
+
const opposite = (directive) => (directive === 'use client' ? 'use server' : 'use client');
|
|
70
|
+
const wanted = new Set(misplaced.map(entry => entry.literal.text));
|
|
71
|
+
const edits = [];
|
|
72
|
+
const toTop = [];
|
|
73
|
+
const placed = new Set(kept);
|
|
74
|
+
for (const { statement, literal, before } of misplaced) {
|
|
75
|
+
const place = sourceLocation(source, statement);
|
|
76
|
+
const directive = literal.text;
|
|
77
|
+
if (kept.has(opposite(directive)) || wanted.has(opposite(directive))) {
|
|
78
|
+
findings.push({ file, ...place, code: 'CONFLICTING_DIRECTIVES', reason: `"use client" and "use server" cannot share a module: split it, then keep the one directive that stays at the top` });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
edits.push({ ...removalSpan(text, statement, source), text: '' });
|
|
82
|
+
if (!placed.has(directive)) {
|
|
83
|
+
toTop.push(literal.getText(source) + (statement.getText(source).endsWith(';') ? ';' : ''));
|
|
84
|
+
placed.add(directive);
|
|
85
|
+
}
|
|
86
|
+
const mangled = ts.isExpressionStatement(statement) && ts.isParenthesizedExpression(statement.expression);
|
|
87
|
+
changes.push({ file, ...place, code: mangled ? 'DIRECTIVE_PARENTHESIZED' : 'DIRECTIVE_BELOW_STATEMENT', from: `${mangled ? 'parenthesized, ' : ''}after ${before} statement${before === 1 ? '' : 's'}`, to: 'first statement' });
|
|
88
|
+
}
|
|
89
|
+
if (toTop.length)
|
|
90
|
+
edits.push({ start: first.getStart(source), end: first.getStart(source), text: `${toTop.join('\n')}\n\n` });
|
|
91
|
+
let output = text;
|
|
92
|
+
for (const edit of edits.sort((a, b) => b.start - a.start || b.end - a.end))
|
|
93
|
+
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
94
|
+
return { text: output, changes, findings };
|
|
95
|
+
}
|
|
96
|
+
/** Preview or apply the moves in one app (`be/`, `fe/`, or `src/` of a single-side app); a conflicting module is kept in the returned report. */
|
|
97
|
+
export function migrateDirectiveFirst({ root, write = false, ts = loadTypescript(root, PACKAGE_ROOT) }) {
|
|
98
|
+
if (!ts)
|
|
99
|
+
throw new Error('directive-first needs TypeScript installed in the app or @starci/hfs development checkout');
|
|
100
|
+
const folders = sideFolders(root);
|
|
101
|
+
if (!folders.length)
|
|
102
|
+
throw new Error('directive-first runs at an app root with a fe/ directory');
|
|
103
|
+
const report = { schema: 'starci/directive-first-report@1', root, write, filesScanned: 0, filesChanged: [], changes: [], findings: [] };
|
|
104
|
+
for (const target of folders.flatMap(folder => filesUnder(folder, SOURCE)).filter(target => !DECLARATION.test(target))) {
|
|
105
|
+
const file = relativePosix(root, target);
|
|
106
|
+
const original = fs.readFileSync(target, 'utf8');
|
|
107
|
+
const result = transformDirectiveFirst({ ts, text: original, file });
|
|
108
|
+
report.filesScanned += 1;
|
|
109
|
+
report.changes.push(...result.changes);
|
|
110
|
+
report.findings.push(...result.findings);
|
|
111
|
+
if (result.text !== original) {
|
|
112
|
+
report.filesChanged.push(file);
|
|
113
|
+
if (write)
|
|
114
|
+
fs.writeFileSync(target, result.text);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return report;
|
|
118
|
+
}
|
|
119
|
+
const cell = (value) => String(value).replaceAll('|', '\\|').replaceAll('\n', ' ');
|
|
120
|
+
/** Render the moves and what is left for a person as Markdown; JSON consumers read the same report object. */
|
|
121
|
+
export function directiveFirstMarkdown(report) {
|
|
122
|
+
const out = ['# directive-first', '', `Scanned ${report.filesScanned} files, ${report.filesChanged.length} ${report.write ? 'changed' : 'would change'}, ${report.changes.length} directives moved, ${report.findings.length} left for a person.`, ''];
|
|
123
|
+
if (report.findings.length) {
|
|
124
|
+
out.push('## Left for a person', '', '| location | code | reason |', '|---|---|---|');
|
|
125
|
+
for (const finding of report.findings)
|
|
126
|
+
out.push(`| ${cell(`${finding.file}:${finding.line}:${finding.column}`)} | ${finding.code} | ${cell(finding.reason)} |`);
|
|
127
|
+
out.push('');
|
|
128
|
+
}
|
|
129
|
+
if (report.changes.length) {
|
|
130
|
+
out.push('## Moves', '', '| location | code | from | to |', '|---|---|---|---|');
|
|
131
|
+
for (const item of report.changes)
|
|
132
|
+
out.push(`| ${cell(`${item.file}:${item.line}:${item.column}`)} | ${item.code} | ${cell(item.from)} | ${cell(item.to)} |`);
|
|
133
|
+
out.push('');
|
|
134
|
+
}
|
|
135
|
+
return `${out.join('\n')}\n`;
|
|
136
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ImportNeed } from './explicit-surface-render.mjs';
|
|
2
|
+
import type * as TsTypes from 'typescript';
|
|
3
|
+
type Ts = typeof import('typescript');
|
|
4
|
+
/** One text edit of the source: the span `start`..`end` becomes `text`. */
|
|
5
|
+
export interface Edit {
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/** The edits that give a file the imports `needs` names: a specifier added to a named import of the same module, else one `import type` line per module. */
|
|
11
|
+
export declare function importEdits(ts: Ts, source: TsTypes.SourceFile, text: string, needs: readonly ImportNeed[]): Edit[];
|
|
12
|
+
/** `text` with `edits` applied, last position first so every offset stays true; edits at one position keep their order. */
|
|
13
|
+
export declare function applyEdits(text: string, edits: readonly Edit[]): string;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** The edits that give a file the imports `needs` names: a specifier added to a named import of the same module, else one `import type` line per module. */
|
|
2
|
+
export function importEdits(ts, source, text, needs) {
|
|
3
|
+
if (!needs.length)
|
|
4
|
+
return [];
|
|
5
|
+
const eol = text.includes('\r\n') ? '\r\n' : '\n';
|
|
6
|
+
const imports = source.statements.filter(ts.isImportDeclaration);
|
|
7
|
+
const first = imports[0];
|
|
8
|
+
const quote = first !== undefined && ts.isStringLiteral(first.moduleSpecifier) ? first.moduleSpecifier.getText(source).slice(0, 1) : '"';
|
|
9
|
+
const semicolon = first !== undefined && first.getText(source).endsWith(';') ? ';' : '';
|
|
10
|
+
const edits = [];
|
|
11
|
+
const lines = [];
|
|
12
|
+
const bySpecifier = new Map();
|
|
13
|
+
for (const need of needs)
|
|
14
|
+
bySpecifier.set(need.specifier, [...(bySpecifier.get(need.specifier) ?? []), need.name]);
|
|
15
|
+
for (const [specifier, names] of bySpecifier) {
|
|
16
|
+
const sorted = [...new Set(names)].sort();
|
|
17
|
+
const home = imports.find(declaration => ts.isStringLiteral(declaration.moduleSpecifier) && declaration.moduleSpecifier.text === specifier
|
|
18
|
+
&& declaration.importClause?.namedBindings !== undefined && ts.isNamedImports(declaration.importClause.namedBindings) && declaration.importClause.namedBindings.elements.length > 0);
|
|
19
|
+
const clause = home?.importClause;
|
|
20
|
+
const bindings = clause?.namedBindings;
|
|
21
|
+
if (clause !== undefined && bindings !== undefined && ts.isNamedImports(bindings)) {
|
|
22
|
+
const last = bindings.elements[bindings.elements.length - 1];
|
|
23
|
+
edits.push({ start: last.end, end: last.end, text: sorted.map(name => `, ${clause.isTypeOnly ? '' : 'type '}${name}`).join('') });
|
|
24
|
+
}
|
|
25
|
+
else
|
|
26
|
+
lines.push(`import type { ${sorted.join(', ')} } from ${quote}${specifier}${quote}${semicolon}`);
|
|
27
|
+
}
|
|
28
|
+
if (lines.length)
|
|
29
|
+
edits.push(newImports(ts, source, text, lines, eol));
|
|
30
|
+
return edits;
|
|
31
|
+
}
|
|
32
|
+
/** The edit that adds whole import lines: after the last import, else after the directive prologue, else above the first statement. */
|
|
33
|
+
function newImports(ts, source, text, lines, eol) {
|
|
34
|
+
const block = lines.join(eol);
|
|
35
|
+
const last = source.statements.filter(ts.isImportDeclaration).at(-1);
|
|
36
|
+
if (last !== undefined)
|
|
37
|
+
return { start: last.end, end: last.end, text: `${eol}${block}` };
|
|
38
|
+
let prologue;
|
|
39
|
+
for (const statement of source.statements) {
|
|
40
|
+
if (!ts.isExpressionStatement(statement) || !ts.isStringLiteral(statement.expression))
|
|
41
|
+
break;
|
|
42
|
+
prologue = statement;
|
|
43
|
+
}
|
|
44
|
+
if (prologue !== undefined)
|
|
45
|
+
return { start: prologue.end, end: prologue.end, text: `${eol}${eol}${block}` };
|
|
46
|
+
const first = source.statements[0];
|
|
47
|
+
const at = first === undefined ? text.length : first.getStart(source);
|
|
48
|
+
return { start: at, end: at, text: `${block}${eol}${eol}` };
|
|
49
|
+
}
|
|
50
|
+
/** `text` with `edits` applied, last position first so every offset stays true; edits at one position keep their order. */
|
|
51
|
+
export function applyEdits(text, edits) {
|
|
52
|
+
const ordered = edits.map((edit, at) => ({ edit, at })).sort((a, b) => b.edit.start - a.edit.start || b.edit.end - a.edit.end || b.at - a.at);
|
|
53
|
+
let output = text;
|
|
54
|
+
for (const { edit } of ordered)
|
|
55
|
+
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as TsTypes from 'typescript';
|
|
2
|
+
/** The TypeScript compiler API of the app's own install, typed by the version this package develops against. */
|
|
3
|
+
type Ts = typeof import('typescript');
|
|
4
|
+
/** What naming a type reads of the app: its compiler, program and one source file. */
|
|
5
|
+
export interface NamingContext {
|
|
6
|
+
ts: Ts;
|
|
7
|
+
program: TsTypes.Program;
|
|
8
|
+
checker: TsTypes.TypeChecker;
|
|
9
|
+
options: TsTypes.CompilerOptions;
|
|
10
|
+
source: TsTypes.SourceFile;
|
|
11
|
+
/** The absolute, forward-slash name of the file being annotated. */
|
|
12
|
+
file: string;
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
/** The source file of the program a module path (absolute, without its extension) names, or undefined. */
|
|
16
|
+
export declare function moduleFile(c: NamingContext, modulePath: string): TsTypes.SourceFile | undefined;
|
|
17
|
+
/** The symbol a module exports under `name`, with aliases resolved, or undefined. */
|
|
18
|
+
export declare function exportedSymbol(c: NamingContext, file: TsTypes.SourceFile, name: string): TsTypes.Symbol | undefined;
|
|
19
|
+
/** The declaration `symbol` stands for: an import resolved to what it imports, a local export to its export symbol, so two names of one declaration compare equal. */
|
|
20
|
+
export declare function declarationOf(c: NamingContext, symbol: TsTypes.Symbol): TsTypes.Symbol;
|
|
21
|
+
/** The names that can be written at `enclosing` without an import, as symbols; what a function body declares is not visible from its signature. */
|
|
22
|
+
export declare function namesInScope(c: NamingContext, enclosing: TsTypes.Node): Map<string, TsTypes.Symbol>;
|
|
23
|
+
/**
|
|
24
|
+
* `node` (the type node the checker wrote for `type`) with every anonymous object type that an exported type of the app stands for replaced by a
|
|
25
|
+
* reference to that type. It follows type arguments (`Promise<{...}>`), arrays and unions; any other anonymous part stays for the caller to refuse.
|
|
26
|
+
*/
|
|
27
|
+
export declare function nameAnonymousParts(c: NamingContext, node: TsTypes.TypeNode, type: TsTypes.Type, enclosing: TsTypes.Node): TsTypes.TypeNode;
|
|
28
|
+
/** The directory-relative form of `target` from the folder of `from`, without extension, the way a relative import is written. */
|
|
29
|
+
export declare function relativeSpecifier(from: string, target: string): string;
|
|
30
|
+
export {};
|