launchpd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of launchpd might be problematic. Click here for more details.

@@ -0,0 +1,74 @@
1
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import { join, relative, posix, sep } from 'node:path';
4
+ import mime from 'mime-types';
5
+ import { config } from '../config.js';
6
+ import { info } from './logger.js';
7
+
8
+ /**
9
+ * Create S3-compatible client for Cloudflare R2
10
+ */
11
+ function createR2Client() {
12
+ return new S3Client({
13
+ region: 'auto',
14
+ endpoint: `https://${config.r2.accountId}.r2.cloudflarestorage.com`,
15
+ credentials: {
16
+ accessKeyId: config.r2.accessKeyId,
17
+ secretAccessKey: config.r2.secretAccessKey,
18
+ },
19
+ });
20
+ }
21
+
22
+ /**
23
+ * Convert Windows path to POSIX for R2 keys
24
+ * @param {string} windowsPath
25
+ * @returns {string}
26
+ */
27
+ function toPosixPath(windowsPath) {
28
+ return windowsPath.split(sep).join(posix.sep);
29
+ }
30
+
31
+ /**
32
+ * Upload a folder to R2 under a subdomain prefix with versioning
33
+ * @param {string} localPath - Local folder path
34
+ * @param {string} subdomain - Subdomain to use as bucket prefix
35
+ * @param {number} version - Version number for this deployment
36
+ */
37
+ export async function uploadFolder(localPath, subdomain, version = 1) {
38
+ const client = createR2Client();
39
+ const files = await readdir(localPath, { recursive: true, withFileTypes: true });
40
+
41
+ let uploaded = 0;
42
+ let totalBytes = 0;
43
+ const total = files.filter(f => f.isFile()).length;
44
+
45
+ for (const file of files) {
46
+ if (!file.isFile()) continue;
47
+
48
+ // Build full local path
49
+ const fullPath = join(file.parentPath || file.path, file.name);
50
+
51
+ // Build R2 key: subdomain/v{version}/relative/path/to/file.ext
52
+ const relativePath = relative(localPath, fullPath);
53
+ const key = `${subdomain}/v${version}/${toPosixPath(relativePath)}`;
54
+
55
+ // Detect content type
56
+ const contentType = mime.lookup(file.name) || 'application/octet-stream';
57
+
58
+ // Read file and upload
59
+ const body = await readFile(fullPath);
60
+ totalBytes += body.length;
61
+
62
+ await client.send(new PutObjectCommand({
63
+ Bucket: config.r2.bucketName,
64
+ Key: key,
65
+ Body: body,
66
+ ContentType: contentType,
67
+ }));
68
+
69
+ uploaded++;
70
+ info(` Uploaded (${uploaded}/${total}): ${toPosixPath(relativePath)}`);
71
+ }
72
+
73
+ return { uploaded, subdomain, totalBytes };
74
+ }