@ticketpm/core 0.0.4

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.
Files changed (48) hide show
  1. package/LICENSE +372 -0
  2. package/README.md +364 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/canonical.d.ts +26 -0
  5. package/dist/canonical.d.ts.map +1 -0
  6. package/dist/canonical.js +51 -0
  7. package/dist/canonical.js.map +1 -0
  8. package/dist/compact.d.ts +24 -0
  9. package/dist/compact.d.ts.map +1 -0
  10. package/dist/compact.js +164 -0
  11. package/dist/compact.js.map +1 -0
  12. package/dist/constants.d.ts +11 -0
  13. package/dist/constants.d.ts.map +1 -0
  14. package/dist/constants.js +11 -0
  15. package/dist/constants.js.map +1 -0
  16. package/dist/identity.d.ts +24 -0
  17. package/dist/identity.d.ts.map +1 -0
  18. package/dist/identity.js +35 -0
  19. package/dist/identity.js.map +1 -0
  20. package/dist/index.d.ts +10 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +10 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/media-proxy.d.ts +100 -0
  25. package/dist/media-proxy.d.ts.map +1 -0
  26. package/dist/media-proxy.js +318 -0
  27. package/dist/media-proxy.js.map +1 -0
  28. package/dist/runtime.d.ts +12 -0
  29. package/dist/runtime.d.ts.map +1 -0
  30. package/dist/runtime.js +48 -0
  31. package/dist/runtime.js.map +1 -0
  32. package/dist/types.d.ts +509 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +94 -0
  35. package/dist/types.js.map +1 -0
  36. package/dist/upload-client.d.ts +99 -0
  37. package/dist/upload-client.d.ts.map +1 -0
  38. package/dist/upload-client.js +115 -0
  39. package/dist/upload-client.js.map +1 -0
  40. package/dist/utils.d.ts +7 -0
  41. package/dist/utils.d.ts.map +1 -0
  42. package/dist/utils.js +35 -0
  43. package/dist/utils.js.map +1 -0
  44. package/dist/validation.d.ts +34 -0
  45. package/dist/validation.d.ts.map +1 -0
  46. package/dist/validation.js +425 -0
  47. package/dist/validation.js.map +1 -0
  48. package/package.json +32 -0
@@ -0,0 +1,318 @@
1
+ import { isRecord, joinUrl, readTrimmedString } from "./utils.js";
2
+ function buildUploadHeaders(options) {
3
+ const headers = new Headers({ "Content-Type": options.contentType });
4
+ if (options.token) {
5
+ headers.set("Authorization", options.token.startsWith("Bearer ") ? options.token : `Bearer ${options.token}`);
6
+ }
7
+ return headers;
8
+ }
9
+ function isValidAvatarHash(avatar) {
10
+ if (typeof avatar !== "string") {
11
+ return false;
12
+ }
13
+ const normalized = avatar.trim();
14
+ return normalized.length > 0 && /^[a-zA-Z0-9_]+$/.test(normalized);
15
+ }
16
+ function buildAnimatedAssetUrl(baseUrl, kind, hash, animated) {
17
+ const url = new URL(joinUrl(baseUrl, `/${kind}/${hash}`));
18
+ if (animated) {
19
+ url.searchParams.set("animated", "true");
20
+ }
21
+ return url.toString();
22
+ }
23
+ function isLikelyMediaObject(record) {
24
+ return (readTrimmedString(record, "proxy_url") !== undefined ||
25
+ readTrimmedString(record, "content_type") !== undefined ||
26
+ readTrimmedString(record, "filename") !== undefined ||
27
+ typeof record.width === "number" ||
28
+ typeof record.height === "number" ||
29
+ typeof record.size === "number");
30
+ }
31
+ /**
32
+ * Stateful client with in-memory dedupe so repeated avatar/icon/media uploads
33
+ * inside a single export run do not fan out into redundant requests.
34
+ */
35
+ export class TicketPmMediaProxyClient {
36
+ options;
37
+ fetchImpl;
38
+ avatarUploadCache = new Map();
39
+ iconUploadCache = new Map();
40
+ attachmentUploadCache = new Map();
41
+ constructor(options) {
42
+ this.options = options;
43
+ this.fetchImpl = options.fetch ?? fetch;
44
+ }
45
+ get baseUrl() {
46
+ return this.options.baseUrl;
47
+ }
48
+ /**
49
+ * Upload a Discord avatar hash to the configured media proxy.
50
+ *
51
+ * On success, the returned value is the proxy avatar URL. On failure, this
52
+ * returns `undefined` and the caller is expected to keep the original avatar
53
+ * hash untouched in transcript data.
54
+ */
55
+ async uploadAvatarHash(hash, userId) {
56
+ if (!isValidAvatarHash(hash)) {
57
+ return undefined;
58
+ }
59
+ const normalizedHash = hash.trim();
60
+ const cached = this.avatarUploadCache.get(normalizedHash);
61
+ if (cached) {
62
+ return cached;
63
+ }
64
+ const request = (async () => {
65
+ const response = await this.fetchImpl(joinUrl(this.options.baseUrl, "/avatars/upload"), {
66
+ method: "POST",
67
+ headers: buildUploadHeaders({
68
+ contentType: "application/json",
69
+ token: this.options.token
70
+ }),
71
+ body: JSON.stringify({ hash: normalizedHash, id: userId })
72
+ });
73
+ if (!response.ok) {
74
+ return undefined;
75
+ }
76
+ const payload = (await response.json());
77
+ if (typeof payload.hash !== "string" || payload.hash.length === 0) {
78
+ return undefined;
79
+ }
80
+ return buildAnimatedAssetUrl(this.options.baseUrl, "avatars", payload.hash, normalizedHash.startsWith("a_"));
81
+ })();
82
+ this.avatarUploadCache.set(normalizedHash, request);
83
+ request.catch(() => {
84
+ this.avatarUploadCache.delete(normalizedHash);
85
+ });
86
+ return request;
87
+ }
88
+ async uploadGuildIconHash(hash, guildId) {
89
+ if (!isValidAvatarHash(hash)) {
90
+ return undefined;
91
+ }
92
+ const normalizedHash = hash.trim();
93
+ const cached = this.iconUploadCache.get(normalizedHash);
94
+ if (cached) {
95
+ return cached;
96
+ }
97
+ const request = (async () => {
98
+ const response = await this.fetchImpl(joinUrl(this.options.baseUrl, "/icons/upload"), {
99
+ method: "POST",
100
+ headers: buildUploadHeaders({
101
+ contentType: "application/json",
102
+ token: this.options.token
103
+ }),
104
+ body: JSON.stringify({ hash: normalizedHash, id: guildId })
105
+ });
106
+ if (!response.ok) {
107
+ return undefined;
108
+ }
109
+ const payload = (await response.json());
110
+ if (typeof payload.hash !== "string" || payload.hash.length === 0) {
111
+ return undefined;
112
+ }
113
+ return buildAnimatedAssetUrl(this.options.baseUrl, "icons", payload.hash, normalizedHash.startsWith("a_"));
114
+ })();
115
+ this.iconUploadCache.set(normalizedHash, request);
116
+ request.catch(() => {
117
+ this.iconUploadCache.delete(normalizedHash);
118
+ });
119
+ return request;
120
+ }
121
+ /**
122
+ * Upload a media URL to the configured proxy.
123
+ *
124
+ * If the proxy is unavailable, returns a non-2xx response, or produces an
125
+ * invalid payload, this returns `undefined`. Callers should treat that as a
126
+ * no-rewrite outcome and keep the original media URL fields.
127
+ */
128
+ async uploadAttachmentUrl(url) {
129
+ const cached = this.attachmentUploadCache.get(url);
130
+ if (cached) {
131
+ return cached;
132
+ }
133
+ const request = (async () => {
134
+ const response = await this.fetchImpl(joinUrl(this.options.baseUrl, "/attachments/upload"), {
135
+ method: "POST",
136
+ headers: buildUploadHeaders({
137
+ contentType: "application/json",
138
+ token: this.options.token
139
+ }),
140
+ body: JSON.stringify({ url })
141
+ });
142
+ if (!response.ok) {
143
+ return undefined;
144
+ }
145
+ const payload = (await response.json());
146
+ return typeof payload.hash === "string" && payload.hash.length > 0
147
+ ? joinUrl(this.options.baseUrl, `/attachments/${payload.hash}`)
148
+ : undefined;
149
+ })();
150
+ this.attachmentUploadCache.set(url, request);
151
+ request.catch(() => {
152
+ this.attachmentUploadCache.delete(url);
153
+ });
154
+ return request;
155
+ }
156
+ }
157
+ /**
158
+ * Walk a transcript recursively and collect the media URLs that would be sent
159
+ * to the proxy service.
160
+ */
161
+ export function collectTranscriptMediaUrls(messages) {
162
+ const urls = new Set();
163
+ const visited = new WeakSet();
164
+ const maybeAdd = (value) => {
165
+ if (!value) {
166
+ return;
167
+ }
168
+ try {
169
+ new URL(value);
170
+ urls.add(value);
171
+ }
172
+ catch {
173
+ // Skip invalid URLs so callers can still use the set size as progress.
174
+ }
175
+ };
176
+ const visit = (value) => {
177
+ if (Array.isArray(value)) {
178
+ for (const item of value) {
179
+ visit(item);
180
+ }
181
+ return;
182
+ }
183
+ if (!isRecord(value) || visited.has(value)) {
184
+ return;
185
+ }
186
+ visited.add(value);
187
+ const mediaSourceUrl = readTrimmedString(value, "proxy_url") ?? readTrimmedString(value, "url");
188
+ if (mediaSourceUrl && isLikelyMediaObject(value)) {
189
+ maybeAdd(mediaSourceUrl);
190
+ }
191
+ const iconSourceUrl = readTrimmedString(value, "proxy_icon_url") ?? readTrimmedString(value, "icon_url");
192
+ if (iconSourceUrl) {
193
+ maybeAdd(iconSourceUrl);
194
+ }
195
+ for (const nestedValue of Object.values(value)) {
196
+ visit(nestedValue);
197
+ }
198
+ };
199
+ for (const message of messages) {
200
+ visit(message);
201
+ }
202
+ return urls;
203
+ }
204
+ /**
205
+ * Upload all avatar hashes already present in `context.users` so the media API
206
+ * can cache them, but keep `user.avatar` as the original Discord hash.
207
+ *
208
+ * The viewer still expects `user.avatar` to be a raw Discord avatar hash and
209
+ * derives the final CDN URL from `user.id + user.avatar`. Replacing the field
210
+ * with a proxy URL would make the serialized transcript incompatible with the
211
+ * existing viewer contract.
212
+ */
213
+ export async function proxyTranscriptAvatarsInPlace(users, client, options) {
214
+ const uploads = Object.values(users)
215
+ .filter((user) => isValidAvatarHash(user.avatar))
216
+ .map((user) => ({ hash: user.avatar.trim(), userId: user.id }));
217
+ const total = uploads.length;
218
+ let completed = 0;
219
+ options?.onProgress?.(completed, total);
220
+ for (const upload of uploads) {
221
+ await client.uploadAvatarHash(upload.hash, upload.userId);
222
+ completed += 1;
223
+ options?.onProgress?.(completed, total);
224
+ }
225
+ }
226
+ /**
227
+ * Proxy the guild icon if one exists and mutate the guild object in place.
228
+ */
229
+ export async function proxyGuildIconInPlace(guild, client) {
230
+ if (!isValidAvatarHash(guild.icon)) {
231
+ return undefined;
232
+ }
233
+ const proxied = await client.uploadGuildIconHash(guild.icon.trim(), guild.id);
234
+ if (proxied) {
235
+ guild.proxy_icon_url = proxied;
236
+ }
237
+ return proxied;
238
+ }
239
+ function isAlreadyRewrittenAttachmentUrl(baseUrl, url) {
240
+ return url.startsWith(joinUrl(baseUrl, "/attachments/"));
241
+ }
242
+ async function rewriteMediaUrlFields(record, client) {
243
+ const mediaSourceUrl = readTrimmedString(record, "proxy_url") ?? readTrimmedString(record, "url");
244
+ if (mediaSourceUrl && isLikelyMediaObject(record) && !isAlreadyRewrittenAttachmentUrl(client.baseUrl, mediaSourceUrl)) {
245
+ const proxiedUrl = await client.uploadAttachmentUrl(mediaSourceUrl);
246
+ if (proxiedUrl) {
247
+ record.proxy_url = proxiedUrl;
248
+ }
249
+ }
250
+ const iconSourceUrl = readTrimmedString(record, "proxy_icon_url") ?? readTrimmedString(record, "icon_url");
251
+ if (iconSourceUrl && !isAlreadyRewrittenAttachmentUrl(client.baseUrl, iconSourceUrl)) {
252
+ const proxiedIconUrl = await client.uploadAttachmentUrl(iconSourceUrl);
253
+ if (proxiedIconUrl) {
254
+ record.proxy_icon_url = proxiedIconUrl;
255
+ }
256
+ }
257
+ }
258
+ /**
259
+ * Rewrite nested media-bearing fields to prefer the ticket.pm proxy service.
260
+ *
261
+ * Failure behavior:
262
+ *
263
+ * - successful proxy uploads write `proxy_url` or `proxy_icon_url`
264
+ * - failed proxy uploads leave existing fields unchanged
265
+ * - original Discord-hosted media URLs remain available as fallback
266
+ */
267
+ export async function rewriteTranscriptMediaUrlsInPlace(messages, client, options) {
268
+ const visited = new WeakSet();
269
+ const expectedUrls = options?.expectedUrls ?? collectTranscriptMediaUrls(messages);
270
+ const total = expectedUrls.size;
271
+ let completed = 0;
272
+ options?.onProgress?.(completed, total);
273
+ const visit = async (value) => {
274
+ if (Array.isArray(value)) {
275
+ for (const item of value) {
276
+ await visit(item);
277
+ }
278
+ return;
279
+ }
280
+ if (!isRecord(value) || visited.has(value)) {
281
+ return;
282
+ }
283
+ visited.add(value);
284
+ const mediaSourceUrl = readTrimmedString(value, "proxy_url") ?? readTrimmedString(value, "url");
285
+ if (mediaSourceUrl && expectedUrls.delete(mediaSourceUrl)) {
286
+ completed += 1;
287
+ options?.onProgress?.(completed, total);
288
+ }
289
+ await rewriteMediaUrlFields(value, client);
290
+ for (const nestedValue of Object.values(value)) {
291
+ await visit(nestedValue);
292
+ }
293
+ };
294
+ for (const message of messages) {
295
+ await visit(message);
296
+ }
297
+ }
298
+ /**
299
+ * Convenience wrapper for callers that keep the whole transcript structure
300
+ * together while proxying assets.
301
+ *
302
+ * This helper is best-effort. Media proxy failures do not throw away the
303
+ * original transcript media fields; they simply leave them as they were.
304
+ */
305
+ export async function proxyTranscriptAssetsInPlace(transcript, client, options) {
306
+ if (transcript.context.users) {
307
+ await proxyTranscriptAvatarsInPlace(transcript.context.users, client, {
308
+ onProgress: options?.avatarProgress
309
+ });
310
+ }
311
+ if (transcript.context.guild) {
312
+ await proxyGuildIconInPlace(transcript.context.guild, client);
313
+ }
314
+ await rewriteTranscriptMediaUrlsInPlace(transcript.messages, client, {
315
+ onProgress: options?.mediaProgress
316
+ });
317
+ }
318
+ //# sourceMappingURL=media-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-proxy.js","sourceRoot":"","sources":["../src/media-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ type ZstdCompressOptions = {
2
+ level?: number;
3
+ runtime?: "auto" | "bun" | "node";
4
+ };
5
+ type BunLike = {
6
+ zstdCompress(bytes: Uint8Array, options?: ZstdCompressOptions): Uint8Array | ArrayBuffer | Promise<Uint8Array | ArrayBuffer>;
7
+ };
8
+ export declare function getBunRuntime(): BunLike | undefined;
9
+ export declare function isRunningOnBun(): boolean;
10
+ export declare function compressWithRuntimeZstd(bytes: Uint8Array, options?: ZstdCompressOptions): Promise<Uint8Array>;
11
+ export {};
12
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA,KAAK,mBAAmB,GAAG;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CAClC,CAAC;AAEF,KAAK,OAAO,GAAG;IACd,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,WAAW,CAAC,CAAC;CAC7H,CAAC;AAMF,wBAAgB,aAAa,IAAI,OAAO,GAAG,SAAS,CAYnD;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAyBD,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAenH"}
@@ -0,0 +1,48 @@
1
+ import { promisify } from "node:util";
2
+ import { toUint8Array } from "./utils.js";
3
+ let nodeZstdCompressPromise;
4
+ export function getBunRuntime() {
5
+ const maybeBun = globalThis.Bun;
6
+ if (typeof maybeBun === "object" &&
7
+ maybeBun !== null &&
8
+ "zstdCompress" in maybeBun &&
9
+ typeof maybeBun.zstdCompress === "function") {
10
+ return maybeBun;
11
+ }
12
+ return undefined;
13
+ }
14
+ export function isRunningOnBun() {
15
+ return getBunRuntime() !== undefined;
16
+ }
17
+ async function getNodeZstdCompress() {
18
+ if (!nodeZstdCompressPromise) {
19
+ nodeZstdCompressPromise = (async () => {
20
+ const { zstdCompress } = await import("node:zlib");
21
+ // Polyfill node versions without zstd
22
+ if (typeof zstdCompress !== "function") {
23
+ throw new Error("Node.js runtime does not expose zstd compression. Use Bun or a Node.js version with node:zlib zstd support.");
24
+ }
25
+ const zstdCompressAsync = promisify(zstdCompress);
26
+ return async (bytes, options) => {
27
+ const compressed = await zstdCompressAsync(bytes, options);
28
+ return toUint8Array(compressed);
29
+ };
30
+ })();
31
+ }
32
+ return nodeZstdCompressPromise;
33
+ }
34
+ // Bun is much faster for this native zstd path, otherwise fallback to standard Node zlib
35
+ export async function compressWithRuntimeZstd(bytes, options) {
36
+ const runtime = options?.runtime ?? "auto";
37
+ const bun = getBunRuntime();
38
+ if (runtime !== "node" && bun) {
39
+ const compressed = await bun.zstdCompress(bytes, options);
40
+ return toUint8Array(compressed);
41
+ }
42
+ if (runtime === "bun") {
43
+ throw new Error("Bun runtime was requested for ZSTD compression, but Bun is not available.");
44
+ }
45
+ const nodeZstdCompress = await getNodeZstdCompress();
46
+ return nodeZstdCompress(bytes, options);
47
+ }
48
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAa1C,IAAI,uBAA8D,CAAC;AAEnE,MAAM,UAAU,aAAa;IAC5B,MAAM,QAAQ,GAAI,UAAgC,CAAC,GAAG,CAAC;IACvD,IACC,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,IAAI;QACjB,cAAc,IAAI,QAAQ;QAC1B,OAAO,QAAQ,CAAC,YAAY,KAAK,UAAU,EAC1C,CAAC;QACF,OAAO,QAAmB,CAAC;IAC5B,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc;IAC7B,OAAO,aAAa,EAAE,KAAK,SAAS,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,mBAAmB;IACjC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC9B,uBAAuB,GAAG,CAAC,KAAK,IAAI,EAAE;YACrC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YACnD,sCAAsC;YACtC,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,CAAC;gBACxC,MAAM,IAAI,KAAK,CACd,6GAA6G,CAC7G,CAAC;YACH,CAAC;YACD,MAAM,iBAAiB,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;YAElD,OAAO,KAAK,EAAE,KAAiB,EAAE,OAA6B,EAAuB,EAAE;gBACtF,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,OAAgB,CAAC,CAAC;gBACpE,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;YACjC,CAAC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,uBAAuB,CAAC;AAChC,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAAiB,EAAE,OAA6B;IAC7F,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC;IAC3C,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAE5B,IAAI,OAAO,KAAK,MAAM,IAAI,GAAG,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,mBAAmB,EAAE,CAAC;IACrD,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC"}