@maydotinc/s3-sync 0.1.1 → 0.1.2
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 +130 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -524,26 +524,118 @@ function computeDiff(localFiles, remoteObjects, prefix, shouldDelete) {
|
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
// src/notifications/slack.ts
|
|
527
|
-
|
|
527
|
+
var SLACK_GREEN = "#2EB67D";
|
|
528
|
+
var SLACK_ATTACHMENT_TEXT_LIMIT = 7e3;
|
|
529
|
+
var SLACK_ATTACHMENT_TITLE_LIMIT = 256;
|
|
530
|
+
var SLACK_FIELD_TITLE_LIMIT = 200;
|
|
531
|
+
var SLACK_FIELD_VALUE_LIMIT = 2e3;
|
|
532
|
+
function truncate(input, maxLength) {
|
|
533
|
+
if (input.length <= maxLength) return input;
|
|
534
|
+
return `${input.slice(0, maxLength - 13)}
|
|
535
|
+
|
|
536
|
+
...[truncated]`;
|
|
537
|
+
}
|
|
538
|
+
function normalizePayload(payload) {
|
|
539
|
+
if (typeof payload === "string") {
|
|
540
|
+
return { message: payload };
|
|
541
|
+
}
|
|
542
|
+
return payload;
|
|
543
|
+
}
|
|
544
|
+
async function sendSlackNotification(webhookUrl, payload) {
|
|
545
|
+
const normalized = normalizePayload(payload);
|
|
546
|
+
const title = truncate(
|
|
547
|
+
normalized.title?.trim() || "S3 Sync Completed",
|
|
548
|
+
SLACK_ATTACHMENT_TITLE_LIMIT
|
|
549
|
+
);
|
|
550
|
+
const text = normalized.message ? truncate(normalized.message, SLACK_ATTACHMENT_TEXT_LIMIT) : void 0;
|
|
551
|
+
const fields = (normalized.fields ?? []).filter((field) => field.name.trim() && field.value.trim()).map((field) => ({
|
|
552
|
+
title: truncate(field.name.trim(), SLACK_FIELD_TITLE_LIMIT),
|
|
553
|
+
value: truncate(field.value.trim(), SLACK_FIELD_VALUE_LIMIT),
|
|
554
|
+
short: field.inline ?? false
|
|
555
|
+
}));
|
|
556
|
+
const attachment = {
|
|
557
|
+
color: SLACK_GREEN,
|
|
558
|
+
title,
|
|
559
|
+
mrkdwn_in: ["text", "fields"],
|
|
560
|
+
footer: "@maydotinc/s3-sync"
|
|
561
|
+
};
|
|
562
|
+
if (text) {
|
|
563
|
+
attachment.text = text;
|
|
564
|
+
}
|
|
565
|
+
if (fields.length > 0) {
|
|
566
|
+
attachment.fields = fields;
|
|
567
|
+
}
|
|
528
568
|
const response = await fetch(webhookUrl, {
|
|
529
569
|
method: "POST",
|
|
530
570
|
headers: { "Content-Type": "application/json" },
|
|
531
|
-
body: JSON.stringify({
|
|
571
|
+
body: JSON.stringify({
|
|
572
|
+
text: normalized.title?.trim() || "S3 sync completed",
|
|
573
|
+
attachments: [attachment]
|
|
574
|
+
})
|
|
532
575
|
});
|
|
533
576
|
if (!response.ok) {
|
|
534
|
-
|
|
577
|
+
const body = await response.text();
|
|
578
|
+
throw new Error(
|
|
579
|
+
`Slack notification failed (${response.status}): ${body || response.statusText}`
|
|
580
|
+
);
|
|
535
581
|
}
|
|
536
582
|
}
|
|
537
583
|
|
|
538
584
|
// src/notifications/discord.ts
|
|
539
|
-
|
|
585
|
+
var DISCORD_GREEN = 3061373;
|
|
586
|
+
var DISCORD_EMBED_DESCRIPTION_LIMIT = 4096;
|
|
587
|
+
var DISCORD_EMBED_TITLE_LIMIT = 256;
|
|
588
|
+
var DISCORD_EMBED_FIELD_NAME_LIMIT = 256;
|
|
589
|
+
var DISCORD_EMBED_FIELD_VALUE_LIMIT = 1024;
|
|
590
|
+
var DISCORD_EMBED_FIELD_MAX = 25;
|
|
591
|
+
function truncate2(input, maxLength) {
|
|
592
|
+
if (input.length <= maxLength) return input;
|
|
593
|
+
return `${input.slice(0, maxLength - 13)}
|
|
594
|
+
|
|
595
|
+
...[truncated]`;
|
|
596
|
+
}
|
|
597
|
+
function normalizePayload2(payload) {
|
|
598
|
+
if (typeof payload === "string") {
|
|
599
|
+
return { message: payload };
|
|
600
|
+
}
|
|
601
|
+
return payload;
|
|
602
|
+
}
|
|
603
|
+
async function sendDiscordNotification(webhookUrl, payload) {
|
|
604
|
+
const normalized = normalizePayload2(payload);
|
|
605
|
+
const title = truncate2(
|
|
606
|
+
normalized.title?.trim() || "S3 Sync Completed",
|
|
607
|
+
DISCORD_EMBED_TITLE_LIMIT
|
|
608
|
+
);
|
|
609
|
+
const description = normalized.message ? truncate2(normalized.message, DISCORD_EMBED_DESCRIPTION_LIMIT) : void 0;
|
|
610
|
+
const fields = (normalized.fields ?? []).filter((field) => field.name.trim() && field.value.trim()).slice(0, DISCORD_EMBED_FIELD_MAX).map((field) => ({
|
|
611
|
+
name: truncate2(field.name.trim(), DISCORD_EMBED_FIELD_NAME_LIMIT),
|
|
612
|
+
value: truncate2(field.value.trim(), DISCORD_EMBED_FIELD_VALUE_LIMIT),
|
|
613
|
+
inline: field.inline ?? false
|
|
614
|
+
}));
|
|
615
|
+
const embed = {
|
|
616
|
+
title,
|
|
617
|
+
color: DISCORD_GREEN,
|
|
618
|
+
footer: { text: "@maydotinc/s3-sync" },
|
|
619
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
620
|
+
};
|
|
621
|
+
if (description) {
|
|
622
|
+
embed.description = description;
|
|
623
|
+
}
|
|
624
|
+
if (fields.length > 0) {
|
|
625
|
+
embed.fields = fields;
|
|
626
|
+
}
|
|
540
627
|
const response = await fetch(webhookUrl, {
|
|
541
628
|
method: "POST",
|
|
542
629
|
headers: { "Content-Type": "application/json" },
|
|
543
|
-
body: JSON.stringify({
|
|
630
|
+
body: JSON.stringify({
|
|
631
|
+
embeds: [embed]
|
|
632
|
+
})
|
|
544
633
|
});
|
|
545
634
|
if (!response.ok) {
|
|
546
|
-
|
|
635
|
+
const body = await response.text();
|
|
636
|
+
throw new Error(
|
|
637
|
+
`Discord notification failed (${response.status}): ${body || response.statusText}`
|
|
638
|
+
);
|
|
547
639
|
}
|
|
548
640
|
}
|
|
549
641
|
|
|
@@ -563,7 +655,11 @@ async function syncCommand() {
|
|
|
563
655
|
log.error("No sync targets configured. Run setup first.");
|
|
564
656
|
process.exit(1);
|
|
565
657
|
}
|
|
566
|
-
|
|
658
|
+
let syncedTargets = 0;
|
|
659
|
+
const syncedTargetMappings = [];
|
|
660
|
+
let totalUploaded = 0;
|
|
661
|
+
let totalDeleted = 0;
|
|
662
|
+
let totalUnchanged = 0;
|
|
567
663
|
let hasErrors = false;
|
|
568
664
|
const globalEndpoint = process.env.S3_ENDPOINT?.trim() || void 0;
|
|
569
665
|
const hasTargetSpecificEndpoints = config.targets.some(
|
|
@@ -582,15 +678,32 @@ async function syncCommand() {
|
|
|
582
678
|
secretAccessKey,
|
|
583
679
|
endpoint
|
|
584
680
|
);
|
|
585
|
-
|
|
681
|
+
syncedTargets += 1;
|
|
682
|
+
syncedTargetMappings.push(buildTargetMapping(target));
|
|
683
|
+
totalUploaded += diff.toUpload.length;
|
|
684
|
+
totalDeleted += diff.toDelete.length;
|
|
685
|
+
totalUnchanged += diff.unchanged.length;
|
|
586
686
|
} catch (err) {
|
|
587
687
|
log.error(`[${target.directory}] ${err.message}`);
|
|
588
688
|
hasErrors = true;
|
|
589
689
|
}
|
|
590
690
|
}
|
|
591
|
-
if (
|
|
592
|
-
const
|
|
593
|
-
|
|
691
|
+
if (syncedTargets > 0) {
|
|
692
|
+
const targetLabel = syncedTargets === 1 ? "target" : "targets";
|
|
693
|
+
const message = hasErrors ? `Synced ${syncedTargets} ${targetLabel}. Some targets failed.` : `Synced ${syncedTargets} ${targetLabel}.`;
|
|
694
|
+
await notify(config, {
|
|
695
|
+
title: hasErrors ? "S3 Sync Completed with Errors" : "S3 Sync Completed",
|
|
696
|
+
message,
|
|
697
|
+
fields: [
|
|
698
|
+
{
|
|
699
|
+
name: syncedTargetMappings.length === 1 ? "Target" : "Targets",
|
|
700
|
+
value: syncedTargetMappings.map((mapping) => `- ${mapping}`).join("\n")
|
|
701
|
+
},
|
|
702
|
+
{ name: "Uploaded", value: String(totalUploaded), inline: true },
|
|
703
|
+
{ name: "Deleted", value: String(totalDeleted), inline: true },
|
|
704
|
+
{ name: "Unchanged", value: String(totalUnchanged), inline: true }
|
|
705
|
+
]
|
|
706
|
+
});
|
|
594
707
|
}
|
|
595
708
|
if (hasErrors) {
|
|
596
709
|
process.exit(1);
|
|
@@ -656,30 +769,16 @@ function resolveEndpoint(targetEndpoint, globalEndpoint, hasTargetSpecificEndpoi
|
|
|
656
769
|
if (!hasTargetSpecificEndpoints) return globalEndpoint;
|
|
657
770
|
return void 0;
|
|
658
771
|
}
|
|
659
|
-
function
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
`Uploaded: ${diff.toUpload.length}`,
|
|
663
|
-
`Deleted: ${diff.toDelete.length}`,
|
|
664
|
-
`Unchanged: ${diff.unchanged.length}`
|
|
665
|
-
];
|
|
666
|
-
if (diff.toUpload.length > 0) {
|
|
667
|
-
const fileList = diff.toUpload.slice(0, 10).map((f) => ` \u2022 ${f.relativePath}`).join("\n");
|
|
668
|
-
lines.push(`
|
|
669
|
-
Uploaded files:
|
|
670
|
-
${fileList}`);
|
|
671
|
-
if (diff.toUpload.length > 10) {
|
|
672
|
-
lines.push(` ...and ${diff.toUpload.length - 10} more`);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
return lines.join("\n");
|
|
772
|
+
function buildTargetMapping(target) {
|
|
773
|
+
const remotePath = target.prefix.trim() ? `${target.bucket}/${target.prefix}` : `${target.bucket}/`;
|
|
774
|
+
return `${target.directory}/ -> ${remotePath}`;
|
|
676
775
|
}
|
|
677
|
-
async function notify(config,
|
|
776
|
+
async function notify(config, payload) {
|
|
678
777
|
if (config.notifications?.slack) {
|
|
679
778
|
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
|
|
680
779
|
if (webhookUrl) {
|
|
681
780
|
try {
|
|
682
|
-
await sendSlackNotification(webhookUrl,
|
|
781
|
+
await sendSlackNotification(webhookUrl, payload);
|
|
683
782
|
log.success("Slack notification sent");
|
|
684
783
|
} catch (err) {
|
|
685
784
|
log.warn(`Slack notification failed: ${err.message}`);
|
|
@@ -690,7 +789,7 @@ async function notify(config, message) {
|
|
|
690
789
|
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
|
|
691
790
|
if (webhookUrl) {
|
|
692
791
|
try {
|
|
693
|
-
await sendDiscordNotification(webhookUrl,
|
|
792
|
+
await sendDiscordNotification(webhookUrl, payload);
|
|
694
793
|
log.success("Discord notification sent");
|
|
695
794
|
} catch (err) {
|
|
696
795
|
log.warn(`Discord notification failed: ${err.message}`);
|
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 = [\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"]}
|
|
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 S3SyncConfig, 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\";\nimport type { NotificationPayload } from \"../notifications/types.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 let syncedTargets = 0;\n const syncedTargetMappings: string[] = [];\n let totalUploaded = 0;\n let totalDeleted = 0;\n let totalUnchanged = 0;\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 syncedTargets += 1;\n syncedTargetMappings.push(buildTargetMapping(target));\n totalUploaded += diff.toUpload.length;\n totalDeleted += diff.toDelete.length;\n totalUnchanged += diff.unchanged.length;\n } catch (err: any) {\n log.error(`[${target.directory}] ${err.message}`);\n hasErrors = true;\n }\n }\n\n if (syncedTargets > 0) {\n const targetLabel = syncedTargets === 1 ? \"target\" : \"targets\";\n const message = hasErrors\n ? `Synced ${syncedTargets} ${targetLabel}. Some targets failed.`\n : `Synced ${syncedTargets} ${targetLabel}.`;\n await notify(config, {\n title: hasErrors ? \"S3 Sync Completed with Errors\" : \"S3 Sync Completed\",\n message,\n fields: [\n {\n name: syncedTargetMappings.length === 1 ? \"Target\" : \"Targets\",\n value: syncedTargetMappings\n .map((mapping) => `- ${mapping}`)\n .join(\"\\n\"),\n },\n { name: \"Uploaded\", value: String(totalUploaded), inline: true },\n { name: \"Deleted\", value: String(totalDeleted), inline: true },\n { name: \"Unchanged\", value: String(totalUnchanged), inline: true },\n ],\n });\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 buildTargetMapping(target: SyncTarget): string {\n const remotePath = target.prefix.trim()\n ? `${target.bucket}/${target.prefix}`\n : `${target.bucket}/`;\n return `${target.directory}/ -> ${remotePath}`;\n}\n\nasync function notify(\n config: S3SyncConfig,\n payload: string | NotificationPayload\n): Promise<void> {\n if (config.notifications?.slack) {\n const webhookUrl = process.env.SLACK_WEBHOOK_URL;\n if (webhookUrl) {\n try {\n await sendSlackNotification(webhookUrl, payload);\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, payload);\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","import type { NotificationPayload } from \"./types.js\";\n\nconst SLACK_GREEN = \"#2EB67D\";\nconst SLACK_ATTACHMENT_TEXT_LIMIT = 7000;\nconst SLACK_ATTACHMENT_TITLE_LIMIT = 256;\nconst SLACK_FIELD_TITLE_LIMIT = 200;\nconst SLACK_FIELD_VALUE_LIMIT = 2000;\n\nfunction truncate(input: string, maxLength: number): string {\n if (input.length <= maxLength) return input;\n return `${input.slice(0, maxLength - 13)}\\n\\n...[truncated]`;\n}\n\nfunction normalizePayload(\n payload: string | NotificationPayload\n): NotificationPayload {\n if (typeof payload === \"string\") {\n return { message: payload };\n }\n return payload;\n}\n\nexport async function sendSlackNotification(\n webhookUrl: string,\n payload: string | NotificationPayload\n): Promise<void> {\n const normalized = normalizePayload(payload);\n const title = truncate(\n normalized.title?.trim() || \"S3 Sync Completed\",\n SLACK_ATTACHMENT_TITLE_LIMIT\n );\n const text = normalized.message\n ? truncate(normalized.message, SLACK_ATTACHMENT_TEXT_LIMIT)\n : undefined;\n const fields = (normalized.fields ?? [])\n .filter((field) => field.name.trim() && field.value.trim())\n .map((field) => ({\n title: truncate(field.name.trim(), SLACK_FIELD_TITLE_LIMIT),\n value: truncate(field.value.trim(), SLACK_FIELD_VALUE_LIMIT),\n short: field.inline ?? false,\n }));\n\n const attachment: {\n color: string;\n title: string;\n mrkdwn_in: string[];\n footer: string;\n text?: string;\n fields?: Array<{ title: string; value: string; short: boolean }>;\n } = {\n color: SLACK_GREEN,\n title,\n mrkdwn_in: [\"text\", \"fields\"],\n footer: \"@maydotinc/s3-sync\",\n };\n\n if (text) {\n attachment.text = text;\n }\n\n if (fields.length > 0) {\n attachment.fields = fields;\n }\n\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n text: normalized.title?.trim() || \"S3 sync completed\",\n attachments: [attachment],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Slack notification failed (${response.status}): ${body || response.statusText}`\n );\n }\n}\n","import type { NotificationPayload } from \"./types.js\";\n\nconst DISCORD_GREEN = 0x2eb67d;\nconst DISCORD_EMBED_DESCRIPTION_LIMIT = 4096;\nconst DISCORD_EMBED_TITLE_LIMIT = 256;\nconst DISCORD_EMBED_FIELD_NAME_LIMIT = 256;\nconst DISCORD_EMBED_FIELD_VALUE_LIMIT = 1024;\nconst DISCORD_EMBED_FIELD_MAX = 25;\n\nfunction truncate(input: string, maxLength: number): string {\n if (input.length <= maxLength) return input;\n return `${input.slice(0, maxLength - 13)}\\n\\n...[truncated]`;\n}\n\nfunction normalizePayload(\n payload: string | NotificationPayload\n): NotificationPayload {\n if (typeof payload === \"string\") {\n return { message: payload };\n }\n return payload;\n}\n\nexport async function sendDiscordNotification(\n webhookUrl: string,\n payload: string | NotificationPayload\n): Promise<void> {\n const normalized = normalizePayload(payload);\n const title = truncate(\n normalized.title?.trim() || \"S3 Sync Completed\",\n DISCORD_EMBED_TITLE_LIMIT\n );\n const description = normalized.message\n ? truncate(normalized.message, DISCORD_EMBED_DESCRIPTION_LIMIT)\n : undefined;\n const fields = (normalized.fields ?? [])\n .filter((field) => field.name.trim() && field.value.trim())\n .slice(0, DISCORD_EMBED_FIELD_MAX)\n .map((field) => ({\n name: truncate(field.name.trim(), DISCORD_EMBED_FIELD_NAME_LIMIT),\n value: truncate(field.value.trim(), DISCORD_EMBED_FIELD_VALUE_LIMIT),\n inline: field.inline ?? false,\n }));\n\n const embed: {\n title: string;\n color: number;\n footer: { text: string };\n timestamp: string;\n description?: string;\n fields?: Array<{ name: string; value: string; inline: boolean }>;\n } = {\n title,\n color: DISCORD_GREEN,\n footer: { text: \"@maydotinc/s3-sync\" },\n timestamp: new Date().toISOString(),\n };\n\n if (description) {\n embed.description = description;\n }\n\n if (fields.length > 0) {\n embed.fields = fields;\n }\n\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n embeds: [embed],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Discord notification failed (${response.status}): ${body || response.statusText}`\n );\n }\n}\n"],"mappings":"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","names":["writeFile","existsSync","path","path","existsSync","path","path","existsSync","writeFile","existsSync","path","readFile","path","readFile","truncate","normalizePayload","path","existsSync"]}
|