@maydotinc/s3-sync 0.1.0 → 0.1.1
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/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -107,7 +107,11 @@ function defaultConfig() {
|
|
|
107
107
|
// src/workflow/generator.ts
|
|
108
108
|
function generateWorkflow(config, packageSpecifier) {
|
|
109
109
|
const envBlock = buildEnvBlock(config);
|
|
110
|
-
const pathFilters =
|
|
110
|
+
const pathFilters = [
|
|
111
|
+
...config.targets.map((t) => `${t.directory}/**`),
|
|
112
|
+
"s3-sync.json",
|
|
113
|
+
".github/workflows/s3-sync.yml"
|
|
114
|
+
].map((pattern) => ` - '${pattern}'`).join("\n");
|
|
111
115
|
return `name: S3 Sync
|
|
112
116
|
|
|
113
117
|
on:
|
|
@@ -115,6 +119,7 @@ on:
|
|
|
115
119
|
branches: [${config.branch}]
|
|
116
120
|
paths:
|
|
117
121
|
${pathFilters}
|
|
122
|
+
workflow_dispatch:
|
|
118
123
|
|
|
119
124
|
jobs:
|
|
120
125
|
sync:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/setup.ts","../src/utils/config.ts","../src/workflow/generator.ts","../src/utils/logger.ts","../src/utils/package-meta.ts","../src/commands/sync.ts","../src/core/fingerprint.ts","../src/core/s3-client.ts","../src/core/diff.ts","../src/notifications/slack.ts","../src/notifications/discord.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { setupCommand } from \"./commands/setup.js\";\nimport { syncCommand } from \"./commands/sync.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"s3-sync\")\n .description(\"Sync local directories to S3-compatible buckets via GitHub Actions\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"sync\")\n .description(\"Execute sync for all configured targets\")\n .action(async () => {\n try {\n await syncCommand();\n } catch (err: any) {\n console.error(err.message);\n process.exit(1);\n }\n });\n\nprogram\n .argument(\"<directory>\", \"Local directory to sync (e.g. public, apps/web/dist)\")\n .argument(\"sync\", \"Generate workflow and config for syncing this directory\")\n .option(\"--bucket <name>\", \"S3 bucket name\")\n .option(\"--region <region>\", \"AWS region\")\n .option(\"--endpoint <url>\", \"Custom S3-compatible endpoint\")\n .option(\"--prefix <prefix>\", \"Path prefix in bucket\")\n .option(\"--branch <branch>\", \"Git branch to trigger on\")\n .option(\"--delete\", \"Delete remote files not present locally\")\n .option(\"--no-delete\", \"Keep remote files not present locally\")\n .option(\"--slack\", \"Enable Slack notifications\")\n .option(\"--discord\", \"Enable Discord notifications\")\n .action(async (directory: string, _syncArg: string, options: any) => {\n try {\n await setupCommand(directory, {\n bucket: options.bucket,\n region: options.region,\n endpoint: options.endpoint,\n prefix: options.prefix,\n branch: options.branch,\n delete: options.delete === true ? true : options.delete === false ? false : undefined,\n slack: options.slack,\n discord: options.discord,\n });\n } catch (err: any) {\n console.error(err.message);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport inquirer from \"inquirer\";\nimport {\n defaultConfig,\n defaultTarget,\n writeConfig,\n readConfig,\n configExists,\n type S3SyncConfig,\n type SyncTarget,\n} from \"../utils/config.js\";\nimport { generateWorkflow } from \"../workflow/generator.js\";\nimport { log } from \"../utils/logger.js\";\nimport { getCurrentPackageSpecifier } from \"../utils/package-meta.js\";\n\ninterface SetupFlags {\n bucket?: string;\n region?: string;\n endpoint?: string;\n prefix?: string;\n branch?: string;\n delete?: boolean;\n slack?: boolean;\n discord?: boolean;\n}\n\nexport async function setupCommand(\n directory: string,\n flags: SetupFlags\n): Promise<void> {\n log.heading(\"s3-sync setup\");\n\n let config: S3SyncConfig;\n let isAdding = false;\n\n if (configExists()) {\n const existing = await readConfig();\n const alreadyHasDir = existing.targets.some(\n (t) => t.directory === directory\n );\n\n if (alreadyHasDir) {\n const { overwrite } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"overwrite\",\n message: `Target \"${directory}\" already exists. Overwrite it?`,\n default: false,\n },\n ]);\n if (!overwrite) {\n log.info(\"Setup cancelled.\");\n return;\n }\n existing.targets = existing.targets.filter(\n (t) => t.directory !== directory\n );\n } else {\n log.info(\n `Existing config found with ${existing.targets.length} target(s). Adding \"${directory}\".`\n );\n isAdding = true;\n }\n config = existing;\n } else {\n config = defaultConfig();\n }\n\n const dirPath = path.resolve(directory);\n if (!existsSync(dirPath)) {\n log.warn(\n `Directory \"${directory}\" doesn't exist yet — that's okay, it will be created later.`\n );\n }\n\n const target = await gatherTarget(directory, flags);\n config.targets.push(target);\n\n if (!isAdding) {\n await gatherSharedConfig(config, flags);\n }\n\n await writeConfig(config);\n log.success(\"Updated s3-sync.json\");\n\n const workflowDir = path.join(process.cwd(), \".github\", \"workflows\");\n await mkdir(workflowDir, { recursive: true });\n\n const workflowPath = path.join(workflowDir, \"s3-sync.yml\");\n const packageSpecifier = getCurrentPackageSpecifier();\n const workflowContent = generateWorkflow(config, packageSpecifier);\n await writeFile(workflowPath, workflowContent, \"utf-8\");\n log.success(\"Updated .github/workflows/s3-sync.yml\");\n\n printSecretInstructions(config);\n}\n\nasync function gatherTarget(\n directory: string,\n flags: SetupFlags\n): Promise<SyncTarget> {\n const target = defaultTarget();\n target.directory = directory;\n\n const questions: any[] = [];\n\n if (flags.bucket === undefined) {\n questions.push({\n type: \"input\",\n name: \"bucket\",\n message: `[${directory}] S3 bucket name:`,\n validate: (v: string) => (v.trim() ? true : \"Bucket name is required\"),\n });\n } else {\n target.bucket = flags.bucket;\n }\n\n if (flags.region === undefined) {\n questions.push({\n type: \"input\",\n name: \"region\",\n message: `[${directory}] AWS region:`,\n default: \"us-east-1\",\n });\n } else {\n target.region = flags.region;\n }\n\n if (flags.endpoint === undefined) {\n questions.push({\n type: \"input\",\n name: \"endpoint\",\n message: `[${directory}] Custom S3 endpoint (leave blank for AWS):`,\n default: \"\",\n });\n } else {\n target.endpoint = flags.endpoint;\n }\n\n if (flags.prefix === undefined) {\n questions.push({\n type: \"input\",\n name: \"prefix\",\n message: `[${directory}] Path prefix (leave blank for root):`,\n default: \"\",\n });\n } else {\n target.prefix = flags.prefix;\n }\n\n if (flags.delete === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"delete\",\n message: `[${directory}] Delete files from S3 that no longer exist locally?`,\n default: false,\n });\n } else {\n target.delete = flags.delete;\n }\n\n if (questions.length > 0) {\n const answers = await inquirer.prompt(questions);\n if (answers.bucket !== undefined) target.bucket = answers.bucket;\n if (answers.region !== undefined) target.region = answers.region;\n if (answers.endpoint !== undefined) target.endpoint = answers.endpoint;\n if (answers.prefix !== undefined) target.prefix = answers.prefix;\n if (answers.delete !== undefined) target.delete = answers.delete;\n }\n\n return target;\n}\n\nasync function gatherSharedConfig(\n config: S3SyncConfig,\n flags: SetupFlags\n): Promise<void> {\n const questions: any[] = [];\n\n if (flags.branch === undefined) {\n questions.push({\n type: \"input\",\n name: \"branch\",\n message: \"Branch to trigger sync on:\",\n default: \"main\",\n });\n } else {\n config.branch = flags.branch;\n }\n\n if (flags.slack === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"slack\",\n message: \"Enable Slack notifications?\",\n default: false,\n });\n } else {\n config.notifications.slack = flags.slack;\n }\n\n if (flags.discord === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"discord\",\n message: \"Enable Discord notifications?\",\n default: false,\n });\n } else {\n config.notifications.discord = flags.discord;\n }\n\n if (questions.length > 0) {\n const answers = await inquirer.prompt(questions);\n if (answers.branch !== undefined) config.branch = answers.branch;\n if (answers.slack !== undefined) config.notifications.slack = answers.slack;\n if (answers.discord !== undefined)\n config.notifications.discord = answers.discord;\n }\n}\n\nfunction printSecretInstructions(config: S3SyncConfig): void {\n log.heading(\"Next steps\");\n log.info(\"Add these secrets to your GitHub repository:\");\n log.dim(\n \" Settings → Secrets and variables → Actions → New repository secret\\n\"\n );\n\n const secrets = [\n [\"S3_ACCESS_KEY_ID\", \"Your S3 access key\"],\n [\"S3_SECRET_ACCESS_KEY\", \"Your S3 secret key\"],\n ];\n\n if (config.notifications.slack) {\n secrets.push([\"SLACK_WEBHOOK_URL\", \"Slack incoming webhook URL\"]);\n }\n\n if (config.notifications.discord) {\n secrets.push([\"DISCORD_WEBHOOK_URL\", \"Discord webhook URL\"]);\n }\n\n for (const [name, desc] of secrets) {\n log.dim(` • ${name} — ${desc}`);\n }\n\n console.log();\n log.heading(\"Configured targets\");\n for (const t of config.targets) {\n log.info(` ${t.directory}/ → s3://${t.bucket}/${t.prefix}`);\n }\n\n console.log();\n log.success(\n `Push to \"${config.branch}\" to trigger a sync.`\n );\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nexport interface SyncTarget {\n directory: string;\n bucket: string;\n region: string;\n endpoint: string;\n prefix: string;\n delete: boolean;\n}\n\nexport interface S3SyncConfig {\n targets: SyncTarget[];\n branch: string;\n notifications: {\n slack: boolean;\n discord: boolean;\n };\n}\n\nconst CONFIG_FILE = \"s3-sync.json\";\n\nconst syncTargetSchema: z.ZodType<SyncTarget> = z.object({\n directory: z.string().min(1),\n bucket: z.string().min(1),\n region: z.string().min(1),\n endpoint: z.string().default(\"\"),\n prefix: z.string().default(\"\"),\n delete: z.boolean().default(false),\n});\n\nconst notificationsSchema: z.ZodType<S3SyncConfig[\"notifications\"]> = z.object({\n slack: z.boolean().default(false),\n discord: z.boolean().default(false),\n});\n\nconst configSchema: z.ZodType<S3SyncConfig> = z.object({\n targets: z.array(syncTargetSchema).default([]),\n branch: z.string().min(1).default(\"main\"),\n notifications: notificationsSchema.default({ slack: false, discord: false }),\n});\n\nconst legacyConfigSchema = z.object({\n directory: z.string().min(1),\n bucket: z.string().min(1),\n region: z.string().min(1),\n endpoint: z.string().default(\"\"),\n prefix: z.string().default(\"\"),\n branch: z.string().min(1).default(\"main\"),\n delete: z.boolean().default(false),\n notifications: notificationsSchema.default({ slack: false, discord: false }),\n});\n\nexport function getConfigPath(cwd: string = process.cwd()): string {\n return path.join(cwd, CONFIG_FILE);\n}\n\nexport function configExists(cwd: string = process.cwd()): boolean {\n return existsSync(getConfigPath(cwd));\n}\n\nexport async function readConfig(\n cwd: string = process.cwd()\n): Promise<S3SyncConfig> {\n const configPath = getConfigPath(cwd);\n const raw = JSON.parse(await readFile(configPath, \"utf-8\"));\n const parsedConfig = configSchema.safeParse(raw);\n if (parsedConfig.success) return parsedConfig.data;\n\n const parsedLegacyConfig = legacyConfigSchema.safeParse(raw);\n if (!parsedLegacyConfig.success) {\n const issues = parsedConfig.error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"root\";\n return `${path}: ${issue.message}`;\n })\n .join(\"; \");\n throw new Error(`Invalid ${CONFIG_FILE}: ${issues}`);\n }\n\n const legacy = parsedLegacyConfig.data;\n return {\n targets: [\n {\n directory: legacy.directory,\n bucket: legacy.bucket,\n region: legacy.region,\n endpoint: legacy.endpoint,\n prefix: legacy.prefix,\n delete: legacy.delete,\n },\n ],\n branch: legacy.branch,\n notifications: legacy.notifications,\n };\n}\n\nexport async function writeConfig(\n config: S3SyncConfig,\n cwd: string = process.cwd()\n): Promise<void> {\n const configPath = getConfigPath(cwd);\n const validatedConfig = configSchema.parse(config);\n await writeFile(\n configPath,\n JSON.stringify(validatedConfig, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\nexport function defaultTarget(): SyncTarget {\n return {\n directory: \"public\",\n bucket: \"\",\n region: \"us-east-1\",\n endpoint: \"\",\n prefix: \"\",\n delete: false,\n };\n}\n\nexport function defaultConfig(): S3SyncConfig {\n return {\n targets: [],\n branch: \"main\",\n notifications: { slack: false, discord: false },\n };\n}\n","import type { S3SyncConfig } from \"../utils/config.js\";\n\nexport function generateWorkflow(\n config: S3SyncConfig,\n packageSpecifier: string\n): string {\n const envBlock = buildEnvBlock(config);\n const pathFilters = config.targets\n .map((t) => ` - '${t.directory}/**'`)\n .join(\"\\n\");\n\n return `name: S3 Sync\n\non:\n push:\n branches: [${config.branch}]\n paths:\n${pathFilters}\n\njobs:\n sync:\n name: Sync to S3\n runs-on: ubuntu-latest\n\n steps:\n - name: Checkout\n uses: actions/checkout@v4\n\n - name: Setup Node\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Sync files\n run: npx --yes ${packageSpecifier} sync\n${envBlock}\n`;\n}\n\nfunction buildEnvBlock(config: S3SyncConfig): string {\n const lines = [\n \" env:\",\n \" S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}\",\n \" S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}\",\n ];\n\n if (config.notifications.slack) {\n lines.push(\n \" SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}\"\n );\n }\n\n if (config.notifications.discord) {\n lines.push(\n \" DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}\"\n );\n }\n\n return lines.join(\"\\n\");\n}\n","import chalk from \"chalk\";\n\nexport const log = {\n info: (msg: string) => console.log(chalk.blue(\"ℹ\"), msg),\n success: (msg: string) => console.log(chalk.green(\"✓\"), msg),\n warn: (msg: string) => console.log(chalk.yellow(\"⚠\"), msg),\n error: (msg: string) => console.error(chalk.red(\"✗\"), msg),\n dim: (msg: string) => console.log(chalk.dim(msg)),\n heading: (msg: string) => console.log(chalk.bold.cyan(`\\n${msg}\\n`)),\n};\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageMeta {\n name?: string;\n version?: string;\n}\n\nfunction findPackageJson(startDirectory: string): string | undefined {\n let currentDirectory = startDirectory;\n while (true) {\n const candidate = path.join(currentDirectory, \"package.json\");\n if (existsSync(candidate)) return candidate;\n const parentDirectory = path.dirname(currentDirectory);\n if (parentDirectory === currentDirectory) return undefined;\n currentDirectory = parentDirectory;\n }\n}\n\nexport function getCurrentPackageSpecifier(\n fallbackName = \"@maydotinc/s3-sync\"\n): string {\n const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = findPackageJson(currentFileDirectory);\n if (!packageJsonPath) return fallbackName;\n\n try {\n const parsed = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageMeta;\n const packageName = parsed.name?.trim() || fallbackName;\n const packageVersion = parsed.version?.trim();\n return packageVersion ? `${packageName}@${packageVersion}` : packageName;\n } catch {\n return fallbackName;\n }\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { readConfig, type SyncTarget } from \"../utils/config.js\";\nimport { log } from \"../utils/logger.js\";\nimport { fingerprintDirectory } from \"../core/fingerprint.js\";\nimport {\n createS3,\n listAllObjects,\n uploadFile,\n deleteObject,\n} from \"../core/s3-client.js\";\nimport { computeDiff, type DiffResult } from \"../core/diff.js\";\nimport { sendSlackNotification } from \"../notifications/slack.js\";\nimport { sendDiscordNotification } from \"../notifications/discord.js\";\n\nexport async function syncCommand(): Promise<void> {\n log.heading(\"s3-sync\");\n\n const config = await readConfig();\n\n const accessKeyId = process.env.S3_ACCESS_KEY_ID;\n const secretAccessKey = process.env.S3_SECRET_ACCESS_KEY;\n\n if (!accessKeyId || !secretAccessKey) {\n log.error(\n \"Missing S3_ACCESS_KEY_ID or S3_SECRET_ACCESS_KEY environment variables.\"\n );\n process.exit(1);\n }\n\n if (config.targets.length === 0) {\n log.error(\"No sync targets configured. Run setup first.\");\n process.exit(1);\n }\n\n const summaries: string[] = [];\n let hasErrors = false;\n const globalEndpoint = process.env.S3_ENDPOINT?.trim() || undefined;\n const hasTargetSpecificEndpoints = config.targets.some(\n (target) => target.endpoint.trim().length > 0\n );\n\n for (const target of config.targets) {\n try {\n const endpoint = resolveEndpoint(\n target.endpoint,\n globalEndpoint,\n hasTargetSpecificEndpoints\n );\n const diff = await syncTarget(\n target,\n accessKeyId,\n secretAccessKey,\n endpoint\n );\n summaries.push(buildTargetSummary(target, diff));\n } catch (err: any) {\n log.error(`[${target.directory}] ${err.message}`);\n hasErrors = true;\n }\n }\n\n if (summaries.length > 0) {\n const fullSummary = summaries.join(\"\\n\\n---\\n\\n\");\n await notify(config, fullSummary);\n }\n\n if (hasErrors) {\n process.exit(1);\n }\n\n log.success(\"All targets synced.\");\n}\n\nasync function syncTarget(\n target: SyncTarget,\n accessKeyId: string,\n secretAccessKey: string,\n endpoint: string | undefined\n): Promise<DiffResult> {\n log.heading(`Syncing ${target.directory}/ → s3://${target.bucket}/${target.prefix}`);\n\n const directory = path.resolve(target.directory);\n if (!existsSync(directory)) {\n throw new Error(`Directory \"${target.directory}\" does not exist.`);\n }\n\n const client = createS3({\n bucket: target.bucket,\n region: target.region,\n endpoint,\n accessKeyId,\n secretAccessKey,\n });\n\n log.info(`Scanning ${target.directory}/...`);\n const localFiles = await fingerprintDirectory(directory);\n log.info(`Found ${localFiles.length} local files`);\n\n log.info(`Listing objects in s3://${target.bucket}/${target.prefix}...`);\n const remoteObjects = await listAllObjects(\n client,\n target.bucket,\n target.prefix\n );\n log.info(`Found ${remoteObjects.length} remote objects`);\n\n const diff = computeDiff(\n localFiles,\n remoteObjects,\n target.prefix,\n target.delete\n );\n\n log.info(\n `${diff.toUpload.length} to upload, ${diff.toDelete.length} to delete, ${diff.unchanged.length} unchanged`\n );\n\n if (diff.toUpload.length === 0 && diff.toDelete.length === 0) {\n log.success(`[${target.directory}] Already in sync.`);\n return diff;\n }\n\n for (const file of diff.toUpload) {\n const key = target.prefix\n ? `${target.prefix}/${file.relativePath}`\n : file.relativePath;\n log.dim(` uploading ${key}`);\n await uploadFile(client, target.bucket, key, file.absolutePath);\n }\n\n if (diff.toUpload.length > 0) {\n log.success(`[${target.directory}] Uploaded ${diff.toUpload.length} files`);\n }\n\n for (const obj of diff.toDelete) {\n log.dim(` deleting ${obj.key}`);\n await deleteObject(client, target.bucket, obj.key);\n }\n\n if (diff.toDelete.length > 0) {\n log.success(`[${target.directory}] Deleted ${diff.toDelete.length} files`);\n }\n\n return diff;\n}\n\nfunction resolveEndpoint(\n targetEndpoint: string,\n globalEndpoint: string | undefined,\n hasTargetSpecificEndpoints: boolean\n): string | undefined {\n const trimmedTargetEndpoint = targetEndpoint.trim();\n if (trimmedTargetEndpoint) return trimmedTargetEndpoint;\n if (!hasTargetSpecificEndpoints) return globalEndpoint;\n return undefined;\n}\n\nfunction buildTargetSummary(target: SyncTarget, diff: DiffResult): string {\n const lines = [\n `*${target.directory}/ → s3://${target.bucket}/${target.prefix}*`,\n `Uploaded: ${diff.toUpload.length}`,\n `Deleted: ${diff.toDelete.length}`,\n `Unchanged: ${diff.unchanged.length}`,\n ];\n\n if (diff.toUpload.length > 0) {\n const fileList = diff.toUpload\n .slice(0, 10)\n .map((f) => ` • ${f.relativePath}`)\n .join(\"\\n\");\n lines.push(`\\nUploaded files:\\n${fileList}`);\n if (diff.toUpload.length > 10) {\n lines.push(` ...and ${diff.toUpload.length - 10} more`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nasync function notify(config: any, message: string): Promise<void> {\n if (config.notifications?.slack) {\n const webhookUrl = process.env.SLACK_WEBHOOK_URL;\n if (webhookUrl) {\n try {\n await sendSlackNotification(webhookUrl, message);\n log.success(\"Slack notification sent\");\n } catch (err: any) {\n log.warn(`Slack notification failed: ${err.message}`);\n }\n }\n }\n\n if (config.notifications?.discord) {\n const webhookUrl = process.env.DISCORD_WEBHOOK_URL;\n if (webhookUrl) {\n try {\n await sendDiscordNotification(webhookUrl, message);\n log.success(\"Discord notification sent\");\n } catch (err: any) {\n log.warn(`Discord notification failed: ${err.message}`);\n }\n }\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { glob } from \"glob\";\n\nexport interface LocalFile {\n relativePath: string;\n absolutePath: string;\n md5: string;\n size: number;\n}\n\nexport async function fingerprintDirectory(\n directory: string\n): Promise<LocalFile[]> {\n const absoluteDir = path.resolve(directory);\n const files = await glob(\"**/*\", {\n cwd: absoluteDir,\n nodir: true,\n dot: false,\n });\n\n const results: LocalFile[] = [];\n\n await Promise.all(\n files.map(async (relativePath) => {\n const absolutePath = path.join(absoluteDir, relativePath);\n const content = await readFile(absolutePath);\n const md5 = createHash(\"md5\").update(content).digest(\"hex\");\n\n results.push({\n relativePath: relativePath.replace(/\\\\/g, \"/\"),\n absolutePath,\n md5,\n size: content.length,\n });\n })\n );\n\n return results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));\n}\n","import {\n S3Client,\n ListObjectsV2Command,\n type ListObjectsV2CommandOutput,\n PutObjectCommand,\n DeleteObjectCommand,\n} from \"@aws-sdk/client-s3\";\nimport { readFile } from \"node:fs/promises\";\nimport mime from \"mime-types\";\n\nexport interface S3Object {\n key: string;\n etag: string;\n size: number;\n}\n\nexport interface S3Config {\n bucket: string;\n region: string;\n endpoint?: string;\n accessKeyId: string;\n secretAccessKey: string;\n}\n\nexport function createS3(config: S3Config): S3Client {\n return new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n ...(config.endpoint && {\n endpoint: config.endpoint,\n forcePathStyle: true,\n }),\n });\n}\n\nexport async function listAllObjects(\n client: S3Client,\n bucket: string,\n prefix: string\n): Promise<S3Object[]> {\n const objects: S3Object[] = [];\n let continuationToken: string | undefined;\n\n do {\n const command = new ListObjectsV2Command({\n Bucket: bucket,\n Prefix: prefix || undefined,\n ContinuationToken: continuationToken,\n });\n\n const response: ListObjectsV2CommandOutput = await client.send(command);\n\n if (response.Contents) {\n for (const obj of response.Contents) {\n if (obj.Key && obj.ETag && obj.Size !== undefined) {\n objects.push({\n key: obj.Key,\n etag: obj.ETag.replace(/\"/g, \"\"),\n size: obj.Size,\n });\n }\n }\n }\n\n continuationToken = response.IsTruncated\n ? response.NextContinuationToken\n : undefined;\n } while (continuationToken);\n\n return objects;\n}\n\nexport async function uploadFile(\n client: S3Client,\n bucket: string,\n key: string,\n filePath: string\n): Promise<void> {\n const body = await readFile(filePath);\n const contentType = mime.lookup(filePath) || \"application/octet-stream\";\n\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n })\n );\n}\n\nexport async function deleteObject(\n client: S3Client,\n bucket: string,\n key: string\n): Promise<void> {\n await client.send(\n new DeleteObjectCommand({\n Bucket: bucket,\n Key: key,\n })\n );\n}\n","import type { LocalFile } from \"./fingerprint.js\";\nimport type { S3Object } from \"./s3-client.js\";\n\nexport interface DiffResult {\n toUpload: LocalFile[];\n toDelete: S3Object[];\n unchanged: LocalFile[];\n}\n\nexport function computeDiff(\n localFiles: LocalFile[],\n remoteObjects: S3Object[],\n prefix: string,\n shouldDelete: boolean\n): DiffResult {\n const remoteByKey = new Map<string, S3Object>();\n for (const obj of remoteObjects) {\n remoteByKey.set(obj.key, obj);\n }\n\n const toUpload: LocalFile[] = [];\n const unchanged: LocalFile[] = [];\n const localKeys = new Set<string>();\n\n for (const file of localFiles) {\n const key = prefix ? `${prefix}/${file.relativePath}` : file.relativePath;\n localKeys.add(key);\n\n const remote = remoteByKey.get(key);\n\n if (!remote || remote.etag !== file.md5) {\n toUpload.push(file);\n } else {\n unchanged.push(file);\n }\n }\n\n const toDelete: S3Object[] = [];\n if (shouldDelete) {\n for (const obj of remoteObjects) {\n if (!localKeys.has(obj.key)) {\n toDelete.push(obj);\n }\n }\n }\n\n return { toUpload, toDelete, unchanged };\n}\n","export async function sendSlackNotification(\n webhookUrl: string,\n message: string\n): Promise<void> {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ text: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Slack notification failed: ${response.statusText}`);\n }\n}\n","export async function sendDiscordNotification(\n webhookUrl: string,\n message: string\n): Promise<void> {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Discord notification failed: ${response.statusText}`);\n }\n}\n"],"mappings":"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","names":["writeFile","existsSync","path","path","existsSync","path","path","existsSync","writeFile","existsSync","path","readFile","path","readFile","path","existsSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/setup.ts","../src/utils/config.ts","../src/workflow/generator.ts","../src/utils/logger.ts","../src/utils/package-meta.ts","../src/commands/sync.ts","../src/core/fingerprint.ts","../src/core/s3-client.ts","../src/core/diff.ts","../src/notifications/slack.ts","../src/notifications/discord.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { setupCommand } from \"./commands/setup.js\";\nimport { syncCommand } from \"./commands/sync.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"s3-sync\")\n .description(\"Sync local directories to S3-compatible buckets via GitHub Actions\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"sync\")\n .description(\"Execute sync for all configured targets\")\n .action(async () => {\n try {\n await syncCommand();\n } catch (err: any) {\n console.error(err.message);\n process.exit(1);\n }\n });\n\nprogram\n .argument(\"<directory>\", \"Local directory to sync (e.g. public, apps/web/dist)\")\n .argument(\"sync\", \"Generate workflow and config for syncing this directory\")\n .option(\"--bucket <name>\", \"S3 bucket name\")\n .option(\"--region <region>\", \"AWS region\")\n .option(\"--endpoint <url>\", \"Custom S3-compatible endpoint\")\n .option(\"--prefix <prefix>\", \"Path prefix in bucket\")\n .option(\"--branch <branch>\", \"Git branch to trigger on\")\n .option(\"--delete\", \"Delete remote files not present locally\")\n .option(\"--no-delete\", \"Keep remote files not present locally\")\n .option(\"--slack\", \"Enable Slack notifications\")\n .option(\"--discord\", \"Enable Discord notifications\")\n .action(async (directory: string, _syncArg: string, options: any) => {\n try {\n await setupCommand(directory, {\n bucket: options.bucket,\n region: options.region,\n endpoint: options.endpoint,\n prefix: options.prefix,\n branch: options.branch,\n delete: options.delete === true ? true : options.delete === false ? false : undefined,\n slack: options.slack,\n discord: options.discord,\n });\n } catch (err: any) {\n console.error(err.message);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport inquirer from \"inquirer\";\nimport {\n defaultConfig,\n defaultTarget,\n writeConfig,\n readConfig,\n configExists,\n type S3SyncConfig,\n type SyncTarget,\n} from \"../utils/config.js\";\nimport { generateWorkflow } from \"../workflow/generator.js\";\nimport { log } from \"../utils/logger.js\";\nimport { getCurrentPackageSpecifier } from \"../utils/package-meta.js\";\n\ninterface SetupFlags {\n bucket?: string;\n region?: string;\n endpoint?: string;\n prefix?: string;\n branch?: string;\n delete?: boolean;\n slack?: boolean;\n discord?: boolean;\n}\n\nexport async function setupCommand(\n directory: string,\n flags: SetupFlags\n): Promise<void> {\n log.heading(\"s3-sync setup\");\n\n let config: S3SyncConfig;\n let isAdding = false;\n\n if (configExists()) {\n const existing = await readConfig();\n const alreadyHasDir = existing.targets.some(\n (t) => t.directory === directory\n );\n\n if (alreadyHasDir) {\n const { overwrite } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"overwrite\",\n message: `Target \"${directory}\" already exists. Overwrite it?`,\n default: false,\n },\n ]);\n if (!overwrite) {\n log.info(\"Setup cancelled.\");\n return;\n }\n existing.targets = existing.targets.filter(\n (t) => t.directory !== directory\n );\n } else {\n log.info(\n `Existing config found with ${existing.targets.length} target(s). Adding \"${directory}\".`\n );\n isAdding = true;\n }\n config = existing;\n } else {\n config = defaultConfig();\n }\n\n const dirPath = path.resolve(directory);\n if (!existsSync(dirPath)) {\n log.warn(\n `Directory \"${directory}\" doesn't exist yet — that's okay, it will be created later.`\n );\n }\n\n const target = await gatherTarget(directory, flags);\n config.targets.push(target);\n\n if (!isAdding) {\n await gatherSharedConfig(config, flags);\n }\n\n await writeConfig(config);\n log.success(\"Updated s3-sync.json\");\n\n const workflowDir = path.join(process.cwd(), \".github\", \"workflows\");\n await mkdir(workflowDir, { recursive: true });\n\n const workflowPath = path.join(workflowDir, \"s3-sync.yml\");\n const packageSpecifier = getCurrentPackageSpecifier();\n const workflowContent = generateWorkflow(config, packageSpecifier);\n await writeFile(workflowPath, workflowContent, \"utf-8\");\n log.success(\"Updated .github/workflows/s3-sync.yml\");\n\n printSecretInstructions(config);\n}\n\nasync function gatherTarget(\n directory: string,\n flags: SetupFlags\n): Promise<SyncTarget> {\n const target = defaultTarget();\n target.directory = directory;\n\n const questions: any[] = [];\n\n if (flags.bucket === undefined) {\n questions.push({\n type: \"input\",\n name: \"bucket\",\n message: `[${directory}] S3 bucket name:`,\n validate: (v: string) => (v.trim() ? true : \"Bucket name is required\"),\n });\n } else {\n target.bucket = flags.bucket;\n }\n\n if (flags.region === undefined) {\n questions.push({\n type: \"input\",\n name: \"region\",\n message: `[${directory}] AWS region:`,\n default: \"us-east-1\",\n });\n } else {\n target.region = flags.region;\n }\n\n if (flags.endpoint === undefined) {\n questions.push({\n type: \"input\",\n name: \"endpoint\",\n message: `[${directory}] Custom S3 endpoint (leave blank for AWS):`,\n default: \"\",\n });\n } else {\n target.endpoint = flags.endpoint;\n }\n\n if (flags.prefix === undefined) {\n questions.push({\n type: \"input\",\n name: \"prefix\",\n message: `[${directory}] Path prefix (leave blank for root):`,\n default: \"\",\n });\n } else {\n target.prefix = flags.prefix;\n }\n\n if (flags.delete === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"delete\",\n message: `[${directory}] Delete files from S3 that no longer exist locally?`,\n default: false,\n });\n } else {\n target.delete = flags.delete;\n }\n\n if (questions.length > 0) {\n const answers = await inquirer.prompt(questions);\n if (answers.bucket !== undefined) target.bucket = answers.bucket;\n if (answers.region !== undefined) target.region = answers.region;\n if (answers.endpoint !== undefined) target.endpoint = answers.endpoint;\n if (answers.prefix !== undefined) target.prefix = answers.prefix;\n if (answers.delete !== undefined) target.delete = answers.delete;\n }\n\n return target;\n}\n\nasync function gatherSharedConfig(\n config: S3SyncConfig,\n flags: SetupFlags\n): Promise<void> {\n const questions: any[] = [];\n\n if (flags.branch === undefined) {\n questions.push({\n type: \"input\",\n name: \"branch\",\n message: \"Branch to trigger sync on:\",\n default: \"main\",\n });\n } else {\n config.branch = flags.branch;\n }\n\n if (flags.slack === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"slack\",\n message: \"Enable Slack notifications?\",\n default: false,\n });\n } else {\n config.notifications.slack = flags.slack;\n }\n\n if (flags.discord === undefined) {\n questions.push({\n type: \"confirm\",\n name: \"discord\",\n message: \"Enable Discord notifications?\",\n default: false,\n });\n } else {\n config.notifications.discord = flags.discord;\n }\n\n if (questions.length > 0) {\n const answers = await inquirer.prompt(questions);\n if (answers.branch !== undefined) config.branch = answers.branch;\n if (answers.slack !== undefined) config.notifications.slack = answers.slack;\n if (answers.discord !== undefined)\n config.notifications.discord = answers.discord;\n }\n}\n\nfunction printSecretInstructions(config: S3SyncConfig): void {\n log.heading(\"Next steps\");\n log.info(\"Add these secrets to your GitHub repository:\");\n log.dim(\n \" Settings → Secrets and variables → Actions → New repository secret\\n\"\n );\n\n const secrets = [\n [\"S3_ACCESS_KEY_ID\", \"Your S3 access key\"],\n [\"S3_SECRET_ACCESS_KEY\", \"Your S3 secret key\"],\n ];\n\n if (config.notifications.slack) {\n secrets.push([\"SLACK_WEBHOOK_URL\", \"Slack incoming webhook URL\"]);\n }\n\n if (config.notifications.discord) {\n secrets.push([\"DISCORD_WEBHOOK_URL\", \"Discord webhook URL\"]);\n }\n\n for (const [name, desc] of secrets) {\n log.dim(` • ${name} — ${desc}`);\n }\n\n console.log();\n log.heading(\"Configured targets\");\n for (const t of config.targets) {\n log.info(` ${t.directory}/ → s3://${t.bucket}/${t.prefix}`);\n }\n\n console.log();\n log.success(\n `Push to \"${config.branch}\" to trigger a sync.`\n );\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nexport interface SyncTarget {\n directory: string;\n bucket: string;\n region: string;\n endpoint: string;\n prefix: string;\n delete: boolean;\n}\n\nexport interface S3SyncConfig {\n targets: SyncTarget[];\n branch: string;\n notifications: {\n slack: boolean;\n discord: boolean;\n };\n}\n\nconst CONFIG_FILE = \"s3-sync.json\";\n\nconst syncTargetSchema: z.ZodType<SyncTarget> = z.object({\n directory: z.string().min(1),\n bucket: z.string().min(1),\n region: z.string().min(1),\n endpoint: z.string().default(\"\"),\n prefix: z.string().default(\"\"),\n delete: z.boolean().default(false),\n});\n\nconst notificationsSchema: z.ZodType<S3SyncConfig[\"notifications\"]> = z.object({\n slack: z.boolean().default(false),\n discord: z.boolean().default(false),\n});\n\nconst configSchema: z.ZodType<S3SyncConfig> = z.object({\n targets: z.array(syncTargetSchema).default([]),\n branch: z.string().min(1).default(\"main\"),\n notifications: notificationsSchema.default({ slack: false, discord: false }),\n});\n\nconst legacyConfigSchema = z.object({\n directory: z.string().min(1),\n bucket: z.string().min(1),\n region: z.string().min(1),\n endpoint: z.string().default(\"\"),\n prefix: z.string().default(\"\"),\n branch: z.string().min(1).default(\"main\"),\n delete: z.boolean().default(false),\n notifications: notificationsSchema.default({ slack: false, discord: false }),\n});\n\nexport function getConfigPath(cwd: string = process.cwd()): string {\n return path.join(cwd, CONFIG_FILE);\n}\n\nexport function configExists(cwd: string = process.cwd()): boolean {\n return existsSync(getConfigPath(cwd));\n}\n\nexport async function readConfig(\n cwd: string = process.cwd()\n): Promise<S3SyncConfig> {\n const configPath = getConfigPath(cwd);\n const raw = JSON.parse(await readFile(configPath, \"utf-8\"));\n const parsedConfig = configSchema.safeParse(raw);\n if (parsedConfig.success) return parsedConfig.data;\n\n const parsedLegacyConfig = legacyConfigSchema.safeParse(raw);\n if (!parsedLegacyConfig.success) {\n const issues = parsedConfig.error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"root\";\n return `${path}: ${issue.message}`;\n })\n .join(\"; \");\n throw new Error(`Invalid ${CONFIG_FILE}: ${issues}`);\n }\n\n const legacy = parsedLegacyConfig.data;\n return {\n targets: [\n {\n directory: legacy.directory,\n bucket: legacy.bucket,\n region: legacy.region,\n endpoint: legacy.endpoint,\n prefix: legacy.prefix,\n delete: legacy.delete,\n },\n ],\n branch: legacy.branch,\n notifications: legacy.notifications,\n };\n}\n\nexport async function writeConfig(\n config: S3SyncConfig,\n cwd: string = process.cwd()\n): Promise<void> {\n const configPath = getConfigPath(cwd);\n const validatedConfig = configSchema.parse(config);\n await writeFile(\n configPath,\n JSON.stringify(validatedConfig, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\nexport function defaultTarget(): SyncTarget {\n return {\n directory: \"public\",\n bucket: \"\",\n region: \"us-east-1\",\n endpoint: \"\",\n prefix: \"\",\n delete: false,\n };\n}\n\nexport function defaultConfig(): S3SyncConfig {\n return {\n targets: [],\n branch: \"main\",\n notifications: { slack: false, discord: false },\n };\n}\n","import type { S3SyncConfig } from \"../utils/config.js\";\n\nexport function generateWorkflow(\n config: S3SyncConfig,\n packageSpecifier: string\n): string {\n const envBlock = buildEnvBlock(config);\n const pathFilters = [\n ...config.targets.map((t) => `${t.directory}/**`),\n \"s3-sync.json\",\n \".github/workflows/s3-sync.yml\",\n ]\n .map((pattern) => ` - '${pattern}'`)\n .join(\"\\n\");\n\n return `name: S3 Sync\n\non:\n push:\n branches: [${config.branch}]\n paths:\n${pathFilters}\n workflow_dispatch:\n\njobs:\n sync:\n name: Sync to S3\n runs-on: ubuntu-latest\n\n steps:\n - name: Checkout\n uses: actions/checkout@v4\n\n - name: Setup Node\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Sync files\n run: npx --yes ${packageSpecifier} sync\n${envBlock}\n`;\n}\n\nfunction buildEnvBlock(config: S3SyncConfig): string {\n const lines = [\n \" env:\",\n \" S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}\",\n \" S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}\",\n ];\n\n if (config.notifications.slack) {\n lines.push(\n \" SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}\"\n );\n }\n\n if (config.notifications.discord) {\n lines.push(\n \" DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}\"\n );\n }\n\n return lines.join(\"\\n\");\n}\n","import chalk from \"chalk\";\n\nexport const log = {\n info: (msg: string) => console.log(chalk.blue(\"ℹ\"), msg),\n success: (msg: string) => console.log(chalk.green(\"✓\"), msg),\n warn: (msg: string) => console.log(chalk.yellow(\"⚠\"), msg),\n error: (msg: string) => console.error(chalk.red(\"✗\"), msg),\n dim: (msg: string) => console.log(chalk.dim(msg)),\n heading: (msg: string) => console.log(chalk.bold.cyan(`\\n${msg}\\n`)),\n};\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageMeta {\n name?: string;\n version?: string;\n}\n\nfunction findPackageJson(startDirectory: string): string | undefined {\n let currentDirectory = startDirectory;\n while (true) {\n const candidate = path.join(currentDirectory, \"package.json\");\n if (existsSync(candidate)) return candidate;\n const parentDirectory = path.dirname(currentDirectory);\n if (parentDirectory === currentDirectory) return undefined;\n currentDirectory = parentDirectory;\n }\n}\n\nexport function getCurrentPackageSpecifier(\n fallbackName = \"@maydotinc/s3-sync\"\n): string {\n const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = findPackageJson(currentFileDirectory);\n if (!packageJsonPath) return fallbackName;\n\n try {\n const parsed = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageMeta;\n const packageName = parsed.name?.trim() || fallbackName;\n const packageVersion = parsed.version?.trim();\n return packageVersion ? `${packageName}@${packageVersion}` : packageName;\n } catch {\n return fallbackName;\n }\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { readConfig, type SyncTarget } from \"../utils/config.js\";\nimport { log } from \"../utils/logger.js\";\nimport { fingerprintDirectory } from \"../core/fingerprint.js\";\nimport {\n createS3,\n listAllObjects,\n uploadFile,\n deleteObject,\n} from \"../core/s3-client.js\";\nimport { computeDiff, type DiffResult } from \"../core/diff.js\";\nimport { sendSlackNotification } from \"../notifications/slack.js\";\nimport { sendDiscordNotification } from \"../notifications/discord.js\";\n\nexport async function syncCommand(): Promise<void> {\n log.heading(\"s3-sync\");\n\n const config = await readConfig();\n\n const accessKeyId = process.env.S3_ACCESS_KEY_ID;\n const secretAccessKey = process.env.S3_SECRET_ACCESS_KEY;\n\n if (!accessKeyId || !secretAccessKey) {\n log.error(\n \"Missing S3_ACCESS_KEY_ID or S3_SECRET_ACCESS_KEY environment variables.\"\n );\n process.exit(1);\n }\n\n if (config.targets.length === 0) {\n log.error(\"No sync targets configured. Run setup first.\");\n process.exit(1);\n }\n\n const summaries: string[] = [];\n let hasErrors = false;\n const globalEndpoint = process.env.S3_ENDPOINT?.trim() || undefined;\n const hasTargetSpecificEndpoints = config.targets.some(\n (target) => target.endpoint.trim().length > 0\n );\n\n for (const target of config.targets) {\n try {\n const endpoint = resolveEndpoint(\n target.endpoint,\n globalEndpoint,\n hasTargetSpecificEndpoints\n );\n const diff = await syncTarget(\n target,\n accessKeyId,\n secretAccessKey,\n endpoint\n );\n summaries.push(buildTargetSummary(target, diff));\n } catch (err: any) {\n log.error(`[${target.directory}] ${err.message}`);\n hasErrors = true;\n }\n }\n\n if (summaries.length > 0) {\n const fullSummary = summaries.join(\"\\n\\n---\\n\\n\");\n await notify(config, fullSummary);\n }\n\n if (hasErrors) {\n process.exit(1);\n }\n\n log.success(\"All targets synced.\");\n}\n\nasync function syncTarget(\n target: SyncTarget,\n accessKeyId: string,\n secretAccessKey: string,\n endpoint: string | undefined\n): Promise<DiffResult> {\n log.heading(`Syncing ${target.directory}/ → s3://${target.bucket}/${target.prefix}`);\n\n const directory = path.resolve(target.directory);\n if (!existsSync(directory)) {\n throw new Error(`Directory \"${target.directory}\" does not exist.`);\n }\n\n const client = createS3({\n bucket: target.bucket,\n region: target.region,\n endpoint,\n accessKeyId,\n secretAccessKey,\n });\n\n log.info(`Scanning ${target.directory}/...`);\n const localFiles = await fingerprintDirectory(directory);\n log.info(`Found ${localFiles.length} local files`);\n\n log.info(`Listing objects in s3://${target.bucket}/${target.prefix}...`);\n const remoteObjects = await listAllObjects(\n client,\n target.bucket,\n target.prefix\n );\n log.info(`Found ${remoteObjects.length} remote objects`);\n\n const diff = computeDiff(\n localFiles,\n remoteObjects,\n target.prefix,\n target.delete\n );\n\n log.info(\n `${diff.toUpload.length} to upload, ${diff.toDelete.length} to delete, ${diff.unchanged.length} unchanged`\n );\n\n if (diff.toUpload.length === 0 && diff.toDelete.length === 0) {\n log.success(`[${target.directory}] Already in sync.`);\n return diff;\n }\n\n for (const file of diff.toUpload) {\n const key = target.prefix\n ? `${target.prefix}/${file.relativePath}`\n : file.relativePath;\n log.dim(` uploading ${key}`);\n await uploadFile(client, target.bucket, key, file.absolutePath);\n }\n\n if (diff.toUpload.length > 0) {\n log.success(`[${target.directory}] Uploaded ${diff.toUpload.length} files`);\n }\n\n for (const obj of diff.toDelete) {\n log.dim(` deleting ${obj.key}`);\n await deleteObject(client, target.bucket, obj.key);\n }\n\n if (diff.toDelete.length > 0) {\n log.success(`[${target.directory}] Deleted ${diff.toDelete.length} files`);\n }\n\n return diff;\n}\n\nfunction resolveEndpoint(\n targetEndpoint: string,\n globalEndpoint: string | undefined,\n hasTargetSpecificEndpoints: boolean\n): string | undefined {\n const trimmedTargetEndpoint = targetEndpoint.trim();\n if (trimmedTargetEndpoint) return trimmedTargetEndpoint;\n if (!hasTargetSpecificEndpoints) return globalEndpoint;\n return undefined;\n}\n\nfunction buildTargetSummary(target: SyncTarget, diff: DiffResult): string {\n const lines = [\n `*${target.directory}/ → s3://${target.bucket}/${target.prefix}*`,\n `Uploaded: ${diff.toUpload.length}`,\n `Deleted: ${diff.toDelete.length}`,\n `Unchanged: ${diff.unchanged.length}`,\n ];\n\n if (diff.toUpload.length > 0) {\n const fileList = diff.toUpload\n .slice(0, 10)\n .map((f) => ` • ${f.relativePath}`)\n .join(\"\\n\");\n lines.push(`\\nUploaded files:\\n${fileList}`);\n if (diff.toUpload.length > 10) {\n lines.push(` ...and ${diff.toUpload.length - 10} more`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nasync function notify(config: any, message: string): Promise<void> {\n if (config.notifications?.slack) {\n const webhookUrl = process.env.SLACK_WEBHOOK_URL;\n if (webhookUrl) {\n try {\n await sendSlackNotification(webhookUrl, message);\n log.success(\"Slack notification sent\");\n } catch (err: any) {\n log.warn(`Slack notification failed: ${err.message}`);\n }\n }\n }\n\n if (config.notifications?.discord) {\n const webhookUrl = process.env.DISCORD_WEBHOOK_URL;\n if (webhookUrl) {\n try {\n await sendDiscordNotification(webhookUrl, message);\n log.success(\"Discord notification sent\");\n } catch (err: any) {\n log.warn(`Discord notification failed: ${err.message}`);\n }\n }\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { glob } from \"glob\";\n\nexport interface LocalFile {\n relativePath: string;\n absolutePath: string;\n md5: string;\n size: number;\n}\n\nexport async function fingerprintDirectory(\n directory: string\n): Promise<LocalFile[]> {\n const absoluteDir = path.resolve(directory);\n const files = await glob(\"**/*\", {\n cwd: absoluteDir,\n nodir: true,\n dot: false,\n });\n\n const results: LocalFile[] = [];\n\n await Promise.all(\n files.map(async (relativePath) => {\n const absolutePath = path.join(absoluteDir, relativePath);\n const content = await readFile(absolutePath);\n const md5 = createHash(\"md5\").update(content).digest(\"hex\");\n\n results.push({\n relativePath: relativePath.replace(/\\\\/g, \"/\"),\n absolutePath,\n md5,\n size: content.length,\n });\n })\n );\n\n return results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));\n}\n","import {\n S3Client,\n ListObjectsV2Command,\n type ListObjectsV2CommandOutput,\n PutObjectCommand,\n DeleteObjectCommand,\n} from \"@aws-sdk/client-s3\";\nimport { readFile } from \"node:fs/promises\";\nimport mime from \"mime-types\";\n\nexport interface S3Object {\n key: string;\n etag: string;\n size: number;\n}\n\nexport interface S3Config {\n bucket: string;\n region: string;\n endpoint?: string;\n accessKeyId: string;\n secretAccessKey: string;\n}\n\nexport function createS3(config: S3Config): S3Client {\n return new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n ...(config.endpoint && {\n endpoint: config.endpoint,\n forcePathStyle: true,\n }),\n });\n}\n\nexport async function listAllObjects(\n client: S3Client,\n bucket: string,\n prefix: string\n): Promise<S3Object[]> {\n const objects: S3Object[] = [];\n let continuationToken: string | undefined;\n\n do {\n const command = new ListObjectsV2Command({\n Bucket: bucket,\n Prefix: prefix || undefined,\n ContinuationToken: continuationToken,\n });\n\n const response: ListObjectsV2CommandOutput = await client.send(command);\n\n if (response.Contents) {\n for (const obj of response.Contents) {\n if (obj.Key && obj.ETag && obj.Size !== undefined) {\n objects.push({\n key: obj.Key,\n etag: obj.ETag.replace(/\"/g, \"\"),\n size: obj.Size,\n });\n }\n }\n }\n\n continuationToken = response.IsTruncated\n ? response.NextContinuationToken\n : undefined;\n } while (continuationToken);\n\n return objects;\n}\n\nexport async function uploadFile(\n client: S3Client,\n bucket: string,\n key: string,\n filePath: string\n): Promise<void> {\n const body = await readFile(filePath);\n const contentType = mime.lookup(filePath) || \"application/octet-stream\";\n\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n })\n );\n}\n\nexport async function deleteObject(\n client: S3Client,\n bucket: string,\n key: string\n): Promise<void> {\n await client.send(\n new DeleteObjectCommand({\n Bucket: bucket,\n Key: key,\n })\n );\n}\n","import type { LocalFile } from \"./fingerprint.js\";\nimport type { S3Object } from \"./s3-client.js\";\n\nexport interface DiffResult {\n toUpload: LocalFile[];\n toDelete: S3Object[];\n unchanged: LocalFile[];\n}\n\nexport function computeDiff(\n localFiles: LocalFile[],\n remoteObjects: S3Object[],\n prefix: string,\n shouldDelete: boolean\n): DiffResult {\n const remoteByKey = new Map<string, S3Object>();\n for (const obj of remoteObjects) {\n remoteByKey.set(obj.key, obj);\n }\n\n const toUpload: LocalFile[] = [];\n const unchanged: LocalFile[] = [];\n const localKeys = new Set<string>();\n\n for (const file of localFiles) {\n const key = prefix ? `${prefix}/${file.relativePath}` : file.relativePath;\n localKeys.add(key);\n\n const remote = remoteByKey.get(key);\n\n if (!remote || remote.etag !== file.md5) {\n toUpload.push(file);\n } else {\n unchanged.push(file);\n }\n }\n\n const toDelete: S3Object[] = [];\n if (shouldDelete) {\n for (const obj of remoteObjects) {\n if (!localKeys.has(obj.key)) {\n toDelete.push(obj);\n }\n }\n }\n\n return { toUpload, toDelete, unchanged };\n}\n","export async function sendSlackNotification(\n webhookUrl: string,\n message: string\n): Promise<void> {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ text: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Slack notification failed: ${response.statusText}`);\n }\n}\n","export async function sendDiscordNotification(\n webhookUrl: string,\n message: string\n): Promise<void> {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content: message }),\n });\n\n if (!response.ok) {\n throw new Error(`Discord notification failed: ${response.statusText}`);\n }\n}\n"],"mappings":"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","names":["writeFile","existsSync","path","path","existsSync","path","path","existsSync","writeFile","existsSync","path","readFile","path","readFile","path","existsSync"]}
|