@aurostack/stacks 0.1.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/LICENSE +21 -0
- package/README.md +209 -0
- package/cli/src/extract.mjs +165 -0
- package/cli/src/fsx.mjs +197 -0
- package/cli/src/generate.mjs +235 -0
- package/cli/src/hooks.mjs +55 -0
- package/cli/src/manifest.mjs +163 -0
- package/cli/src/strip.mjs +208 -0
- package/cli/src/tokens.mjs +102 -0
- package/cli/stack.mjs +672 -0
- package/package.json +37 -0
- package/templates/nest-api/files/.bin/db.ts +123 -0
- package/templates/nest-api/files/.bin/dc.ts +74 -0
- package/templates/nest-api/files/.bin/helpers.ts +49 -0
- package/templates/nest-api/files/.bin/secrets.ts +68 -0
- package/templates/nest-api/files/.bin/start.ts +47 -0
- package/templates/nest-api/files/.dockerignore +7 -0
- package/templates/nest-api/files/.env.example +91 -0
- package/templates/nest-api/files/.env.test.example +75 -0
- package/templates/nest-api/files/.forgejo/workflows/build-and-push.yml +41 -0
- package/templates/nest-api/files/.forgejo/workflows/test-e2e.yml +83 -0
- package/templates/nest-api/files/.forgejo/workflows/test.yml +53 -0
- package/templates/nest-api/files/.npmrc.example +1 -0
- package/templates/nest-api/files/.nvmrc +1 -0
- package/templates/nest-api/files/.prettierrc +8 -0
- package/templates/nest-api/files/.swcrc +15 -0
- package/templates/nest-api/files/.watchmanconfig +3 -0
- package/templates/nest-api/files/.yarnrc.yml +1 -0
- package/templates/nest-api/files/Dockerfile +40 -0
- package/templates/nest-api/files/__mocks__/activation-twofa.js +7 -0
- package/templates/nest-api/files/__mocks__/activation.js +7 -0
- package/templates/nest-api/files/__mocks__/forgot-password.js +7 -0
- package/templates/nest-api/files/__mocks__/main-layout.js +7 -0
- package/templates/nest-api/files/__mocks__/password-changed.js +7 -0
- package/templates/nest-api/files/__mocks__/reset-password.js +7 -0
- package/templates/nest-api/files/__mocks__/set-password.js +7 -0
- package/templates/nest-api/files/__mocks__/welcome.js +7 -0
- package/templates/nest-api/files/_gitignore +62 -0
- package/templates/nest-api/files/compose.test.yml +33 -0
- package/templates/nest-api/files/compose.yml +39 -0
- package/templates/nest-api/files/emails/activation-twofa.tsx +58 -0
- package/templates/nest-api/files/emails/activation.tsx +36 -0
- package/templates/nest-api/files/emails/forgot-password.tsx +29 -0
- package/templates/nest-api/files/emails/main-layout.tsx +134 -0
- package/templates/nest-api/files/emails/password-changed.tsx +24 -0
- package/templates/nest-api/files/emails/reset-password.tsx +24 -0
- package/templates/nest-api/files/emails/set-password.tsx +25 -0
- package/templates/nest-api/files/emails/welcome.tsx +39 -0
- package/templates/nest-api/files/eslint.config.mjs +28 -0
- package/templates/nest-api/files/global.d.ts +5 -0
- package/templates/nest-api/files/nest-cli.json +8 -0
- package/templates/nest-api/files/package.json +150 -0
- package/templates/nest-api/files/prisma/schema/auth.prisma +75 -0
- package/templates/nest-api/files/prisma/schema/base.prisma +17 -0
- package/templates/nest-api/files/prisma/schema/enum.prisma +17 -0
- package/templates/nest-api/files/prisma/schema/notification.prisma +14 -0
- package/templates/nest-api/files/prisma.config.ts +13 -0
- package/templates/nest-api/files/public/.gitkeep +0 -0
- package/templates/nest-api/files/seeders/index.ts +27 -0
- package/templates/nest-api/files/seeders/util.ts +88 -0
- package/templates/nest-api/files/src/__tests__/app.controller.spec.ts +29 -0
- package/templates/nest-api/files/src/app.controller.ts +40 -0
- package/templates/nest-api/files/src/app.module.ts +268 -0
- package/templates/nest-api/files/src/app.resolver.ts +32 -0
- package/templates/nest-api/files/src/app.setup.ts +178 -0
- package/templates/nest-api/files/src/common/__tests__/auth.service.spec.ts +31 -0
- package/templates/nest-api/files/src/common/__tests__/cache.service.spec.ts +214 -0
- package/templates/nest-api/files/src/common/__tests__/config.service.spec.ts +56 -0
- package/templates/nest-api/files/src/common/__tests__/generator.service.spec.ts +114 -0
- package/templates/nest-api/files/src/common/__tests__/health.controller.spec.ts +72 -0
- package/templates/nest-api/files/src/common/__tests__/helpers.spec.ts +124 -0
- package/templates/nest-api/files/src/common/__tests__/mail.service.spec.ts +75 -0
- package/templates/nest-api/files/src/common/__tests__/prisma.indicator.spec.ts +39 -0
- package/templates/nest-api/files/src/common/__tests__/prisma.service.spec.ts +15 -0
- package/templates/nest-api/files/src/common/__tests__/records.spec.ts +87 -0
- package/templates/nest-api/files/src/common/__tests__/schemas.spec.ts +142 -0
- package/templates/nest-api/files/src/common/__tests__/throttler-storage.service.spec.ts +80 -0
- package/templates/nest-api/files/src/common/common.module.ts +72 -0
- package/templates/nest-api/files/src/common/constants/index.ts +2 -0
- package/templates/nest-api/files/src/common/constants/options.ts +52 -0
- package/templates/nest-api/files/src/common/constants/pino.ts +24 -0
- package/templates/nest-api/files/src/common/controllers/health.controller.ts +54 -0
- package/templates/nest-api/files/src/common/controllers/index.ts +2 -0
- package/templates/nest-api/files/src/common/controllers/metrics.controller.ts +21 -0
- package/templates/nest-api/files/src/common/decorators/index.ts +2 -0
- package/templates/nest-api/files/src/common/decorators/misc.decorator.ts +49 -0
- package/templates/nest-api/files/src/common/decorators/multi-part.decorator.ts +6 -0
- package/templates/nest-api/files/src/common/dto/filters.dto.ts +25 -0
- package/templates/nest-api/files/src/common/dto/index.ts +1 -0
- package/templates/nest-api/files/src/common/entity/index.ts +2 -0
- package/templates/nest-api/files/src/common/entity/ok.entity.ts +11 -0
- package/templates/nest-api/files/src/common/entity/pagination.entity.ts +24 -0
- package/templates/nest-api/files/src/common/exceptions/index.ts +1 -0
- package/templates/nest-api/files/src/common/exceptions/prisma.exception.ts +57 -0
- package/templates/nest-api/files/src/common/guards/feature-flag.guard.ts +51 -0
- package/templates/nest-api/files/src/common/guards/index.ts +1 -0
- package/templates/nest-api/files/src/common/interceptors/index.ts +1 -0
- package/templates/nest-api/files/src/common/interceptors/metrics.interceptor.ts +37 -0
- package/templates/nest-api/files/src/common/misc/helpers.ts +26 -0
- package/templates/nest-api/files/src/common/misc/index.ts +3 -0
- package/templates/nest-api/files/src/common/misc/origin-builder.ts +56 -0
- package/templates/nest-api/files/src/common/misc/prisma.indicator.ts +24 -0
- package/templates/nest-api/files/src/common/modules/index.ts +3 -0
- package/templates/nest-api/files/src/common/modules/logger.module.ts +104 -0
- package/templates/nest-api/files/src/common/modules/metrics.module.ts +28 -0
- package/templates/nest-api/files/src/common/modules/queue.module.ts +23 -0
- package/templates/nest-api/files/src/common/processors/index.ts +1 -0
- package/templates/nest-api/files/src/common/processors/mail/index.ts +47 -0
- package/templates/nest-api/files/src/common/processors/mail/process.ts +225 -0
- package/templates/nest-api/files/src/common/schemas/fields.ts +76 -0
- package/templates/nest-api/files/src/common/schemas/index.ts +1 -0
- package/templates/nest-api/files/src/common/services/auth-rate-limit.storage.ts +79 -0
- package/templates/nest-api/files/src/common/services/auth.service.ts +16 -0
- package/templates/nest-api/files/src/common/services/cache.service.ts +118 -0
- package/templates/nest-api/files/src/common/services/config.service.ts +388 -0
- package/templates/nest-api/files/src/common/services/feature-flag.service.ts +117 -0
- package/templates/nest-api/files/src/common/services/generator.service.ts +54 -0
- package/templates/nest-api/files/src/common/services/index.ts +10 -0
- package/templates/nest-api/files/src/common/services/logger.service.ts +14 -0
- package/templates/nest-api/files/src/common/services/mail.service.ts +44 -0
- package/templates/nest-api/files/src/common/services/prisma.service.ts +106 -0
- package/templates/nest-api/files/src/common/services/throttler-storage.service.ts +112 -0
- package/templates/nest-api/files/src/common/types/email.ts +55 -0
- package/templates/nest-api/files/src/common/types/index.ts +3 -0
- package/templates/nest-api/files/src/common/types/misc.ts +20 -0
- package/templates/nest-api/files/src/common/types/prisma.ts +18 -0
- package/templates/nest-api/files/src/common/utils/enum.ts +39 -0
- package/templates/nest-api/files/src/common/utils/helpers.ts +139 -0
- package/templates/nest-api/files/src/common/utils/index.ts +5 -0
- package/templates/nest-api/files/src/common/utils/ip.ts +22 -0
- package/templates/nest-api/files/src/common/utils/records.ts +66 -0
- package/templates/nest-api/files/src/common/utils/redis.keys.ts +31 -0
- package/templates/nest-api/files/src/lib/__tests__/access.spec.ts +33 -0
- package/templates/nest-api/files/src/lib/access.ts +114 -0
- package/templates/nest-api/files/src/lib/auth.ts +76 -0
- package/templates/nest-api/files/src/lib/rate-limit.ts +20 -0
- package/templates/nest-api/files/src/main.ts +28 -0
- package/templates/nest-api/files/src/media/__tests__/media.service.spec.ts +60 -0
- package/templates/nest-api/files/src/media/__tests__/s3.service.spec.ts +159 -0
- package/templates/nest-api/files/src/media/entity/file.entity.ts +7 -0
- package/templates/nest-api/files/src/media/entity/index.ts +1 -0
- package/templates/nest-api/files/src/media/media.module.ts +20 -0
- package/templates/nest-api/files/src/media/processors/index.ts +44 -0
- package/templates/nest-api/files/src/media/services/index.ts +2 -0
- package/templates/nest-api/files/src/media/services/media.service.ts +29 -0
- package/templates/nest-api/files/src/media/services/s3.service.ts +168 -0
- package/templates/nest-api/files/src/notifications/controllers/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/controllers/notifications.controller.ts +72 -0
- package/templates/nest-api/files/src/notifications/dto/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/dto/notification.dto.ts +11 -0
- package/templates/nest-api/files/src/notifications/entity/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/entity/notification.entity.ts +37 -0
- package/templates/nest-api/files/src/notifications/notifications.module.ts +11 -0
- package/templates/nest-api/files/src/notifications/services/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/services/notifications.service.ts +98 -0
- package/templates/nest-api/files/src/realtime/__tests__/events.gateway.spec.ts +88 -0
- package/templates/nest-api/files/src/realtime/__tests__/rooms.spec.ts +29 -0
- package/templates/nest-api/files/src/realtime/__tests__/ws-permissions.guard.spec.ts +54 -0
- package/templates/nest-api/files/src/realtime/adapters/redis-io.adapter.ts +57 -0
- package/templates/nest-api/files/src/realtime/constants/index.ts +68 -0
- package/templates/nest-api/files/src/realtime/decorators/index.ts +1 -0
- package/templates/nest-api/files/src/realtime/decorators/ws-permissions.decorator.ts +14 -0
- package/templates/nest-api/files/src/realtime/gateways/events.gateway.ts +187 -0
- package/templates/nest-api/files/src/realtime/guards/index.ts +2 -0
- package/templates/nest-api/files/src/realtime/guards/ws-auth.guard.ts +20 -0
- package/templates/nest-api/files/src/realtime/guards/ws-permissions.guard.ts +35 -0
- package/templates/nest-api/files/src/realtime/misc/session-from-socket.ts +54 -0
- package/templates/nest-api/files/src/realtime/realtime.module.ts +26 -0
- package/templates/nest-api/files/src/realtime/services/presence.service.ts +56 -0
- package/templates/nest-api/files/src/realtime/services/realtime.service.ts +72 -0
- package/templates/nest-api/files/src/users/__tests__/current-user.controller.spec.ts +77 -0
- package/templates/nest-api/files/src/users/__tests__/current-user.service.spec.ts +159 -0
- package/templates/nest-api/files/src/users/__tests__/user.entity.spec.ts +175 -0
- package/templates/nest-api/files/src/users/__tests__/users.controller.spec.ts +89 -0
- package/templates/nest-api/files/src/users/__tests__/users.service.spec.ts +398 -0
- package/templates/nest-api/files/src/users/controllers/current-user.controller.ts +82 -0
- package/templates/nest-api/files/src/users/controllers/index.ts +2 -0
- package/templates/nest-api/files/src/users/controllers/users.controller.ts +40 -0
- package/templates/nest-api/files/src/users/decorators/index.ts +1 -0
- package/templates/nest-api/files/src/users/decorators/permissions.decorator.ts +24 -0
- package/templates/nest-api/files/src/users/dto/avatar.dto.ts +13 -0
- package/templates/nest-api/files/src/users/dto/filters.dto.ts +8 -0
- package/templates/nest-api/files/src/users/dto/index.ts +2 -0
- package/templates/nest-api/files/src/users/entity/index.ts +1 -0
- package/templates/nest-api/files/src/users/entity/user.entity.ts +53 -0
- package/templates/nest-api/files/src/users/services/current-user.service.ts +46 -0
- package/templates/nest-api/files/src/users/services/index.ts +2 -0
- package/templates/nest-api/files/src/users/services/users.service.ts +88 -0
- package/templates/nest-api/files/src/users/users.module.ts +10 -0
- package/templates/nest-api/files/test/app.spec.ts +102 -0
- package/templates/nest-api/files/test/auth.spec.ts +108 -0
- package/templates/nest-api/files/test/current-user.spec.ts +154 -0
- package/templates/nest-api/files/test/factory/app.ts +172 -0
- package/templates/nest-api/files/test/factory/constants.ts +22 -0
- package/templates/nest-api/files/test/factory/dto.ts +27 -0
- package/templates/nest-api/files/test/factory/entity.ts +70 -0
- package/templates/nest-api/files/test/factory/gen.ts +75 -0
- package/templates/nest-api/files/test/factory/util.ts +21 -0
- package/templates/nest-api/files/test/notifications.spec.ts +176 -0
- package/templates/nest-api/files/test/rate-limit.spec.ts +180 -0
- package/templates/nest-api/files/test/setup.ts +1 -0
- package/templates/nest-api/files/test/users.spec.ts +437 -0
- package/templates/nest-api/files/tsconfig.build.json +13 -0
- package/templates/nest-api/files/tsconfig.json +35 -0
- package/templates/nest-api/files/vitest.config.ts +53 -0
- package/templates/nest-api/template.json +452 -0
- package/templates/node-worker/files/.bin/db.ts +123 -0
- package/templates/node-worker/files/.bin/helpers.ts +49 -0
- package/templates/node-worker/files/.bin/secrets.ts +68 -0
- package/templates/node-worker/files/.dockerignore +5 -0
- package/templates/node-worker/files/.env.example +21 -0
- package/templates/node-worker/files/.forgejo/workflows/build-and-push.yml +41 -0
- package/templates/node-worker/files/.nvmrc +1 -0
- package/templates/node-worker/files/.prettierignore +4 -0
- package/templates/node-worker/files/.prettierrc +8 -0
- package/templates/node-worker/files/.yarnrc.yml +5 -0
- package/templates/node-worker/files/Dockerfile +63 -0
- package/templates/node-worker/files/Dockerfile.dev +59 -0
- package/templates/node-worker/files/_gitignore +48 -0
- package/templates/node-worker/files/ecosystem.config.cjs +10 -0
- package/templates/node-worker/files/eslint.config.js +37 -0
- package/templates/node-worker/files/package.json +67 -0
- package/templates/node-worker/files/prisma/_gitignore +1 -0
- package/templates/node-worker/files/prisma/schema/base.prisma +36 -0
- package/templates/node-worker/files/prisma.config.ts +13 -0
- package/templates/node-worker/files/src/config.ts +55 -0
- package/templates/node-worker/files/src/db.ts +118 -0
- package/templates/node-worker/files/src/index.ts +7 -0
- package/templates/node-worker/files/src/logger.ts +45 -0
- package/templates/node-worker/files/src/types.ts +23 -0
- package/templates/node-worker/files/src/utils/browser.ts +16 -0
- package/templates/node-worker/files/src/utils/date.ts +68 -0
- package/templates/node-worker/files/src/utils/helpers.ts +17 -0
- package/templates/node-worker/files/src/utils/number.ts +47 -0
- package/templates/node-worker/files/src/utils/worker.ts +29 -0
- package/templates/node-worker/files/src/workers/constants.ts +6 -0
- package/templates/node-worker/files/src/workers/example/index.ts +27 -0
- package/templates/node-worker/files/src/workers/index.ts +1 -0
- package/templates/node-worker/files/tsconfig.json +30 -0
- package/templates/node-worker/template.json +135 -0
- package/templates/py-worker/files/.dockerignore +176 -0
- package/templates/py-worker/files/.env.example +20 -0
- package/templates/py-worker/files/.forgejo/workflows/build-amd-push.yml +39 -0
- package/templates/py-worker/files/Dockerfile +10 -0
- package/templates/py-worker/files/README.md +3 -0
- package/templates/py-worker/files/_gitignore +176 -0
- package/templates/py-worker/files/config.py +89 -0
- package/templates/py-worker/files/models/decorators.py +42 -0
- package/templates/py-worker/files/models/enums.py +32 -0
- package/templates/py-worker/files/models/mixins.py +61 -0
- package/templates/py-worker/files/requirements.txt +18 -0
- package/templates/py-worker/files/utils/db.py +67 -0
- package/templates/py-worker/files/utils/logger.py +97 -0
- package/templates/py-worker/files/utils/misc.py +45 -0
- package/templates/py-worker/files/utils/worker.py +80 -0
- package/templates/py-worker/files/worker.py +41 -0
- package/templates/py-worker/files/workers/hello/process.py +22 -0
- package/templates/py-worker/template.json +87 -0
- package/templates/react-app/README.md +78 -0
- package/templates/react-app/derive.sh +128 -0
- package/templates/react-app/files/.bin/openapi.js +32 -0
- package/templates/react-app/files/.bin/secrets.js +47 -0
- package/templates/react-app/files/.dockerignore +16 -0
- package/templates/react-app/files/.editorconfig +12 -0
- package/templates/react-app/files/.env.example +34 -0
- package/templates/react-app/files/.forgejo/workflows/build-and-push.yml +55 -0
- package/templates/react-app/files/.forgejo/workflows/ci.yml +39 -0
- package/templates/react-app/files/.husky/pre-commit +1 -0
- package/templates/react-app/files/.nvmrc +1 -0
- package/templates/react-app/files/.prettierignore +6 -0
- package/templates/react-app/files/.prettierrc +9 -0
- package/templates/react-app/files/.yarnrc.yml +2 -0
- package/templates/react-app/files/Dockerfile +52 -0
- package/templates/react-app/files/_gitignore +33 -0
- package/templates/react-app/files/build.sh +53 -0
- package/templates/react-app/files/docker/nginx.conf +134 -0
- package/templates/react-app/files/eslint.config.mjs +75 -0
- package/templates/react-app/files/index.html +62 -0
- package/templates/react-app/files/openapi-ts.config.ts +22 -0
- package/templates/react-app/files/package.json +80 -0
- package/templates/react-app/files/src/app/nav.tsx +32 -0
- package/templates/react-app/files/src/app/providers.tsx +39 -0
- package/templates/react-app/files/src/app/router.tsx +142 -0
- package/templates/react-app/files/src/app/store.ts +20 -0
- package/templates/react-app/files/src/features/auth/components/auth-card.tsx +23 -0
- package/templates/react-app/files/src/features/auth/components/checkbox-field.tsx +32 -0
- package/templates/react-app/files/src/features/auth/components/google-button.tsx +41 -0
- package/templates/react-app/files/src/features/auth/components/or-divider.tsx +9 -0
- package/templates/react-app/files/src/features/auth/components/password-field.tsx +70 -0
- package/templates/react-app/files/src/features/auth/components/submit-button.tsx +16 -0
- package/templates/react-app/files/src/features/auth/lib/auth-actions.ts +68 -0
- package/templates/react-app/files/src/features/auth/lib/password-strength.ts +20 -0
- package/templates/react-app/files/src/features/auth/lib/redirect.ts +6 -0
- package/templates/react-app/files/src/features/auth/lib/schemas.ts +37 -0
- package/templates/react-app/files/src/features/seo/routes.ts +65 -0
- package/templates/react-app/files/src/lib/env.ts +28 -0
- package/templates/react-app/files/src/lib/paths.ts +58 -0
- package/templates/react-app/files/src/main.tsx +19 -0
- package/templates/react-app/files/src/routes/admin/home.tsx +24 -0
- package/templates/react-app/files/src/routes/auth/forgot-password.tsx +93 -0
- package/templates/react-app/files/src/routes/auth/login.tsx +87 -0
- package/templates/react-app/files/src/routes/auth/reset-password.tsx +83 -0
- package/templates/react-app/files/src/routes/auth/signup.tsx +117 -0
- package/templates/react-app/files/src/routes/auth/verified.tsx +31 -0
- package/templates/react-app/files/src/routes/auth/verify-email.tsx +97 -0
- package/templates/react-app/files/src/routes/home.tsx +17 -0
- package/templates/react-app/files/src/routes/legal/privacy.tsx +26 -0
- package/templates/react-app/files/src/routes/legal/terms.tsx +28 -0
- package/templates/react-app/files/src/routes/marketing/home.tsx +18 -0
- package/templates/react-app/files/src/routes/settings.tsx +10 -0
- package/templates/react-app/files/src/shared/api/base-api.ts +24 -0
- package/templates/react-app/files/src/shared/api/base-query.ts +29 -0
- package/templates/react-app/files/src/shared/api/config.ts +21 -0
- package/templates/react-app/files/src/shared/api/index.ts +15 -0
- package/templates/react-app/files/src/shared/api/realtime-events.ts +34 -0
- package/templates/react-app/files/src/shared/api/socket.ts +41 -0
- package/templates/react-app/files/src/shared/api/store.ts +39 -0
- package/templates/react-app/files/src/shared/api/upload-query.ts +115 -0
- package/templates/react-app/files/src/shared/auth/client.ts +27 -0
- package/templates/react-app/files/src/shared/auth/config.ts +28 -0
- package/templates/react-app/files/src/shared/auth/guards.tsx +75 -0
- package/templates/react-app/files/src/shared/auth/hooks.ts +13 -0
- package/templates/react-app/files/src/shared/auth/index.ts +20 -0
- package/templates/react-app/files/src/shared/auth/logout.ts +23 -0
- package/templates/react-app/files/src/shared/auth/provider.tsx +52 -0
- package/templates/react-app/files/src/shared/auth/redirect.ts +65 -0
- package/templates/react-app/files/src/shared/auth/slice.ts +54 -0
- package/templates/react-app/files/src/shared/hooks/index.ts +6 -0
- package/templates/react-app/files/src/shared/layouts/app-shell/app-shell.tsx +225 -0
- package/templates/react-app/files/src/shared/layouts/app-shell/user-menu.tsx +74 -0
- package/templates/react-app/files/src/shared/layouts/auth-layout.tsx +137 -0
- package/templates/react-app/files/src/shared/layouts/error-boundary.tsx +38 -0
- package/templates/react-app/files/src/shared/layouts/index.ts +23 -0
- package/templates/react-app/files/src/shared/layouts/marketing/marketing-layout.tsx +29 -0
- package/templates/react-app/files/src/shared/layouts/marketing/section-heading.tsx +38 -0
- package/templates/react-app/files/src/shared/layouts/marketing/section.tsx +30 -0
- package/templates/react-app/files/src/shared/layouts/states/app-shell-skeleton.tsx +47 -0
- package/templates/react-app/files/src/shared/layouts/states/empty.tsx +22 -0
- package/templates/react-app/files/src/shared/layouts/states/forbidden.tsx +14 -0
- package/templates/react-app/files/src/shared/layouts/states/loading.tsx +11 -0
- package/templates/react-app/files/src/shared/layouts/states/not-found.tsx +21 -0
- package/templates/react-app/files/src/shared/layouts/states/page-skeleton.tsx +39 -0
- package/templates/react-app/files/src/shared/layouts/states/route-fallback.tsx +26 -0
- package/templates/react-app/files/src/shared/layouts/states/state-screen.tsx +22 -0
- package/templates/react-app/files/src/shared/types/README.md +28 -0
- package/templates/react-app/files/src/shared/types/api/index.ts +9 -0
- package/templates/react-app/files/src/shared/types/api/zod.gen.ts +5 -0
- package/templates/react-app/files/src/shared/types/index.ts +4 -0
- package/templates/react-app/files/src/shared/ui/components/brand/logo.tsx +51 -0
- package/templates/react-app/files/src/shared/ui/components/brand/social-icons.tsx +37 -0
- package/templates/react-app/files/src/shared/ui/components/form/form.tsx +120 -0
- package/templates/react-app/files/src/shared/ui/components/ui/alert.tsx +25 -0
- package/templates/react-app/files/src/shared/ui/components/ui/button.tsx +43 -0
- package/templates/react-app/files/src/shared/ui/components/ui/calendar.tsx +78 -0
- package/templates/react-app/files/src/shared/ui/components/ui/card.tsx +44 -0
- package/templates/react-app/files/src/shared/ui/components/ui/checkbox.tsx +16 -0
- package/templates/react-app/files/src/shared/ui/components/ui/date-picker.tsx +76 -0
- package/templates/react-app/files/src/shared/ui/components/ui/input.tsx +22 -0
- package/templates/react-app/files/src/shared/ui/components/ui/label.tsx +19 -0
- package/templates/react-app/files/src/shared/ui/components/ui/modal.tsx +94 -0
- package/templates/react-app/files/src/shared/ui/components/ui/popover.tsx +31 -0
- package/templates/react-app/files/src/shared/ui/components/ui/progress.tsx +33 -0
- package/templates/react-app/files/src/shared/ui/components/ui/search-input.tsx +69 -0
- package/templates/react-app/files/src/shared/ui/components/ui/select.tsx +80 -0
- package/templates/react-app/files/src/shared/ui/components/ui/skeleton.tsx +12 -0
- package/templates/react-app/files/src/shared/ui/components/ui/sonner.tsx +23 -0
- package/templates/react-app/files/src/shared/ui/components/ui/spinner.tsx +8 -0
- package/templates/react-app/files/src/shared/ui/components/ui/textarea.tsx +22 -0
- package/templates/react-app/files/src/shared/ui/index.ts +45 -0
- package/templates/react-app/files/src/shared/ui/lib/utils.ts +7 -0
- package/templates/react-app/files/src/shared/ui/lib/zod-setup.ts +28 -0
- package/templates/react-app/files/src/shared/ui/meta/brand.ts +18 -0
- package/templates/react-app/files/src/shared/ui/meta/meta-context.ts +30 -0
- package/templates/react-app/files/src/shared/ui/meta/meta-provider.tsx +24 -0
- package/templates/react-app/files/src/shared/ui/meta/page-meta.tsx +69 -0
- package/templates/react-app/files/src/shared/ui/meta/use-meta-config.ts +11 -0
- package/templates/react-app/files/src/shared/ui/styles/globals.css +285 -0
- package/templates/react-app/files/src/shared/ui/styles/theme.css +48 -0
- package/templates/react-app/files/src/shared/ui/theme/init-script.ts +23 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-colors.ts +14 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-context.ts +60 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-provider.tsx +101 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-toggle.tsx +40 -0
- package/templates/react-app/files/src/shared/ui/theme/use-theme.ts +11 -0
- package/templates/react-app/files/src/vite-env.d.ts +13 -0
- package/templates/react-app/files/tsconfig.json +26 -0
- package/templates/react-app/files/tsconfig.node.json +25 -0
- package/templates/react-app/files/vite.config.ts +344 -0
- package/templates/react-app/overrides/src/routes/auth/forgot-password.tsx +93 -0
- package/templates/react-app/overrides/src/routes/auth/login.tsx +87 -0
- package/templates/react-app/overrides/src/routes/auth/signup.tsx +117 -0
- package/templates/react-app/overrides/src/routes/auth/verified.tsx +31 -0
- package/templates/react-app/overrides/src/routes/auth/verify-email.tsx +97 -0
- package/templates/react-app/overrides/src/shared/auth/config.ts +28 -0
- package/templates/react-app/overrides/src/shared/auth/guards.tsx +75 -0
- package/templates/react-app/overrides/src/shared/auth/index.ts +20 -0
- package/templates/react-app/overrides/src/shared/auth/redirect.ts +65 -0
- package/templates/react-app/overrides/src/shared/hooks/index.ts +6 -0
- package/templates/react-app/overrides/src/shared/types/README.md +28 -0
- package/templates/react-app/template.json +222 -0
- package/templates/react-monorepo/files/.dockerignore +19 -0
- package/templates/react-monorepo/files/.editorconfig +12 -0
- package/templates/react-monorepo/files/.forgejo/workflows/build-and-push.yml +98 -0
- package/templates/react-monorepo/files/.forgejo/workflows/ci.yml +35 -0
- package/templates/react-monorepo/files/.husky/pre-commit +1 -0
- package/templates/react-monorepo/files/.nvmrc +1 -0
- package/templates/react-monorepo/files/.prettierignore +6 -0
- package/templates/react-monorepo/files/.prettierrc +9 -0
- package/templates/react-monorepo/files/.yarnrc.yml +2 -0
- package/templates/react-monorepo/files/Dockerfile +90 -0
- package/templates/react-monorepo/files/_gitignore +35 -0
- package/templates/react-monorepo/files/apps/admin/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/admin/.env.example +16 -0
- package/templates/react-monorepo/files/apps/admin/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/admin/index.html +57 -0
- package/templates/react-monorepo/files/apps/admin/package.json +33 -0
- package/templates/react-monorepo/files/apps/admin/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/admin/src/app/nav.tsx +14 -0
- package/templates/react-monorepo/files/apps/admin/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/admin/src/app/router.tsx +50 -0
- package/templates/react-monorepo/files/apps/admin/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/admin/src/lib/env.ts +12 -0
- package/templates/react-monorepo/files/apps/admin/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/admin/src/routes/home.tsx +17 -0
- package/templates/react-monorepo/files/apps/admin/src/routes/settings.tsx +10 -0
- package/templates/react-monorepo/files/apps/admin/src/vite-env.d.ts +14 -0
- package/templates/react-monorepo/files/apps/admin/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/admin/vite.config.ts +13 -0
- package/templates/react-monorepo/files/apps/auth/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/auth/.env.example +16 -0
- package/templates/react-monorepo/files/apps/auth/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/auth/index.html +57 -0
- package/templates/react-monorepo/files/apps/auth/package.json +32 -0
- package/templates/react-monorepo/files/apps/auth/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/auth/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/auth/src/app/router.tsx +19 -0
- package/templates/react-monorepo/files/apps/auth/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/auth/src/components/auth-card.tsx +23 -0
- package/templates/react-monorepo/files/apps/auth/src/components/checkbox-field.tsx +32 -0
- package/templates/react-monorepo/files/apps/auth/src/components/google-button.tsx +41 -0
- package/templates/react-monorepo/files/apps/auth/src/components/or-divider.tsx +9 -0
- package/templates/react-monorepo/files/apps/auth/src/components/password-field.tsx +70 -0
- package/templates/react-monorepo/files/apps/auth/src/components/submit-button.tsx +16 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/auth-actions.ts +68 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/env.ts +8 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/password-strength.ts +20 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/redirect.ts +6 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/schemas.ts +37 -0
- package/templates/react-monorepo/files/apps/auth/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/forgot-password.tsx +91 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/login.tsx +82 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/reset-password.tsx +83 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/signup.tsx +117 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/verified.tsx +30 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/verify-email.tsx +96 -0
- package/templates/react-monorepo/files/apps/auth/src/vite-env.d.ts +14 -0
- package/templates/react-monorepo/files/apps/auth/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/auth/vite.config.ts +13 -0
- package/templates/react-monorepo/files/apps/client/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/client/.env.example +16 -0
- package/templates/react-monorepo/files/apps/client/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/client/index.html +57 -0
- package/templates/react-monorepo/files/apps/client/package.json +35 -0
- package/templates/react-monorepo/files/apps/client/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/client/src/app/nav.tsx +14 -0
- package/templates/react-monorepo/files/apps/client/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/client/src/app/router.tsx +50 -0
- package/templates/react-monorepo/files/apps/client/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/client/src/lib/env.ts +12 -0
- package/templates/react-monorepo/files/apps/client/src/main.tsx +19 -0
- package/templates/react-monorepo/files/apps/client/src/routes/home.tsx +17 -0
- package/templates/react-monorepo/files/apps/client/src/routes/settings.tsx +10 -0
- package/templates/react-monorepo/files/apps/client/src/vite-env.d.ts +15 -0
- package/templates/react-monorepo/files/apps/client/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/client/vite.config.ts +120 -0
- package/templates/react-monorepo/files/apps/landing/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/landing/.env.example +20 -0
- package/templates/react-monorepo/files/apps/landing/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/landing/index.html +51 -0
- package/templates/react-monorepo/files/apps/landing/package.json +33 -0
- package/templates/react-monorepo/files/apps/landing/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/landing/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/landing/src/app/router.tsx +21 -0
- package/templates/react-monorepo/files/apps/landing/src/app/store.ts +18 -0
- package/templates/react-monorepo/files/apps/landing/src/features/seo/routes.ts +65 -0
- package/templates/react-monorepo/files/apps/landing/src/lib/env.ts +10 -0
- package/templates/react-monorepo/files/apps/landing/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/landing/src/routes/home.tsx +18 -0
- package/templates/react-monorepo/files/apps/landing/src/vite-env.d.ts +15 -0
- package/templates/react-monorepo/files/apps/landing/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/landing/vite.config.ts +169 -0
- package/templates/react-monorepo/files/build.sh +61 -0
- package/templates/react-monorepo/files/docker/nginx.conf +136 -0
- package/templates/react-monorepo/files/package.json +37 -0
- package/templates/react-monorepo/files/packages/.gitkeep +0 -0
- package/templates/react-monorepo/files/packages/api/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/api/package.json +30 -0
- package/templates/react-monorepo/files/packages/api/src/base-api.ts +24 -0
- package/templates/react-monorepo/files/packages/api/src/base-query.ts +29 -0
- package/templates/react-monorepo/files/packages/api/src/config.ts +21 -0
- package/templates/react-monorepo/files/packages/api/src/index.ts +15 -0
- package/templates/react-monorepo/files/packages/api/src/realtime-events.ts +34 -0
- package/templates/react-monorepo/files/packages/api/src/socket.ts +41 -0
- package/templates/react-monorepo/files/packages/api/src/store.ts +39 -0
- package/templates/react-monorepo/files/packages/api/src/upload-query.ts +115 -0
- package/templates/react-monorepo/files/packages/api/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/auth/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/auth/package.json +33 -0
- package/templates/react-monorepo/files/packages/auth/src/client.ts +27 -0
- package/templates/react-monorepo/files/packages/auth/src/config.ts +22 -0
- package/templates/react-monorepo/files/packages/auth/src/guards.tsx +61 -0
- package/templates/react-monorepo/files/packages/auth/src/hooks.ts +13 -0
- package/templates/react-monorepo/files/packages/auth/src/index.ts +20 -0
- package/templates/react-monorepo/files/packages/auth/src/logout.ts +23 -0
- package/templates/react-monorepo/files/packages/auth/src/provider.tsx +52 -0
- package/templates/react-monorepo/files/packages/auth/src/redirect.ts +37 -0
- package/templates/react-monorepo/files/packages/auth/src/slice.ts +54 -0
- package/templates/react-monorepo/files/packages/auth/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/config/README.md +47 -0
- package/templates/react-monorepo/files/packages/config/eslint/base.mjs +83 -0
- package/templates/react-monorepo/files/packages/config/package.json +37 -0
- package/templates/react-monorepo/files/packages/config/tailwind/theme.css +48 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/base.json +22 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/node.json +9 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/react.json +9 -0
- package/templates/react-monorepo/files/packages/config/vite/index.d.ts +18 -0
- package/templates/react-monorepo/files/packages/config/vite/index.mjs +129 -0
- package/templates/react-monorepo/files/packages/hooks/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/hooks/package.json +16 -0
- package/templates/react-monorepo/files/packages/hooks/src/index.ts +3 -0
- package/templates/react-monorepo/files/packages/hooks/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/layouts/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/layouts/package.json +30 -0
- package/templates/react-monorepo/files/packages/layouts/src/app-shell/app-shell.tsx +225 -0
- package/templates/react-monorepo/files/packages/layouts/src/app-shell/user-menu.tsx +74 -0
- package/templates/react-monorepo/files/packages/layouts/src/auth-layout.tsx +137 -0
- package/templates/react-monorepo/files/packages/layouts/src/error-boundary.tsx +38 -0
- package/templates/react-monorepo/files/packages/layouts/src/index.ts +23 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/marketing-layout.tsx +29 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/section-heading.tsx +38 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/section.tsx +30 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/app-shell-skeleton.tsx +47 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/empty.tsx +22 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/forbidden.tsx +14 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/loading.tsx +11 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/not-found.tsx +21 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/page-skeleton.tsx +39 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/route-fallback.tsx +26 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/state-screen.tsx +22 -0
- package/templates/react-monorepo/files/packages/layouts/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/types/.bin/openapi.js +32 -0
- package/templates/react-monorepo/files/packages/types/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/packages/types/README.md +28 -0
- package/templates/react-monorepo/files/packages/types/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/types/openapi-ts.config.ts +22 -0
- package/templates/react-monorepo/files/packages/types/package.json +29 -0
- package/templates/react-monorepo/files/packages/types/src/index.ts +4 -0
- package/templates/react-monorepo/files/packages/types/src/types/api/index.ts +9 -0
- package/templates/react-monorepo/files/packages/types/src/types/api/zod.gen.ts +5 -0
- package/templates/react-monorepo/files/packages/types/tsconfig.json +7 -0
- package/templates/react-monorepo/files/packages/ui/components.json +21 -0
- package/templates/react-monorepo/files/packages/ui/eslint.config.mjs +13 -0
- package/templates/react-monorepo/files/packages/ui/package.json +44 -0
- package/templates/react-monorepo/files/packages/ui/src/components/brand/logo.tsx +51 -0
- package/templates/react-monorepo/files/packages/ui/src/components/brand/social-icons.tsx +37 -0
- package/templates/react-monorepo/files/packages/ui/src/components/form/form.tsx +120 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/alert.tsx +25 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/button.tsx +43 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/calendar.tsx +78 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/card.tsx +44 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/checkbox.tsx +16 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/date-picker.tsx +76 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/input.tsx +22 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/label.tsx +19 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/modal.tsx +94 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/popover.tsx +31 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/progress.tsx +33 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/search-input.tsx +69 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/select.tsx +80 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/skeleton.tsx +12 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/sonner.tsx +23 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/spinner.tsx +8 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/textarea.tsx +22 -0
- package/templates/react-monorepo/files/packages/ui/src/index.ts +45 -0
- package/templates/react-monorepo/files/packages/ui/src/lib/utils.ts +7 -0
- package/templates/react-monorepo/files/packages/ui/src/lib/zod-setup.ts +28 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/brand.ts +18 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/meta-context.ts +30 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/meta-provider.tsx +24 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/page-meta.tsx +69 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/use-meta-config.ts +11 -0
- package/templates/react-monorepo/files/packages/ui/src/styles/globals.css +285 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/init-script.ts +23 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-colors.ts +14 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-context.ts +60 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-provider.tsx +101 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-toggle.tsx +40 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/use-theme.ts +11 -0
- package/templates/react-monorepo/files/packages/ui/tsconfig.json +9 -0
- package/templates/react-monorepo/files/turbo.json +24 -0
- package/templates/react-monorepo/template.json +230 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aurostack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# stacks
|
|
2
|
+
|
|
3
|
+
House project templates, and the generator that stamps them out.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
stack list # what's available
|
|
7
|
+
stack info nest-api # its features and defaults
|
|
8
|
+
stack new nest-api ~/Projects/acme/api --with realtime --without media
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The CLI is plain Node ESM (20.19+) with **no dependencies and no build step**.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
### In Claude Code
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/plugin marketplace add aurostack-org/stacks
|
|
19
|
+
/plugin install stacks@aurostack
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This adds two skills: `/stacks:stack-new` scaffolds a project (or just ask for
|
|
23
|
+
"a new API"), and `/stacks:stack-sync` folds improvements back into the
|
|
24
|
+
templates. The plugin carries the CLI and templates with it.
|
|
25
|
+
|
|
26
|
+
To have Claude Code offer it to everyone who opens a repository, commit this to
|
|
27
|
+
the repository's `.claude/settings.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"extraKnownMarketplaces": {
|
|
32
|
+
"aurostack": {
|
|
33
|
+
"source": { "source": "github", "repo": "aurostack-org/stacks" },
|
|
34
|
+
"autoUpdate": true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"enabledPlugins": {
|
|
38
|
+
"stacks@aurostack": true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### In a terminal or CI
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npx @aurostack/stacks new nest-api ./api # one-off
|
|
47
|
+
npm install -g @aurostack/stacks # or keep `stack` around
|
|
48
|
+
stack --version
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Maintaining the templates
|
|
52
|
+
|
|
53
|
+
Template work (`stack extract`, editing `templates/`, the `/stack-sync` skill)
|
|
54
|
+
happens in a git checkout — the plugin and the npm package are copies that the
|
|
55
|
+
next update replaces, and `stack extract` refuses to run from one:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
git clone https://github.com/aurostack-org/stacks.git
|
|
59
|
+
bash stacks/install.sh # links `stack` to this checkout
|
|
60
|
+
claude --plugin-dir ./stacks # try skill edits without reinstalling
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`package.json` holds the version; the plugin manifests repeat it, and
|
|
64
|
+
`stack doctor` fails if they drift.
|
|
65
|
+
|
|
66
|
+
## The templates
|
|
67
|
+
|
|
68
|
+
| Template | What you get |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `nest-api` | NestJS 12 + Prisma (Postgres) + Redis + BullMQ + better-auth + Vitest, Docker Compose for local Postgres/Redis, Scalar API reference at `/docs` |
|
|
71
|
+
| `react-monorepo` | Yarn workspaces + Turborepo; Vite/React 19 apps over shared packages — one RTK Query `baseApi`, better-auth session handling, a shadcn-style UI kit with light/dark theming, layouts, generated OpenAPI types |
|
|
72
|
+
| `react-app` | The same stack as one Vite app: shared code under `src/shared`, auth screens as routes, optional marketing site and admin console. One deployment instead of four |
|
|
73
|
+
| `node-worker` | Standalone TypeScript worker — BullMQ consumers, Prisma against the API's database, pino, PM2 + Docker |
|
|
74
|
+
| `py-worker` | Python BullMQ worker over the same queues, SQLAlchemy models on the same Postgres |
|
|
75
|
+
|
|
76
|
+
### Monolith or monorepo
|
|
77
|
+
|
|
78
|
+
`react-app` and `react-monorepo` are the same stack, and the choice between them
|
|
79
|
+
is a deployment decision, not a code one:
|
|
80
|
+
|
|
81
|
+
- **`react-app`** — one origin, one build, one image. The auth screens are routes
|
|
82
|
+
(`/login`), so the session guard navigates instead of bouncing across hosts, and
|
|
83
|
+
there is no cross-subdomain cookie to configure. Start here.
|
|
84
|
+
- **`react-monorepo`** — separate apps on separate origins, deployed and released
|
|
85
|
+
independently, sharing one session cookie across subdomains. Worth its extra
|
|
86
|
+
moving parts when a marketing site and an app genuinely ship on different
|
|
87
|
+
cadences, or when one surface needs to scale or be locked down on its own.
|
|
88
|
+
|
|
89
|
+
The shared code is literally the same files: `templates/react-app/derive.sh`
|
|
90
|
+
re-flattens `packages/*` into `src/shared/*` and rewrites the import specifiers,
|
|
91
|
+
so a fix in one lands in the other. See that template's README.
|
|
92
|
+
|
|
93
|
+
## How it works
|
|
94
|
+
|
|
95
|
+
Templates are **subtractive**. `templates/<name>/files` is a complete, runnable
|
|
96
|
+
app with *every* optional feature switched on, annotated with `@feature`
|
|
97
|
+
markers. Generating a project **deletes** what you did not ask for.
|
|
98
|
+
|
|
99
|
+
The point of that choice: the template itself stays installable, buildable and
|
|
100
|
+
testable. A template assembled from additive fragments is only ever exercised at
|
|
101
|
+
generation time, so it rots silently. This one you can `yarn install` and run.
|
|
102
|
+
|
|
103
|
+
Generation does four things:
|
|
104
|
+
|
|
105
|
+
1. drops files owned by a disabled feature (`features.<x>.files`);
|
|
106
|
+
2. strips `@feature` regions from the files that survive;
|
|
107
|
+
3. prunes `package.json` / `requirements.txt` entries the feature owned — JSON
|
|
108
|
+
cannot carry comments, so those are declared in the manifest instead;
|
|
109
|
+
4. applies the rename map, so `@acme/ui` becomes `@yourscope/ui`.
|
|
110
|
+
|
|
111
|
+
Then the post-generation hooks install dependencies and run the project's own
|
|
112
|
+
`yarn format`. That last step is not cosmetic: deleting a feature leaves code
|
|
113
|
+
that is *valid but no longer formatted* — an array that now fits on one line, a
|
|
114
|
+
blank line where a block used to be — and the generated CI runs `format:check`.
|
|
115
|
+
Formatting with the project's own Prettier is the only fix that generalises;
|
|
116
|
+
hand-tuning the template for how each combination happens to strip does not.
|
|
117
|
+
|
|
118
|
+
Template files hold real working identifiers rather than `__PLACEHOLDER__`
|
|
119
|
+
sludge — that is what keeps them compilable in place. Renaming is by declared
|
|
120
|
+
search/replace pairs in `template.json`.
|
|
121
|
+
|
|
122
|
+
### Marker syntax
|
|
123
|
+
|
|
124
|
+
Comment-syntax agnostic, so `//`, `#` and `<!-- -->` all work:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { Gateway } from 'realtime'; // @feature realtime
|
|
128
|
+
|
|
129
|
+
// @feature:start realtime, notifications ← OR across the list
|
|
130
|
+
// @feature:else ← kept only when none matched
|
|
131
|
+
// @feature:end
|
|
132
|
+
|
|
133
|
+
// @feature:start !realtime ← negation
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**One sharp edge.** An inline marker deletes exactly *one line*. Parking one on
|
|
137
|
+
the closing line of a multi-line import or object literal leaves the construct
|
|
138
|
+
half-open — and because the template has every feature on, it still compiles.
|
|
139
|
+
Nothing catches it until someone generates without that feature. Wrap
|
|
140
|
+
multi-line constructs in `@feature:start`/`@feature:end`. `stack doctor` checks
|
|
141
|
+
for this.
|
|
142
|
+
|
|
143
|
+
## stack doctor
|
|
144
|
+
|
|
145
|
+
Run it after touching a template. Across the two extremes of the feature space
|
|
146
|
+
*and* each optional feature flipped on its own, it verifies that:
|
|
147
|
+
|
|
148
|
+
- every `@feature` name is declared in the manifest (a typo'd
|
|
149
|
+
`@feature realtimee` silently deletes code otherwise);
|
|
150
|
+
- every feature's `files` globs match something;
|
|
151
|
+
- TypeScript brackets stay balanced after stripping;
|
|
152
|
+
- JSON still parses;
|
|
153
|
+
- **nothing imports a file the selection deleted** — the one that catches a
|
|
154
|
+
barrel re-exporting a module that is no longer there.
|
|
155
|
+
|
|
156
|
+
## Keeping templates current
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
stack extract nest-api # re-copy from the live repo
|
|
160
|
+
stack extract nest-api --prune # also drop files the source no longer has
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Extraction is deliberately mechanical — it copies what the template's source
|
|
164
|
+
config says. That config is maintainer-local: a gitignored `stacks.local.json`
|
|
165
|
+
at the repo root (or the file `STACKS_LOCAL` names) holding, per template, the
|
|
166
|
+
source repo's path, include/exclude globs, and the `replacements` that map the
|
|
167
|
+
source's identifiers onto the template's working ones (`Acme Corp`, `acme`).
|
|
168
|
+
The `/stack-sync` skill documents the format.
|
|
169
|
+
Files it would overwrite are reported as **conflicts** and left alone, because
|
|
170
|
+
those are usually the ones you hand-edited to add markers or strip domain code.
|
|
171
|
+
Files outside the include globs (a hand-written `.env.example`, a template-only
|
|
172
|
+
seeder) can never be pruned, by construction.
|
|
173
|
+
|
|
174
|
+
Two things never enter a template: **secrets** — `.env*` is excluded for exactly
|
|
175
|
+
this reason, and every template ships a hand-written `.env.example` instead —
|
|
176
|
+
and **migrations**, which belong to one database's history and would fight the
|
|
177
|
+
first migration a generated project creates.
|
|
178
|
+
|
|
179
|
+
## Two things learned the hard way
|
|
180
|
+
|
|
181
|
+
Both templates that use Yarn ship a **`.yarnrc.yml` with `nodeLinker:
|
|
182
|
+
node-modules`**. Under Yarn's default PnP linker, `ts-node` crashes on startup
|
|
183
|
+
and `prisma generate` fails with `EROFS` trying to place its engine binaries.
|
|
184
|
+
Everything else works under PnP; those two do not.
|
|
185
|
+
|
|
186
|
+
Templates with a Prisma schema ship at least **one model**. A model-less schema
|
|
187
|
+
generates no client at all, so every `@db/*` / `#prisma/*` import in a freshly
|
|
188
|
+
scaffolded project fails to resolve, and the error points at the imports rather
|
|
189
|
+
than at the schema.
|
|
190
|
+
|
|
191
|
+
## Layout
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
stacks/
|
|
195
|
+
├── package.json npm package @aurostack/stacks; the version lives here
|
|
196
|
+
├── .claude-plugin/ plugin + marketplace manifests
|
|
197
|
+
├── skills/
|
|
198
|
+
│ ├── stack-new/ scaffolding a new project
|
|
199
|
+
│ └── stack-sync/ folding changes back into the templates
|
|
200
|
+
├── cli/
|
|
201
|
+
│ ├── stack.mjs entry point
|
|
202
|
+
│ └── src/ manifest, generate, extract, strip, tokens, hooks
|
|
203
|
+
└── templates/<name>/
|
|
204
|
+
├── template.json features, rename map, post-gen hooks
|
|
205
|
+
└── files/ the runnable superset app
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Nothing here depends on where it lives: the CLI resolves the templates relative
|
|
209
|
+
to its own location, and the skills reach it through `${CLAUDE_PLUGIN_ROOT}`.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction: a live codebase -> templates/<t>/files.
|
|
3
|
+
*
|
|
4
|
+
* This is the *maintenance* direction. It is deliberately mechanical — it
|
|
5
|
+
* copies whatever the manifest's include/exclude globs say and nothing more.
|
|
6
|
+
* Deciding which files are generic infrastructure and which are domain code is
|
|
7
|
+
* judgement work; that judgement is recorded in the template's source config
|
|
8
|
+
* (stacks.local.json, see `sourceConfig`) and refined by the /stack-sync skill,
|
|
9
|
+
* not re-derived on every run. The config's `replacements` map the source's
|
|
10
|
+
* own identifiers onto the template's (e.g. its product name onto `Acme Corp`)
|
|
11
|
+
* as text files come in, so a refresh never reintroduces them.
|
|
12
|
+
*
|
|
13
|
+
* Extraction never deletes hand-written template files that the source does not
|
|
14
|
+
* have — `@feature` annotations added to a composition root would be lost
|
|
15
|
+
* otherwise. Files that already exist in the template are reported as
|
|
16
|
+
* "conflicts" and skipped unless --overwrite is passed.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import {
|
|
21
|
+
walk,
|
|
22
|
+
matcher,
|
|
23
|
+
isTextFile,
|
|
24
|
+
readText,
|
|
25
|
+
writeText,
|
|
26
|
+
copyBinary,
|
|
27
|
+
DEFAULT_SKIP_DIRS,
|
|
28
|
+
exists
|
|
29
|
+
} from './fsx.mjs';
|
|
30
|
+
import { expandHome } from './manifest.mjs';
|
|
31
|
+
|
|
32
|
+
export function extract(
|
|
33
|
+
manifest,
|
|
34
|
+
{ source, config = {}, overwrite = false, dryRun = false, prune = false }
|
|
35
|
+
) {
|
|
36
|
+
const src = expandHome(source || config.path);
|
|
37
|
+
const translate = (text) =>
|
|
38
|
+
(config.replacements || []).reduce(
|
|
39
|
+
(out, { from, to }) => out.split(from).join(to),
|
|
40
|
+
text
|
|
41
|
+
);
|
|
42
|
+
if (!src) {
|
|
43
|
+
// A template with `derivedFrom` is maintained from another template rather
|
|
44
|
+
// than from a live repo, so pointing extract at a source directory is not
|
|
45
|
+
// the fix — sending someone to `--source` would have them overwrite a
|
|
46
|
+
// derived tree with unflattened files.
|
|
47
|
+
if (manifest.derivedFrom) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Template "${manifest.name}" is derived from "${manifest.derivedFrom}", not extracted from a repo.\n` +
|
|
50
|
+
`Edit "${manifest.derivedFrom}" instead, then run:\n` +
|
|
51
|
+
` bash templates/${manifest.name}/derive.sh`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
throw new Error(
|
|
55
|
+
`No source path for "${manifest.name}": add it to stacks.local.json or pass --source <dir>.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (!exists(src)) {
|
|
59
|
+
throw new Error(`Source directory does not exist: ${src}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const include = matcher(config.include || ['**']);
|
|
63
|
+
const exclude = matcher(config.exclude || []);
|
|
64
|
+
const filesDir = manifest.__filesDir;
|
|
65
|
+
|
|
66
|
+
const candidates = walk(src, {
|
|
67
|
+
skipDirs: [...DEFAULT_SKIP_DIRS, ...(config.skipDirs || [])]
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const report = { copied: [], conflicts: [], skipped: 0, orphans: [] };
|
|
71
|
+
|
|
72
|
+
for (const rel of candidates) {
|
|
73
|
+
if (!include(rel) || exclude(rel)) {
|
|
74
|
+
report.skipped++;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Dotfiles that would be swallowed by the parent repo's ignore rules are
|
|
79
|
+
// stored under an underscore alias and restored at generation time.
|
|
80
|
+
let destRel = rel
|
|
81
|
+
.replace(/(^|\/)\.gitignore$/, '$1_gitignore')
|
|
82
|
+
.replace(/(^|\/)\.npmrc$/, '$1_npmrc');
|
|
83
|
+
|
|
84
|
+
const from = path.join(src, rel);
|
|
85
|
+
const to = path.join(filesDir, destRel);
|
|
86
|
+
|
|
87
|
+
if (exists(to) && !overwrite) {
|
|
88
|
+
// Only flag a genuine difference — an identical file is not a conflict.
|
|
89
|
+
const same = isTextFile(rel)
|
|
90
|
+
? readText(to) === translate(readText(from))
|
|
91
|
+
: fs.readFileSync(to).equals(fs.readFileSync(from));
|
|
92
|
+
if (!same) report.conflicts.push(destRel);
|
|
93
|
+
else report.skipped++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!dryRun) {
|
|
98
|
+
if (isTextFile(rel)) writeText(to, translate(readText(from)));
|
|
99
|
+
else copyBinary(from, to);
|
|
100
|
+
}
|
|
101
|
+
report.copied.push(destRel);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Files in the template that the source no longer produces.
|
|
105
|
+
//
|
|
106
|
+
// Only files the include/exclude globs actually *claim* can be orphans.
|
|
107
|
+
// Anything hand-authored outside that set — a template-only seeder, a
|
|
108
|
+
// .env.example that must never be copied from a real repo — is invisible to
|
|
109
|
+
// --prune by construction, so refreshing a template can never delete work
|
|
110
|
+
// that extraction was never responsible for.
|
|
111
|
+
if (exists(filesDir)) {
|
|
112
|
+
const templated = walk(filesDir, { skipDirs: DEFAULT_SKIP_DIRS });
|
|
113
|
+
const denorm = (rel) =>
|
|
114
|
+
rel
|
|
115
|
+
.replace(/(^|\/)_gitignore$/, '$1.gitignore')
|
|
116
|
+
.replace(/(^|\/)_npmrc$/, '$1.npmrc');
|
|
117
|
+
const sourceSet = new Set(
|
|
118
|
+
candidates
|
|
119
|
+
.filter((rel) => include(rel) && !exclude(rel))
|
|
120
|
+
.map((rel) =>
|
|
121
|
+
rel
|
|
122
|
+
.replace(/(^|\/)\.gitignore$/, '$1_gitignore')
|
|
123
|
+
.replace(/(^|\/)\.npmrc$/, '$1_npmrc')
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
for (const rel of templated) {
|
|
127
|
+
if (sourceSet.has(rel)) continue;
|
|
128
|
+
const original = denorm(rel);
|
|
129
|
+
// Claimed by the globs but absent from the source → genuinely stale.
|
|
130
|
+
if (include(original) && !exclude(original)) report.orphans.push(rel);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (prune && !dryRun) {
|
|
135
|
+
for (const rel of report.orphans) {
|
|
136
|
+
fs.rmSync(path.join(filesDir, rel), { force: true });
|
|
137
|
+
}
|
|
138
|
+
pruneEmptyDirs(filesDir);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return report;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Remove directories left empty by --prune, bottom-up. */
|
|
145
|
+
function pruneEmptyDirs(root) {
|
|
146
|
+
const rec = (dir) => {
|
|
147
|
+
let entries;
|
|
148
|
+
try {
|
|
149
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
150
|
+
} catch {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
let empty = true;
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
if (entry.isDirectory()) {
|
|
156
|
+
if (!rec(path.join(dir, entry.name))) empty = false;
|
|
157
|
+
} else {
|
|
158
|
+
empty = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (empty && dir !== root) fs.rmdirSync(dir);
|
|
162
|
+
return empty;
|
|
163
|
+
};
|
|
164
|
+
rec(root);
|
|
165
|
+
}
|
package/cli/src/fsx.mjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem + glob helpers. No dependencies — this CLI must run from a bare
|
|
3
|
+
* checkout of ~/.claude with nothing installed.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Translate a glob to a RegExp. Supports `**` (any depth, including zero
|
|
10
|
+
* segments), `*` (within a segment), `?`, `{a,b}` alternation and `[abc]`
|
|
11
|
+
* classes. Paths are matched with forward slashes, relative and un-prefixed.
|
|
12
|
+
*/
|
|
13
|
+
export function globToRegExp(glob) {
|
|
14
|
+
let out = '';
|
|
15
|
+
let i = 0;
|
|
16
|
+
let inClass = false;
|
|
17
|
+
|
|
18
|
+
while (i < glob.length) {
|
|
19
|
+
const c = glob[i];
|
|
20
|
+
|
|
21
|
+
if (inClass) {
|
|
22
|
+
if (c === ']') inClass = false;
|
|
23
|
+
out += c === '\\' ? '\\\\' : c;
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
switch (c) {
|
|
29
|
+
case '[':
|
|
30
|
+
inClass = true;
|
|
31
|
+
out += '[';
|
|
32
|
+
i++;
|
|
33
|
+
break;
|
|
34
|
+
case '{':
|
|
35
|
+
out += '(?:';
|
|
36
|
+
i++;
|
|
37
|
+
break;
|
|
38
|
+
case '}':
|
|
39
|
+
out += ')';
|
|
40
|
+
i++;
|
|
41
|
+
break;
|
|
42
|
+
case ',':
|
|
43
|
+
out += '|';
|
|
44
|
+
i++;
|
|
45
|
+
break;
|
|
46
|
+
case '?':
|
|
47
|
+
out += '[^/]';
|
|
48
|
+
i++;
|
|
49
|
+
break;
|
|
50
|
+
case '*': {
|
|
51
|
+
const isGlobstar = glob[i + 1] === '*';
|
|
52
|
+
if (!isGlobstar) {
|
|
53
|
+
out += '[^/]*';
|
|
54
|
+
i++;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
// `a/**/b` must also match `a/b`, so swallow the trailing slash
|
|
58
|
+
// into the optional group rather than leaving it mandatory.
|
|
59
|
+
if (glob[i + 2] === '/') {
|
|
60
|
+
out += '(?:.*/)?';
|
|
61
|
+
i += 3;
|
|
62
|
+
} else {
|
|
63
|
+
out += '.*';
|
|
64
|
+
i += 2;
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
out += /[.+^$()|\\]/.test(c) ? `\\${c}` : c;
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return new RegExp(`^${out}$`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Compile a list of globs into a single predicate. Empty list → never matches. */
|
|
78
|
+
export function matcher(globs) {
|
|
79
|
+
if (!globs || globs.length === 0) return () => false;
|
|
80
|
+
const res = globs.map(globToRegExp);
|
|
81
|
+
// A directory glob like `src/common` should also claim everything under it,
|
|
82
|
+
// which is what people mean when they list a bare directory.
|
|
83
|
+
const prefixes = globs
|
|
84
|
+
.filter((g) => !/[*?{[]/.test(g))
|
|
85
|
+
.map((g) => g.replace(/\/$/, '') + '/');
|
|
86
|
+
return (rel) =>
|
|
87
|
+
res.some((re) => re.test(rel)) || prefixes.some((p) => rel.startsWith(p));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Walk `root` and return repo-relative POSIX paths of every file.
|
|
92
|
+
* `skipDirs` short-circuits whole subtrees (node_modules and friends) so we
|
|
93
|
+
* never pay to descend into them.
|
|
94
|
+
*/
|
|
95
|
+
export function walk(root, { skipDirs = [] } = {}) {
|
|
96
|
+
const skip = new Set(skipDirs);
|
|
97
|
+
const out = [];
|
|
98
|
+
|
|
99
|
+
const rec = (dir, rel) => {
|
|
100
|
+
let entries;
|
|
101
|
+
try {
|
|
102
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
103
|
+
} catch {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
const childRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
108
|
+
if (entry.isDirectory()) {
|
|
109
|
+
if (skip.has(entry.name)) continue;
|
|
110
|
+
rec(path.join(dir, entry.name), childRel);
|
|
111
|
+
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
112
|
+
out.push(childRel);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
rec(root, '');
|
|
118
|
+
return out.sort();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const DEFAULT_SKIP_DIRS = [
|
|
122
|
+
'node_modules',
|
|
123
|
+
'.git',
|
|
124
|
+
'.turbo',
|
|
125
|
+
'dist',
|
|
126
|
+
'build',
|
|
127
|
+
'coverage',
|
|
128
|
+
'.next',
|
|
129
|
+
'.cache',
|
|
130
|
+
'__pycache__',
|
|
131
|
+
'.venv',
|
|
132
|
+
'venv',
|
|
133
|
+
'.react-email',
|
|
134
|
+
'dev-dist',
|
|
135
|
+
'.yarn',
|
|
136
|
+
'generated',
|
|
137
|
+
'.pytest_cache'
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const TEXT_EXT = new Set([
|
|
141
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts',
|
|
142
|
+
'.json', '.jsonc', '.json5', '.yml', '.yaml', '.toml', '.ini',
|
|
143
|
+
'.md', '.mdx', '.txt', '.html', '.htm', '.xml', '.svg',
|
|
144
|
+
'.css', '.scss', '.less', '.prisma', '.graphql', '.gql',
|
|
145
|
+
'.sh', '.bash', '.zsh', '.py', '.rb', '.sql', '.dbml',
|
|
146
|
+
'.env', '.example', '.gitignore', '.dockerignore', '.editorconfig',
|
|
147
|
+
'.npmrc', '.nvmrc', '.prettierrc', '.eslintrc', '.lock', '.alloy'
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
const TEXT_BASENAME = new Set([
|
|
151
|
+
'Dockerfile', 'Dockerfile.dev', 'Makefile', 'LICENSE', 'README',
|
|
152
|
+
'.gitignore', '.dockerignore', '.editorconfig', '.npmrc', '.nvmrc',
|
|
153
|
+
'.env', '.prettierrc', '.prettierignore', '.eslintrc', '.watchmanconfig',
|
|
154
|
+
'.yarnrc.yml', '.swcrc', '.gitmodules', '.gitkeep'
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
/** Heuristic: is this file safe to read as UTF-8 and rewrite? */
|
|
158
|
+
export function isTextFile(rel) {
|
|
159
|
+
const base = path.basename(rel);
|
|
160
|
+
if (TEXT_BASENAME.has(base)) return true;
|
|
161
|
+
if (base.startsWith('.env')) return true;
|
|
162
|
+
if (base.startsWith('Dockerfile')) return true;
|
|
163
|
+
const ext = path.extname(base).toLowerCase();
|
|
164
|
+
return TEXT_EXT.has(ext);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function readText(file) {
|
|
168
|
+
return fs.readFileSync(file, 'utf8');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function writeText(file, content) {
|
|
172
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
173
|
+
fs.writeFileSync(file, content, 'utf8');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function copyBinary(from, to) {
|
|
177
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
178
|
+
fs.copyFileSync(from, to);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function exists(p) {
|
|
182
|
+
try {
|
|
183
|
+
fs.accessSync(p);
|
|
184
|
+
return true;
|
|
185
|
+
} catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function isDirEmpty(dir) {
|
|
191
|
+
if (!exists(dir)) return true;
|
|
192
|
+
return fs.readdirSync(dir).length === 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function rmrf(dir) {
|
|
196
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
197
|
+
}
|