@letsroti/sourceembed 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @letsroti/sourceembed
2
+
3
+ Store embed JSON (or any raw text) to Cloudflare R2 and get a public URL back.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @letsroti/sourceembed
9
+ # or
10
+ yarn add @letsroti/sourceembed
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { SourceEmbed } from '@letsroti/sourceembed';
17
+
18
+ const sourceEmbed = new SourceEmbed({
19
+ accessKeyId: process.env.R2_ACCESS_KEY_ID!,
20
+ secretAccessKey: process.env.R2_ACCESS_KEY_SECRET!,
21
+ endpoint: process.env.R2_ENDPOINT_URL!,
22
+ bucket: 'roti-embed',
23
+ publicDomain: 'raw.letsroti.com',
24
+ });
25
+
26
+ // Store a single embed JSON
27
+ const { key, url } = await sourceEmbed.store(JSON.stringify(embedData, null, 2));
28
+ console.log(url); // https://raw.letsroti.com/<uuid>.json
29
+
30
+ // Store multiple embeds at once
31
+ const results = await sourceEmbed.storeMany(jsonArrayOfStrings);
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ | Option | Description |
37
+ | ---------------- | ----------------------------------------------- |
38
+ | `accessKeyId` | Cloudflare R2 access key ID |
39
+ | `secretAccessKey` | Cloudflare R2 secret access key |
40
+ | `endpoint` | R2 S3-compatible endpoint URL |
41
+ | `bucket` | R2 bucket name |
42
+ | `publicDomain` | Public domain for serving files (without `https://`) |
43
+
44
+ ## Environment Variables
45
+
46
+ Set these in your `.env` file (never commit credentials):
47
+
48
+ ```env
49
+ R2_ACCESS_KEY_ID=your_access_key_id
50
+ R2_ACCESS_KEY_SECRET=your_secret_access_key
51
+ R2_ENDPOINT_URL=https://your-account-id.r2.cloudflarestorage.com
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,40 @@
1
+ interface SourceEmbedOptions {
2
+ /** Cloudflare R2 access key ID */
3
+ accessKeyId: string;
4
+ /** Cloudflare R2 secret access key */
5
+ secretAccessKey: string;
6
+ /** Cloudflare R2 S3-compatible endpoint URL */
7
+ endpoint: string;
8
+ /** R2 bucket name */
9
+ bucket: string;
10
+ /** Public domain for serving files (without protocol) */
11
+ publicDomain: string;
12
+ }
13
+ interface StoreResult {
14
+ /** The unique key/filename of the stored object */
15
+ key: string;
16
+ /** The full public URL to access the stored content */
17
+ url: string;
18
+ }
19
+ declare class SourceEmbed {
20
+ private readonly s3;
21
+ private readonly bucket;
22
+ private readonly publicDomain;
23
+ constructor(options: SourceEmbedOptions);
24
+ /**
25
+ * Stores raw text (typically embed JSON) in R2 and returns a public URL.
26
+ * @param rawText - The text content to store
27
+ * @param extension - File extension (default: 'json')
28
+ * @returns The key and public URL of the stored object
29
+ */
30
+ store(rawText: string, extension?: string): Promise<StoreResult>;
31
+ /**
32
+ * Stores multiple raw texts in parallel.
33
+ * @param rawTexts - Array of text content to store
34
+ * @param extension - File extension (default: 'json')
35
+ * @returns Array of settled results (fulfilled with StoreResult or rejected with error)
36
+ */
37
+ storeMany(rawTexts: string[], extension?: string): Promise<PromiseSettledResult<StoreResult>[]>;
38
+ }
39
+
40
+ export { SourceEmbed, type SourceEmbedOptions, type StoreResult };
@@ -0,0 +1,40 @@
1
+ interface SourceEmbedOptions {
2
+ /** Cloudflare R2 access key ID */
3
+ accessKeyId: string;
4
+ /** Cloudflare R2 secret access key */
5
+ secretAccessKey: string;
6
+ /** Cloudflare R2 S3-compatible endpoint URL */
7
+ endpoint: string;
8
+ /** R2 bucket name */
9
+ bucket: string;
10
+ /** Public domain for serving files (without protocol) */
11
+ publicDomain: string;
12
+ }
13
+ interface StoreResult {
14
+ /** The unique key/filename of the stored object */
15
+ key: string;
16
+ /** The full public URL to access the stored content */
17
+ url: string;
18
+ }
19
+ declare class SourceEmbed {
20
+ private readonly s3;
21
+ private readonly bucket;
22
+ private readonly publicDomain;
23
+ constructor(options: SourceEmbedOptions);
24
+ /**
25
+ * Stores raw text (typically embed JSON) in R2 and returns a public URL.
26
+ * @param rawText - The text content to store
27
+ * @param extension - File extension (default: 'json')
28
+ * @returns The key and public URL of the stored object
29
+ */
30
+ store(rawText: string, extension?: string): Promise<StoreResult>;
31
+ /**
32
+ * Stores multiple raw texts in parallel.
33
+ * @param rawTexts - Array of text content to store
34
+ * @param extension - File extension (default: 'json')
35
+ * @returns Array of settled results (fulfilled with StoreResult or rejected with error)
36
+ */
37
+ storeMany(rawTexts: string[], extension?: string): Promise<PromiseSettledResult<StoreResult>[]>;
38
+ }
39
+
40
+ export { SourceEmbed, type SourceEmbedOptions, type StoreResult };
package/dist/index.js ADDED
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SourceEmbed: () => SourceEmbed
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_client_s3 = require("@aws-sdk/client-s3");
27
+ var import_node_crypto = require("crypto");
28
+ var SourceEmbed = class {
29
+ s3;
30
+ bucket;
31
+ publicDomain;
32
+ constructor(options) {
33
+ this.bucket = options.bucket;
34
+ this.publicDomain = options.publicDomain;
35
+ this.s3 = new import_client_s3.S3Client({
36
+ region: "auto",
37
+ endpoint: options.endpoint,
38
+ credentials: {
39
+ accessKeyId: options.accessKeyId,
40
+ secretAccessKey: options.secretAccessKey
41
+ }
42
+ });
43
+ }
44
+ /**
45
+ * Stores raw text (typically embed JSON) in R2 and returns a public URL.
46
+ * @param rawText - The text content to store
47
+ * @param extension - File extension (default: 'json')
48
+ * @returns The key and public URL of the stored object
49
+ */
50
+ async store(rawText, extension = "json") {
51
+ const key = `${(0, import_node_crypto.randomUUID)()}.${extension}`;
52
+ await this.s3.send(
53
+ new import_client_s3.PutObjectCommand({
54
+ Bucket: this.bucket,
55
+ Key: key,
56
+ Body: rawText,
57
+ ContentType: extension === "json" ? "application/json" : "text/plain"
58
+ })
59
+ );
60
+ return {
61
+ key,
62
+ url: `https://${this.publicDomain}/${key}`
63
+ };
64
+ }
65
+ /**
66
+ * Stores multiple raw texts in parallel.
67
+ * @param rawTexts - Array of text content to store
68
+ * @param extension - File extension (default: 'json')
69
+ * @returns Array of settled results (fulfilled with StoreResult or rejected with error)
70
+ */
71
+ async storeMany(rawTexts, extension = "json") {
72
+ return Promise.allSettled(rawTexts.map((text) => this.store(text, extension)));
73
+ }
74
+ };
75
+ // Annotate the CommonJS export names for ESM import in node:
76
+ 0 && (module.exports = {
77
+ SourceEmbed
78
+ });
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';\r\nimport { randomUUID } from 'node:crypto';\r\n\r\nexport interface SourceEmbedOptions {\r\n\t/** Cloudflare R2 access key ID */\r\n\taccessKeyId: string;\r\n\t/** Cloudflare R2 secret access key */\r\n\tsecretAccessKey: string;\r\n\t/** Cloudflare R2 S3-compatible endpoint URL */\r\n\tendpoint: string;\r\n\t/** R2 bucket name */\r\n\tbucket: string;\r\n\t/** Public domain for serving files (without protocol) */\r\n\tpublicDomain: string;\r\n}\r\n\r\nexport interface StoreResult {\r\n\t/** The unique key/filename of the stored object */\r\n\tkey: string;\r\n\t/** The full public URL to access the stored content */\r\n\turl: string;\r\n}\r\n\r\nexport class SourceEmbed {\r\n\tprivate readonly s3: S3Client;\r\n\tprivate readonly bucket: string;\r\n\tprivate readonly publicDomain: string;\r\n\r\n\tpublic constructor(options: SourceEmbedOptions) {\r\n\t\tthis.bucket = options.bucket;\r\n\t\tthis.publicDomain = options.publicDomain;\r\n\r\n\t\tthis.s3 = new S3Client({\r\n\t\t\tregion: 'auto',\r\n\t\t\tendpoint: options.endpoint,\r\n\t\t\tcredentials: {\r\n\t\t\t\taccessKeyId: options.accessKeyId,\r\n\t\t\t\tsecretAccessKey: options.secretAccessKey,\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Stores raw text (typically embed JSON) in R2 and returns a public URL.\r\n\t * @param rawText - The text content to store\r\n\t * @param extension - File extension (default: 'json')\r\n\t * @returns The key and public URL of the stored object\r\n\t */\r\n\tpublic async store(rawText: string, extension = 'json'): Promise<StoreResult> {\r\n\t\tconst key = `${randomUUID()}.${extension}`;\r\n\r\n\t\tawait this.s3.send(\r\n\t\t\tnew PutObjectCommand({\r\n\t\t\t\tBucket: this.bucket,\r\n\t\t\t\tKey: key,\r\n\t\t\t\tBody: rawText,\r\n\t\t\t\tContentType: extension === 'json' ? 'application/json' : 'text/plain',\r\n\t\t\t})\r\n\t\t);\r\n\r\n\t\treturn {\r\n\t\t\tkey,\r\n\t\t\turl: `https://${this.publicDomain}/${key}`,\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * Stores multiple raw texts in parallel.\r\n\t * @param rawTexts - Array of text content to store\r\n\t * @param extension - File extension (default: 'json')\r\n\t * @returns Array of settled results (fulfilled with StoreResult or rejected with error)\r\n\t */\r\n\tpublic async storeMany(rawTexts: string[], extension = 'json'): Promise<PromiseSettledResult<StoreResult>[]> {\r\n\t\treturn Promise.allSettled(rawTexts.map((text) => this.store(text, extension)));\r\n\t}\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAA2C;AAC3C,yBAA2B;AAsBpB,IAAM,cAAN,MAAkB;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,SAA6B;AAC/C,SAAK,SAAS,QAAQ;AACtB,SAAK,eAAe,QAAQ;AAE5B,SAAK,KAAK,IAAI,0BAAS;AAAA,MACtB,QAAQ;AAAA,MACR,UAAU,QAAQ;AAAA,MAClB,aAAa;AAAA,QACZ,aAAa,QAAQ;AAAA,QACrB,iBAAiB,QAAQ;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,MAAM,SAAiB,YAAY,QAA8B;AAC7E,UAAM,MAAM,OAAG,+BAAW,CAAC,IAAI,SAAS;AAExC,UAAM,KAAK,GAAG;AAAA,MACb,IAAI,kCAAiB;AAAA,QACpB,QAAQ,KAAK;AAAA,QACb,KAAK;AAAA,QACL,MAAM;AAAA,QACN,aAAa,cAAc,SAAS,qBAAqB;AAAA,MAC1D,CAAC;AAAA,IACF;AAEA,WAAO;AAAA,MACN;AAAA,MACA,KAAK,WAAW,KAAK,YAAY,IAAI,GAAG;AAAA,IACzC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,UAAU,UAAoB,YAAY,QAAsD;AAC5G,WAAO,QAAQ,WAAW,SAAS,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AAAA,EAC9E;AACD;","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,54 @@
1
+ // src/index.ts
2
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
3
+ import { randomUUID } from "crypto";
4
+ var SourceEmbed = class {
5
+ s3;
6
+ bucket;
7
+ publicDomain;
8
+ constructor(options) {
9
+ this.bucket = options.bucket;
10
+ this.publicDomain = options.publicDomain;
11
+ this.s3 = new S3Client({
12
+ region: "auto",
13
+ endpoint: options.endpoint,
14
+ credentials: {
15
+ accessKeyId: options.accessKeyId,
16
+ secretAccessKey: options.secretAccessKey
17
+ }
18
+ });
19
+ }
20
+ /**
21
+ * Stores raw text (typically embed JSON) in R2 and returns a public URL.
22
+ * @param rawText - The text content to store
23
+ * @param extension - File extension (default: 'json')
24
+ * @returns The key and public URL of the stored object
25
+ */
26
+ async store(rawText, extension = "json") {
27
+ const key = `${randomUUID()}.${extension}`;
28
+ await this.s3.send(
29
+ new PutObjectCommand({
30
+ Bucket: this.bucket,
31
+ Key: key,
32
+ Body: rawText,
33
+ ContentType: extension === "json" ? "application/json" : "text/plain"
34
+ })
35
+ );
36
+ return {
37
+ key,
38
+ url: `https://${this.publicDomain}/${key}`
39
+ };
40
+ }
41
+ /**
42
+ * Stores multiple raw texts in parallel.
43
+ * @param rawTexts - Array of text content to store
44
+ * @param extension - File extension (default: 'json')
45
+ * @returns Array of settled results (fulfilled with StoreResult or rejected with error)
46
+ */
47
+ async storeMany(rawTexts, extension = "json") {
48
+ return Promise.allSettled(rawTexts.map((text) => this.store(text, extension)));
49
+ }
50
+ };
51
+ export {
52
+ SourceEmbed
53
+ };
54
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';\r\nimport { randomUUID } from 'node:crypto';\r\n\r\nexport interface SourceEmbedOptions {\r\n\t/** Cloudflare R2 access key ID */\r\n\taccessKeyId: string;\r\n\t/** Cloudflare R2 secret access key */\r\n\tsecretAccessKey: string;\r\n\t/** Cloudflare R2 S3-compatible endpoint URL */\r\n\tendpoint: string;\r\n\t/** R2 bucket name */\r\n\tbucket: string;\r\n\t/** Public domain for serving files (without protocol) */\r\n\tpublicDomain: string;\r\n}\r\n\r\nexport interface StoreResult {\r\n\t/** The unique key/filename of the stored object */\r\n\tkey: string;\r\n\t/** The full public URL to access the stored content */\r\n\turl: string;\r\n}\r\n\r\nexport class SourceEmbed {\r\n\tprivate readonly s3: S3Client;\r\n\tprivate readonly bucket: string;\r\n\tprivate readonly publicDomain: string;\r\n\r\n\tpublic constructor(options: SourceEmbedOptions) {\r\n\t\tthis.bucket = options.bucket;\r\n\t\tthis.publicDomain = options.publicDomain;\r\n\r\n\t\tthis.s3 = new S3Client({\r\n\t\t\tregion: 'auto',\r\n\t\t\tendpoint: options.endpoint,\r\n\t\t\tcredentials: {\r\n\t\t\t\taccessKeyId: options.accessKeyId,\r\n\t\t\t\tsecretAccessKey: options.secretAccessKey,\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Stores raw text (typically embed JSON) in R2 and returns a public URL.\r\n\t * @param rawText - The text content to store\r\n\t * @param extension - File extension (default: 'json')\r\n\t * @returns The key and public URL of the stored object\r\n\t */\r\n\tpublic async store(rawText: string, extension = 'json'): Promise<StoreResult> {\r\n\t\tconst key = `${randomUUID()}.${extension}`;\r\n\r\n\t\tawait this.s3.send(\r\n\t\t\tnew PutObjectCommand({\r\n\t\t\t\tBucket: this.bucket,\r\n\t\t\t\tKey: key,\r\n\t\t\t\tBody: rawText,\r\n\t\t\t\tContentType: extension === 'json' ? 'application/json' : 'text/plain',\r\n\t\t\t})\r\n\t\t);\r\n\r\n\t\treturn {\r\n\t\t\tkey,\r\n\t\t\turl: `https://${this.publicDomain}/${key}`,\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * Stores multiple raw texts in parallel.\r\n\t * @param rawTexts - Array of text content to store\r\n\t * @param extension - File extension (default: 'json')\r\n\t * @returns Array of settled results (fulfilled with StoreResult or rejected with error)\r\n\t */\r\n\tpublic async storeMany(rawTexts: string[], extension = 'json'): Promise<PromiseSettledResult<StoreResult>[]> {\r\n\t\treturn Promise.allSettled(rawTexts.map((text) => this.store(text, extension)));\r\n\t}\r\n}\r\n"],"mappings":";AAAA,SAAS,kBAAkB,gBAAgB;AAC3C,SAAS,kBAAkB;AAsBpB,IAAM,cAAN,MAAkB;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,SAA6B;AAC/C,SAAK,SAAS,QAAQ;AACtB,SAAK,eAAe,QAAQ;AAE5B,SAAK,KAAK,IAAI,SAAS;AAAA,MACtB,QAAQ;AAAA,MACR,UAAU,QAAQ;AAAA,MAClB,aAAa;AAAA,QACZ,aAAa,QAAQ;AAAA,QACrB,iBAAiB,QAAQ;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,MAAM,SAAiB,YAAY,QAA8B;AAC7E,UAAM,MAAM,GAAG,WAAW,CAAC,IAAI,SAAS;AAExC,UAAM,KAAK,GAAG;AAAA,MACb,IAAI,iBAAiB;AAAA,QACpB,QAAQ,KAAK;AAAA,QACb,KAAK;AAAA,QACL,MAAM;AAAA,QACN,aAAa,cAAc,SAAS,qBAAqB;AAAA,MAC1D,CAAC;AAAA,IACF;AAEA,WAAO;AAAA,MACN;AAAA,MACA,KAAK,WAAW,KAAK,YAAY,IAAI,GAAG;AAAA,IACzC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,UAAU,UAAoB,YAAY,QAAsD;AAC5G,WAAO,QAAQ,WAAW,SAAS,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AAAA,EAC9E;AACD;","names":[]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@letsroti/sourceembed",
3
+ "version": "1.0.0",
4
+ "description": "Store embed JSON to Cloudflare R2 and return a public URL",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "lint": "eslint src --fix",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "discord",
32
+ "embed",
33
+ "sourceembed",
34
+ "cloudflare",
35
+ "r2"
36
+ ],
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "@aws-sdk/client-s3": "^3.700.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.4.0",
43
+ "tsup": "^8.0.0",
44
+ "typescript": "^5.5.0"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }