Initial commit from monorepo

This commit is contained in:
Ruslan Bakiev
2026-01-07 09:10:35 +07:00
commit 3db50d9637
371 changed files with 43223 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
import { promises as fs } from 'node:fs'
import path from 'node:path'
const componentsRoot = path.resolve(process.cwd(), 'app/components')
const storyExtensions = ['.stories.ts', '.stories.js', '.stories.tsx', '.stories.jsx']
const titleCase = (str) =>
str
.split(/[-_/]+/)
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('/')
const toIdentifier = (str) =>
str
.split(/[^a-zA-Z0-9]+/)
.filter(Boolean)
.map((part, idx) => {
const lower = part.toLowerCase()
if (idx === 0) return lower.charAt(0).toUpperCase() + lower.slice(1)
return part.charAt(0).toUpperCase() + part.slice(1)
})
.join('') || 'Component'
const hasStorySibling = async (dir, baseName) => {
const files = await fs.readdir(dir)
return files.some((file) => {
const match = file.match(/^(.+)\.stories\.(t|j)sx?$/i)
if (!match) return false
return match[1] === baseName
})
}
const walkVueFiles = async (dir) => {
const entries = await fs.readdir(dir, { withFileTypes: true })
const files = []
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
files.push(...(await walkVueFiles(fullPath)))
continue
}
if (entry.isFile() && entry.name.endsWith('.vue')) {
files.push(fullPath)
}
}
return files
}
const buildStoryContent = (filePath) => {
const baseName = path.basename(filePath, '.vue')
const importPath = `./${path.basename(filePath)}`
const relative = path.relative(componentsRoot, filePath).replace(/\\/g, '/')
const title = titleCase(relative.replace('.vue', ''))
const componentId = toIdentifier(baseName)
return `import type { Meta, StoryObj } from '@storybook/vue3'
import StoryComponent from '${importPath}'
const meta: Meta<typeof StoryComponent> = {
title: '${title}',
component: StoryComponent,
render: (args) => ({
components: { StoryComponent },
setup() {
return { args }
},
template: '<StoryComponent v-bind=\"args\" />'
})
}
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {}
}
`
}
const ensureDir = async (dir) => {
await fs.mkdir(dir, { recursive: true })
}
const run = async () => {
const vueFiles = await walkVueFiles(componentsRoot)
const generated = []
for (const filePath of vueFiles) {
const dir = path.dirname(filePath)
const baseName = path.basename(filePath, '.vue')
const storyExists = await hasStorySibling(dir, baseName)
if (storyExists) continue
const storyPath = path.join(dir, `${baseName}.stories.ts`)
await ensureDir(dir)
const content = buildStoryContent(filePath)
await fs.writeFile(storyPath, content, 'utf8')
generated.push(path.relative(process.cwd(), storyPath))
}
if (generated.length === 0) {
console.log('No new stories generated. All components already have stories.')
return
}
console.log(`Generated ${generated.length} stories:`)
generated.forEach((p) => console.log(`- ${p}`))
}
run().catch((err) => {
console.error('Failed to generate stories', err)
process.exit(1)
})

48
scripts/load-secrets.mjs Normal file
View File

@@ -0,0 +1,48 @@
/**
* Load secrets from Infisical using Machine Identity (Universal Auth)
* Writes secrets to .env.infisical file for sourcing
*/
import { InfisicalSDK } from "@infisical/sdk";
import { writeFileSync } from "fs";
const INFISICAL_API_URL = process.env.INFISICAL_API_URL;
const INFISICAL_CLIENT_ID = process.env.INFISICAL_CLIENT_ID;
const INFISICAL_CLIENT_SECRET = process.env.INFISICAL_CLIENT_SECRET;
const INFISICAL_PROJECT_ID = process.env.INFISICAL_PROJECT_ID;
const INFISICAL_ENV = process.env.INFISICAL_ENV || "prod";
if (!INFISICAL_API_URL || !INFISICAL_CLIENT_ID || !INFISICAL_CLIENT_SECRET || !INFISICAL_PROJECT_ID) {
process.stderr.write("Missing required Infisical environment variables\n");
process.exit(1);
}
const client = new InfisicalSDK({ siteUrl: INFISICAL_API_URL });
await client.auth().universalAuth.login({
clientId: INFISICAL_CLIENT_ID,
clientSecret: INFISICAL_CLIENT_SECRET,
});
process.stderr.write(`Loading secrets from Infisical (env: ${INFISICAL_ENV})...\n`);
const envLines = [];
for (const secretPath of ["/webapp", "/shared"]) {
const response = await client.secrets().listSecrets({
projectId: INFISICAL_PROJECT_ID,
environment: INFISICAL_ENV,
secretPath: secretPath,
expandSecretReferences: true,
});
for (const secret of response.secrets) {
// Escape special characters for shell
const escapedValue = secret.secretValue.replace(/'/g, "'\\''");
envLines.push(`export ${secret.secretKey}='${escapedValue}'`);
}
process.stderr.write(` ${secretPath}: ${response.secrets.length} secrets loaded\n`);
}
writeFileSync(".env.infisical", envLines.join("\n"));
process.stderr.write("Secrets written to .env.infisical\n");