@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,99 @@
1
+ import { MAX_TRANSCRIPT_COMPRESSED_BYTES, MAX_TRANSCRIPT_DECOMPRESSED_BYTES } from "./constants.js";
2
+ import { TicketPmMediaProxyClient, type TicketPmMediaProxyClientOptions, type UploadProgressCallback } from "./media-proxy.js";
3
+ import type { StoredTranscript, TranscriptBuildInput } from "./types.js";
4
+ export interface TicketPmUploadClientOptions {
5
+ /**
6
+ * Base transcript API URL, for example `https://ticket.pm/v2`.
7
+ */
8
+ baseUrl: string;
9
+ /**
10
+ * Optional bearer token or raw token string. Raw values are normalized into
11
+ * the `Authorization: Bearer ...` header automatically.
12
+ */
13
+ token?: string;
14
+ /**
15
+ * Optional custom fetch implementation for environments that need their own
16
+ * HTTP transport, instrumentation, retries, or authentication pipeline.
17
+ */
18
+ fetch?: typeof fetch;
19
+ /**
20
+ * Base URL used when `uploadDraftTranscript()` auto-creates a media proxy
21
+ * client for you.
22
+ *
23
+ * Defaults to `https://m.ticket.pm/v2`.
24
+ */
25
+ defaultMediaProxyBaseUrl?: string;
26
+ }
27
+ export interface UploadCompressedTranscriptOptions {
28
+ /**
29
+ * Match the current first-party bot behavior of requesting URL-safe random IDs
30
+ * via `?uuid=uuid`.
31
+ */
32
+ uuidStyleIds?: boolean;
33
+ }
34
+ export type UploadDraftTranscriptMediaProxy = false | TicketPmMediaProxyClient | Partial<Pick<TicketPmMediaProxyClientOptions, "baseUrl" | "token" | "fetch">>;
35
+ export interface UploadDraftTranscriptOptions extends UploadCompressedTranscriptOptions {
36
+ /**
37
+ * Optional ZSTD compression level forwarded to `compressStoredTranscript()`.
38
+ */
39
+ level?: number;
40
+ /**
41
+ * Media proxy configuration for draft uploads.
42
+ *
43
+ * - omit this value to auto-create a proxy client with the uploader token and
44
+ * `https://m.ticket.pm/v2`
45
+ * - pass an existing `TicketPmMediaProxyClient` to fully control media proxy
46
+ * behavior
47
+ * - pass a partial options object to override only `baseUrl`, `token`, and/or
48
+ * `fetch`
49
+ * - pass `false` to disable media proxying entirely for this upload
50
+ */
51
+ mediaProxy?: UploadDraftTranscriptMediaProxy;
52
+ /**
53
+ * Optional progress callback for avatar cache uploads performed before the
54
+ * transcript is built.
55
+ */
56
+ avatarProgress?: UploadProgressCallback;
57
+ /**
58
+ * Optional progress callback for attachment/embed/icon proxy rewrites
59
+ * performed before the transcript is built.
60
+ */
61
+ mediaProgress?: UploadProgressCallback;
62
+ }
63
+ export interface TicketPmUploadResult {
64
+ id: string;
65
+ rateLimitRemaining?: number;
66
+ rateLimitReset?: number;
67
+ }
68
+ /**
69
+ * Minimal upload client for `POST /v2/upload`.
70
+ */
71
+ export declare class TicketPmUploadClient {
72
+ private readonly options;
73
+ private readonly fetchImpl;
74
+ constructor(options: TicketPmUploadClientOptions);
75
+ uploadCompressedTranscript(compressed: Uint8Array, options?: UploadCompressedTranscriptOptions): Promise<TicketPmUploadResult>;
76
+ uploadTranscript(transcript: StoredTranscript, options?: UploadCompressedTranscriptOptions & {
77
+ level?: number;
78
+ }): Promise<TicketPmUploadResult>;
79
+ /**
80
+ * High-level draft upload helper.
81
+ *
82
+ * This clones the draft transcript, proxies assets when enabled, compacts the
83
+ * draft into the stored transcript contract, compresses it, and uploads it.
84
+ *
85
+ * When `options.mediaProxy` is omitted, the client auto-creates a
86
+ * `TicketPmMediaProxyClient` using:
87
+ *
88
+ * - `baseUrl`: `options.defaultMediaProxyBaseUrl ?? https://m.ticket.pm/v2`
89
+ * - `token`: the uploader token
90
+ * - `fetch`: the uploader fetch implementation
91
+ *
92
+ * Pass `mediaProxy: false` to skip proxying and upload the original media
93
+ * fields unchanged.
94
+ */
95
+ uploadDraftTranscript(draftTranscript: TranscriptBuildInput, options?: UploadDraftTranscriptOptions): Promise<TicketPmUploadResult>;
96
+ private resolveMediaProxyClient;
97
+ }
98
+ export { MAX_TRANSCRIPT_COMPRESSED_BYTES, MAX_TRANSCRIPT_DECOMPRESSED_BYTES };
99
+ //# sourceMappingURL=upload-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-client.d.ts","sourceRoot":"","sources":["../src/upload-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAEN,+BAA+B,EAC/B,iCAAiC,EACjC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEN,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,sBAAsB,EAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGzE,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iCAAiC;IACjD;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,+BAA+B,GACxC,KAAK,GACL,wBAAwB,GACxB,OAAO,CAAC,IAAI,CAAC,+BAA+B,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;AAEjF,MAAM,WAAW,4BAA6B,SAAQ,iCAAiC;IACtF;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,+BAA+B,CAAC;IAC7C;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC;;;OAGG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAUD;;GAEG;AACH,qBAAa,oBAAoB;IAGb,OAAO,CAAC,QAAQ,CAAC,OAAO;IAF3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEL,OAAO,EAAE,2BAA2B;IAI3D,0BAA0B,CACtC,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,iCAAiC,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAgCnB,gBAAgB,CAC5B,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iCAAiC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9D,OAAO,CAAC,oBAAoB,CAAC;IAOhC;;;;;;;;;;;;;;;OAeG;IACU,qBAAqB,CACjC,eAAe,EAAE,oBAAoB,EACrC,OAAO,CAAC,EAAE,4BAA4B,GACpC,OAAO,CAAC,oBAAoB,CAAC;IAehC,OAAO,CAAC,uBAAuB;CAe/B;AAuBD,OAAO,EAAE,+BAA+B,EAAE,iCAAiC,EAAE,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { compressStoredTranscript } from "./canonical.js";
2
+ import { buildStoredTranscript } from "./compact.js";
3
+ import { DEFAULT_TICKETPM_MEDIA_PROXY_BASE_URL, MAX_TRANSCRIPT_COMPRESSED_BYTES, MAX_TRANSCRIPT_DECOMPRESSED_BYTES } from "./constants.js";
4
+ import { proxyTranscriptAssetsInPlace, TicketPmMediaProxyClient } from "./media-proxy.js";
5
+ import { joinUrl } from "./utils.js";
6
+ function buildUploadHeaders(token) {
7
+ const headers = new Headers({ "Content-Type": "application/octet-stream" });
8
+ if (token) {
9
+ headers.set("Authorization", token.startsWith("Bearer ") ? token : `Bearer ${token}`);
10
+ }
11
+ return headers;
12
+ }
13
+ /**
14
+ * Minimal upload client for `POST /v2/upload`.
15
+ */
16
+ export class TicketPmUploadClient {
17
+ options;
18
+ fetchImpl;
19
+ constructor(options) {
20
+ this.options = options;
21
+ this.fetchImpl = options.fetch ?? fetch;
22
+ }
23
+ async uploadCompressedTranscript(compressed, options) {
24
+ if (compressed.byteLength > MAX_TRANSCRIPT_COMPRESSED_BYTES) {
25
+ throw new Error(`Compressed transcript exceeds ${MAX_TRANSCRIPT_COMPRESSED_BYTES} bytes.`);
26
+ }
27
+ const uploadUrl = new URL(joinUrl(this.options.baseUrl, "/upload"));
28
+ if (options?.uuidStyleIds !== false) {
29
+ uploadUrl.searchParams.set("uuid", "uuid");
30
+ }
31
+ const response = await this.fetchImpl(uploadUrl, {
32
+ method: "POST",
33
+ headers: buildUploadHeaders(this.options.token),
34
+ body: Buffer.from(compressed)
35
+ });
36
+ if (!response.ok) {
37
+ throw new Error(await response.text());
38
+ }
39
+ const payload = (await response.json());
40
+ if (typeof payload.id !== "string" || payload.id.length === 0) {
41
+ throw new Error("ticket.pm upload completed without returning a transcript id.");
42
+ }
43
+ return {
44
+ id: payload.id,
45
+ rateLimitRemaining: readNumericHeader(response.headers, "X-RateLimit-Remaining"),
46
+ rateLimitReset: readNumericHeader(response.headers, "X-RateLimit-Reset")
47
+ };
48
+ }
49
+ async uploadTranscript(transcript, options) {
50
+ const compressed = await compressStoredTranscript(transcript, {
51
+ level: options?.level
52
+ });
53
+ return this.uploadCompressedTranscript(compressed, options);
54
+ }
55
+ /**
56
+ * High-level draft upload helper.
57
+ *
58
+ * This clones the draft transcript, proxies assets when enabled, compacts the
59
+ * draft into the stored transcript contract, compresses it, and uploads it.
60
+ *
61
+ * When `options.mediaProxy` is omitted, the client auto-creates a
62
+ * `TicketPmMediaProxyClient` using:
63
+ *
64
+ * - `baseUrl`: `options.defaultMediaProxyBaseUrl ?? https://m.ticket.pm/v2`
65
+ * - `token`: the uploader token
66
+ * - `fetch`: the uploader fetch implementation
67
+ *
68
+ * Pass `mediaProxy: false` to skip proxying and upload the original media
69
+ * fields unchanged.
70
+ */
71
+ async uploadDraftTranscript(draftTranscript, options) {
72
+ const workingDraft = cloneTranscriptBuildInput(draftTranscript);
73
+ const mediaProxyClient = this.resolveMediaProxyClient(options?.mediaProxy);
74
+ if (mediaProxyClient) {
75
+ await proxyTranscriptAssetsInPlace(workingDraft, mediaProxyClient, {
76
+ avatarProgress: options?.avatarProgress,
77
+ mediaProgress: options?.mediaProgress
78
+ });
79
+ }
80
+ const transcript = buildStoredTranscript(workingDraft);
81
+ return this.uploadTranscript(transcript, options);
82
+ }
83
+ resolveMediaProxyClient(mediaProxy) {
84
+ if (mediaProxy === false) {
85
+ return undefined;
86
+ }
87
+ if (mediaProxy instanceof TicketPmMediaProxyClient) {
88
+ return mediaProxy;
89
+ }
90
+ return new TicketPmMediaProxyClient({
91
+ baseUrl: mediaProxy?.baseUrl ?? this.options.defaultMediaProxyBaseUrl ?? DEFAULT_TICKETPM_MEDIA_PROXY_BASE_URL,
92
+ token: mediaProxy?.token ?? this.options.token,
93
+ fetch: mediaProxy?.fetch ?? this.fetchImpl
94
+ });
95
+ }
96
+ }
97
+ function cloneTranscriptBuildInput(draftTranscript) {
98
+ if (typeof structuredClone === "function") {
99
+ return structuredClone(draftTranscript);
100
+ }
101
+ return {
102
+ context: JSON.parse(JSON.stringify(draftTranscript.context)),
103
+ messages: JSON.parse(JSON.stringify(draftTranscript.messages))
104
+ };
105
+ }
106
+ function readNumericHeader(headers, key) {
107
+ const value = headers.get(key);
108
+ if (!value) {
109
+ return undefined;
110
+ }
111
+ const parsed = Number(value);
112
+ return Number.isFinite(parsed) ? parsed : undefined;
113
+ }
114
+ export { MAX_TRANSCRIPT_COMPRESSED_BYTES, MAX_TRANSCRIPT_DECOMPRESSED_BYTES };
115
+ //# sourceMappingURL=upload-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-client.js","sourceRoot":"","sources":["../src/upload-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type UnknownRecord = Record<string, unknown>;
2
+ export declare function isRecord(value: unknown): value is UnknownRecord;
3
+ export declare function readTrimmedString(record: UnknownRecord, key: string): string | undefined;
4
+ export declare function sortRecordByKey<T>(record: Record<string, T> | undefined): Record<string, T> | undefined;
5
+ export declare function toUint8Array(value: Uint8Array | ArrayBuffer): Uint8Array;
6
+ export declare function joinUrl(baseUrl: string, path: string): string;
7
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAE/D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQxF;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,CAcvG;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAExE;AAGD,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D"}
package/dist/utils.js ADDED
@@ -0,0 +1,35 @@
1
+ export function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ export function readTrimmedString(record, key) {
5
+ const value = record[key];
6
+ if (typeof value !== "string") {
7
+ return undefined;
8
+ }
9
+ const normalized = value.trim();
10
+ return normalized.length > 0 ? normalized : undefined;
11
+ }
12
+ export function sortRecordByKey(record) {
13
+ if (!record) {
14
+ return undefined;
15
+ }
16
+ return Object.keys(record)
17
+ .sort()
18
+ .reduce((accumulator, key) => {
19
+ const value = record[key];
20
+ if (value !== undefined) {
21
+ accumulator[key] = value;
22
+ }
23
+ return accumulator;
24
+ }, {});
25
+ }
26
+ export function toUint8Array(value) {
27
+ return value instanceof Uint8Array ? value : new Uint8Array(value);
28
+ }
29
+ // Ensure the endpoint joining works as intended without duping slashes
30
+ export function joinUrl(baseUrl, path) {
31
+ const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
32
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
33
+ return `${normalizedBaseUrl}${normalizedPath}`;
34
+ }
35
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAqB,EAAE,GAAW;IACnE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,eAAe,CAAI,MAAqC;IACvE,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;SACxB,IAAI,EAAE;SACN,MAAM,CAAoB,CAAC,WAAW,EAAE,GAAG,EAAE,EAAE;QAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,WAAW,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC1B,CAAC;QACD,OAAO,WAAW,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAA+B;IAC3D,OAAO,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,OAAO,CAAC,OAAe,EAAE,IAAY;IACpD,MAAM,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAChE,OAAO,GAAG,iBAAiB,GAAG,cAAc,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { StoredTranscript, UploadValidationResult } from "./types.js";
2
+ type UrlValidationResult = {
3
+ ok: true;
4
+ } | {
5
+ ok: false;
6
+ path: string;
7
+ reason: string;
8
+ url?: string;
9
+ };
10
+ /**
11
+ * Media URLs are tighter than regular links because the viewer renders them
12
+ * directly.
13
+ */
14
+ export declare function isValidMediaUrl(url: string | null | undefined): url is string;
15
+ /**
16
+ * Clickable links can point anywhere as long as the scheme is safe.
17
+ */
18
+ export declare function isValidLinkUrl(url: string | null | undefined): url is string;
19
+ /**
20
+ * Mirror the server-side URL walk so consumers can reject unsafe payloads
21
+ * before they reach `POST /v2/upload`.
22
+ */
23
+ export declare function validateTranscriptUrls(payload: unknown): UrlValidationResult;
24
+ /**
25
+ * Validate the same structural requirements enforced by the upload API itself.
26
+ */
27
+ export declare function validateTicketPmUploadPayload(transcript: unknown): UploadValidationResult;
28
+ /**
29
+ * Upload acceptance is looser than viewer rendering. This helper closes that
30
+ * gap by checking the compact hydration references that the renderer needs.
31
+ */
32
+ export declare function validateViewerCompatibility(transcript: StoredTranscript): UploadValidationResult;
33
+ export {};
34
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAwB,gBAAgB,EAAyB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAMxH,KAAK,mBAAmB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA0BpG;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,IAAI,MAAM,CAY7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,IAAI,MAAM,CAM5E;AAuSD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAwB5E;AAiCD;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,OAAO,GAAG,sBAAsB,CAuBzF;AA4BD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,gBAAgB,GAAG,sBAAsB,CAuDhG"}