@openrails/contentkit-ui 0.83.0 → 0.86.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 +18 -7
- package/dist/client/beacons.d.ts +18 -6
- package/dist/client/beacons.js +26 -11
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/content/moderation.d.ts +14 -2
- package/dist/client/content/moderation.js +34 -0
- package/dist/client/content/moderation.js.map +1 -1
- package/dist/client/content/types.d.ts +13 -0
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -0
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +9 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +91 -13
- package/dist/client/generated/routes.js +123 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +81 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +4 -2
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/reader-meta.d.ts +27 -0
- package/dist/client/reader-meta.js +99 -0
- package/dist/client/reader-meta.js.map +1 -0
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client/versions.d.ts +1 -1
- package/dist/client/versions.js +7 -2
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +30 -28
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/reader-manifest.d.ts +18 -0
- package/dist/components/admin/reader-manifest.js +490 -0
- package/dist/components/admin/reader-manifest.js.map +1 -0
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.js +43 -43
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/editors.d.ts +2 -1
- package/dist/components/content/editors.js +479 -416
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.js +47 -47
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.d.ts +1 -1
- package/dist/components/groups/banner.js +34 -34
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +294 -294
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +2 -4
- package/dist/components/groups/parts.js +69 -88
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/comments.js +59 -45
- package/dist/components/social/comments.js.map +1 -1
- package/dist/components/social/index.d.ts +1 -0
- package/dist/components/social/moderation.d.ts +5 -4
- package/dist/components/social/moderation.js +177 -160
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/social/parts.js +22 -14
- package/dist/components/social/parts.js.map +1 -1
- package/dist/components/social/rules.d.ts +23 -0
- package/dist/components/social/rules.js +261 -0
- package/dist/components/social/rules.js.map +1 -0
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +28 -24
- package/dist/locales/admin/de.js +105 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +105 -0
- package/dist/locales/admin/en.js +105 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +105 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +105 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +105 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +105 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +4 -1
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +1 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +1 -0
- package/dist/locales/discovery/en.js +1 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +1 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +1 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +1 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +1 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +118 -0
- package/dist/locales/en.js +4 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +4 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +4 -1
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +4 -1
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/social/de.js +3 -0
- package/dist/locales/social/de.js.map +1 -1
- package/dist/locales/social/en.d.ts +3 -0
- package/dist/locales/social/en.js +3 -0
- package/dist/locales/social/en.js.map +1 -1
- package/dist/locales/social/es.js +3 -0
- package/dist/locales/social/es.js.map +1 -1
- package/dist/locales/social/ja.js +3 -0
- package/dist/locales/social/ja.js.map +1 -1
- package/dist/locales/social/ko.js +3 -0
- package/dist/locales/social/ko.js.map +1 -1
- package/dist/locales/social/zh.js +3 -0
- package/dist/locales/social/zh.js.map +1 -1
- package/dist/locales/zh.js +4 -1
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.d.ts +1 -1
- package/dist/react/admin.js +24 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/comments.d.ts +29 -1
- package/dist/react/comments.js +30 -2
- package/dist/react/comments.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/discovery.js +1 -1
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +5 -3
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/resources.js +6 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +20 -20
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,19 +3,19 @@ import { fill as t, publicURL as n } from "../public.js";
|
|
|
3
3
|
import { ratio as r } from "../aspect.js";
|
|
4
4
|
import { stem as i } from "./client.js";
|
|
5
5
|
//#region src/client/media/rules.ts
|
|
6
|
-
var a = (e) => e?.uploads ??
|
|
7
|
-
function
|
|
6
|
+
var a = [], o = (e) => e?.uploads ?? a, s = "{name}", c = (e) => e.endsWith(s), l = (e) => c(e) ? e.slice(0, -6) : e;
|
|
7
|
+
function u(e, t) {
|
|
8
8
|
let n = i(t);
|
|
9
|
-
return e.find((e) => !
|
|
10
|
-
if (!
|
|
11
|
-
let t =
|
|
9
|
+
return e.find((e) => !c(e.path) && e.path === n) ?? e.find((e) => {
|
|
10
|
+
if (!c(e.path)) return !1;
|
|
11
|
+
let t = l(e.path);
|
|
12
12
|
return n.startsWith(t) && n.length > t.length && !n.slice(t.length).includes("/");
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
-
function
|
|
16
|
-
return (e?.files ?? []).filter((e) => e.upload && !e.unattached &&
|
|
15
|
+
function d(e, t, n = [t]) {
|
|
16
|
+
return (e?.files ?? []).filter((e) => e.upload && !e.unattached && u(n, e.path)?.path === t.path);
|
|
17
17
|
}
|
|
18
|
-
var
|
|
18
|
+
var f = {
|
|
19
19
|
jpg: "image/jpeg",
|
|
20
20
|
jpeg: "image/jpeg",
|
|
21
21
|
png: "image/png",
|
|
@@ -35,27 +35,27 @@ var d = {
|
|
|
35
35
|
ssa: "text/x-ssa",
|
|
36
36
|
zip: "application/zip"
|
|
37
37
|
};
|
|
38
|
-
function
|
|
38
|
+
function p(e, t) {
|
|
39
39
|
if (e.type && (!t || t.includes(e.type))) return e;
|
|
40
|
-
let n =
|
|
40
|
+
let n = f[e.name.split(".").pop()?.toLowerCase() ?? ""];
|
|
41
41
|
return n && n !== e.type ? new File([e], e.name, {
|
|
42
42
|
type: n,
|
|
43
43
|
lastModified: e.lastModified
|
|
44
44
|
}) : e;
|
|
45
45
|
}
|
|
46
|
-
function
|
|
46
|
+
function m(e) {
|
|
47
47
|
let t = [...new Set(e.flatMap((e) => e.types))];
|
|
48
48
|
if (!t.length) return;
|
|
49
|
-
let n = Object.entries(
|
|
49
|
+
let n = Object.entries(f).filter(([, e]) => t.includes(e)).map(([e]) => `.${e}`);
|
|
50
50
|
return [...t, ...n].join(",");
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function h(t, n, r = () => 0) {
|
|
53
53
|
let i = {
|
|
54
54
|
accepted: [],
|
|
55
55
|
refused: []
|
|
56
56
|
}, a = /* @__PURE__ */ new Map(), o = [...new Set(n.flatMap((e) => e.types))];
|
|
57
57
|
for (let s of t) {
|
|
58
|
-
let t =
|
|
58
|
+
let t = p(s, o), c = n.find((e) => !e.types.length || e.types.includes(t.type));
|
|
59
59
|
if (!c) {
|
|
60
60
|
let n = {
|
|
61
61
|
type: t.type || void 0,
|
|
@@ -104,7 +104,7 @@ function m(t, n, r = () => 0) {
|
|
|
104
104
|
}
|
|
105
105
|
return i;
|
|
106
106
|
}
|
|
107
|
-
function
|
|
107
|
+
function g(e, t) {
|
|
108
108
|
let n = new Set([...t].map(i));
|
|
109
109
|
if (!n.has(i(e))) return e;
|
|
110
110
|
let r = e.lastIndexOf("."), a = r > 0 ? e.slice(0, r) : e, o = r > 0 ? e.slice(r) : "";
|
|
@@ -113,17 +113,23 @@ function h(e, t) {
|
|
|
113
113
|
if (!n.has(i(t))) return t;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
function
|
|
116
|
+
function _(e) {
|
|
117
117
|
let t = (e) => String(Math.round(e * 100) / 100);
|
|
118
118
|
return e >= 1 ? `${t(e)}:1` : `1:${t(1 / e)}`;
|
|
119
119
|
}
|
|
120
|
-
function
|
|
121
|
-
let i = e.filter((e) => e.kind === t &&
|
|
120
|
+
function v(e, t, n, r) {
|
|
121
|
+
let i = e.filter((e) => e.kind === t && u([{ path: e.from }], n));
|
|
122
122
|
return (r ? i.find((e) => e.name === r) : void 0) ?? i.find((e) => e.aspect) ?? i[0];
|
|
123
123
|
}
|
|
124
|
-
function
|
|
124
|
+
function y(e, t) {
|
|
125
|
+
return !e.default || e.first || e.to.includes(s) ? null : x(e, t);
|
|
126
|
+
}
|
|
127
|
+
function b(e, t) {
|
|
128
|
+
return e.stable ? x(e, t) : null;
|
|
129
|
+
}
|
|
130
|
+
function x(e, i) {
|
|
125
131
|
let a = r(e.aspect);
|
|
126
|
-
return
|
|
132
|
+
return {
|
|
127
133
|
preset: e.name,
|
|
128
134
|
aspect: e.aspect,
|
|
129
135
|
renditions: e.widths.map((r) => ({
|
|
@@ -134,6 +140,6 @@ function v(e, i) {
|
|
|
134
140
|
};
|
|
135
141
|
}
|
|
136
142
|
//#endregion
|
|
137
|
-
export {
|
|
143
|
+
export { m as acceptOf, y as defaultImage, d as filesFor, c as isPattern, v as presetFor, _ as ratioLabel, l as ruleDir, u as ruleFor, h as screenFiles, b as stableImage, g as uniqueName, o as uploadRules, p as withMediaType };
|
|
138
144
|
|
|
139
145
|
//# sourceMappingURL=rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","names":[],"sources":["../../../src/client/media/rules.ts"],"sourcesContent":["import { ContentKitError } from \"../errors.js\";\nimport type { ErrorDetails, PresetRule, ReadResult, UploadRule } from \"../generated/wire.js\";\nimport { fill, publicURL, type PublicPreset } from \"../public.js\";\nimport { ratio } from \"../aspect.js\";\nimport { stem } from \"./client.js\";\n\n/** An editor read's upload rules (a viewer read has none). */\nexport const uploadRules = (read: ReadResult | null | undefined): UploadRule[] => read?.uploads ?? [];\n\nconst NAME = \"{name}\";\n\n/** A pattern path (\"originals/{name}\") holds many files; a literal (\"cover\") one. */\nexport const isPattern = (path: string) => path.endsWith(NAME);\n\n/** The directory of a pattern (\"originals/\"), or the literal itself. */\nexport const ruleDir = (path: string) => (isPattern(path) ? path.slice(0, -NAME.length) : path);\n\n/** The rule an upload path (with or without its extension) falls under: a literal wins over a pattern. */\nexport function ruleFor<R extends { path: string }>(rules: readonly R[], path: string): R | undefined {\n const s = stem(path);\n return (\n rules.find((r) => !isPattern(r.path) && r.path === s) ??\n rules.find((r) => {\n if (!isPattern(r.path)) return false;\n const dir = ruleDir(r.path);\n return s.startsWith(dir) && s.length > dir.length && !s.slice(dir.length).includes(\"/\");\n })\n );\n}\n\n/** Uploads of the read under rule (attached, in manifest order). */\nexport function filesFor(read: ReadResult | null | undefined, rule: { path: string }, rules: readonly { path: string }[] = [rule]) {\n return (read?.files ?? []).filter((f) => f.upload && !f.unattached && ruleFor(rules, f.path)?.path === rule.path);\n}\n\nconst BY_EXTENSION: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n avif: \"image/avif\",\n mp4: \"video/mp4\",\n m4v: \"video/mp4\",\n mov: \"video/quicktime\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n m4a: \"audio/mp4\",\n mp3: \"audio/mpeg\",\n vtt: \"text/vtt\",\n srt: \"application/x-subrip\",\n ass: \"text/x-ass\",\n ssa: \"text/x-ssa\",\n zip: \"application/zip\",\n};\n\n/** The file with a content type from its extension when the browser left it empty or nonstandard (.mkv, .mov, .srt). */\nexport function withMediaType(file: File, allowed?: readonly string[]): File {\n if (file.type && (!allowed || allowed.includes(file.type))) return file;\n const type = BY_EXTENSION[file.name.split(\".\").pop()?.toLowerCase() ?? \"\"];\n return type && type !== file.type ? new File([file], file.name, { type, lastModified: file.lastModified }) : file;\n}\n\n/** `accept` for a file input from the rules' types (plus the extensions browsers leave untyped). */\nexport function acceptOf(rules: readonly UploadRule[]): string | undefined {\n const types = [...new Set(rules.flatMap((r) => r.types))];\n if (!types.length) return undefined;\n const exts = Object.entries(BY_EXTENSION).filter(([, t]) => types.includes(t)).map(([e]) => `.${e}`);\n return [...types, ...exts].join(\",\");\n}\n\nexport interface Screened {\n /** Files to upload, each with its rule (typed by extension when needed). */\n accepted: { file: File; rule: UploadRule }[];\n /** Files the server would refuse, with the refusal it would answer. */\n refused: { file: File; error: ContentKitError }[];\n}\n\n/**\n * Checks files against the rules before any upload: a type some rule takes\n * (the first that does), its size, and the rule's file cap counting\n * have(rule) files already there. A rule without types, size or cap lets\n * that check through; the server decides.\n */\nexport function screenFiles(files: Iterable<File>, rules: readonly UploadRule[], have: (rule: UploadRule) => number = () => 0): Screened {\n const out: Screened = { accepted: [], refused: [] };\n const counts = new Map<UploadRule, number>();\n const allowed = [...new Set(rules.flatMap((r) => r.types))];\n for (const picked of files) {\n const file = withMediaType(picked, allowed);\n const rule = rules.find((r) => !r.types.length || r.types.includes(file.type));\n if (!rule) {\n const details: ErrorDetails = { type: file.type || undefined, allowed };\n out.refused.push({ file, error: new ContentKitError(\"type_not_allowed\", `${file.name}: type not allowed`, { status: 415, details }) });\n continue;\n }\n if (rule.max_bytes > 0 && file.size > rule.max_bytes) {\n const details: ErrorDetails = { type: file.type, size: file.size, max_bytes: rule.max_bytes };\n out.refused.push({ file, error: new ContentKitError(\"too_large\", `${file.name}: too large`, { status: 413, details }) });\n continue;\n }\n const n = counts.get(rule) ?? have(rule);\n if (rule.max && n >= rule.max) {\n const details: ErrorDetails = { max: rule.max };\n out.refused.push({ file, error: new ContentKitError(\"too_many_files\", `${file.name}: at most ${rule.max} files`, { status: 409, details }) });\n continue;\n }\n counts.set(rule, n + 1);\n out.accepted.push({ file, rule });\n }\n return out;\n}\n\n/** name, or name-2, name-3… so its stem is not among taken (names or paths without the directory). */\nexport function uniqueName(name: string, taken: Iterable<string>): string {\n const stems = new Set([...taken].map(stem));\n if (!stems.has(stem(name))) return name;\n const dot = name.lastIndexOf(\".\");\n const base = dot > 0 ? name.slice(0, dot) : name;\n const ext = dot > 0 ? name.slice(dot) : \"\";\n for (let n = 2; ; n++) {\n const candidate = `${base}-${n}${ext}`;\n if (!stems.has(stem(candidate))) return candidate;\n }\n}\n\n/** \"16:9\" style label of a width/height ratio: 2.4 → \"2.4:1\", 0.4167 → \"1:2.4\". */\nexport function ratioLabel(r: number): string {\n const round = (v: number) => String(Math.round(v * 100) / 100);\n return r >= 1 ? `${round(r)}:1` : `1:${round(1 / r)}`;\n}\n\n/** The kind's public preset for an upload path: the one named name, else the first with an aspect, else the first. */\nexport function presetFor(presets: readonly PresetRule[], kind: string, path: string, name?: string): PresetRule | undefined {\n const at = presets.filter((p) => p.kind === kind && ruleFor([{ path: p.from }], path));\n return (name ? at.find((p) => p.name === name) : undefined) ?? at.find((p) => p.aspect) ?? at[0];\n}\n\n/**\n * The kind's default image for a preset, at every width: what the gateway\n * serves for an item without its own (a URL from the template never names a\n * current file). null when the preset has no default (`rule.default`).\n */\nexport function defaultImage(rule: PresetRule, id: string): PublicPreset | null {\n const r = ratio(rule.aspect);\n if (!rule.default || rule.first || rule.to.includes(NAME)) return null;\n return {\n preset: rule.name,\n aspect: rule.aspect,\n renditions: rule.widths.map((w) => ({ url: publicURL(rule.base, rule.namespace, rule.kind, id, fill(rule.to, { w })), w, ...(r ? { h: Math.round(w / r) } : {}) })),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rules.js","names":[],"sources":["../../../src/client/media/rules.ts"],"sourcesContent":["import { ContentKitError } from \"../errors.js\";\nimport type { ErrorDetails, PresetRule, ReadResult, UploadRule } from \"../generated/wire.js\";\nimport { fill, publicURL, type PublicPreset } from \"../public.js\";\nimport { ratio } from \"../aspect.js\";\nimport { stem } from \"./client.js\";\n\nconst NO_RULES: UploadRule[] = [];\n\n/** An editor read's upload rules (a viewer read has none): one empty list, so memos keyed on them hold. */\nexport const uploadRules = (read: ReadResult | null | undefined): UploadRule[] => read?.uploads ?? NO_RULES;\n\nconst NAME = \"{name}\";\n\n/** A pattern path (\"originals/{name}\") holds many files; a literal (\"cover\") one. */\nexport const isPattern = (path: string) => path.endsWith(NAME);\n\n/** The directory of a pattern (\"originals/\"), or the literal itself. */\nexport const ruleDir = (path: string) => (isPattern(path) ? path.slice(0, -NAME.length) : path);\n\n/** The rule an upload path (with or without its extension) falls under: a literal wins over a pattern. */\nexport function ruleFor<R extends { path: string }>(rules: readonly R[], path: string): R | undefined {\n const s = stem(path);\n return (\n rules.find((r) => !isPattern(r.path) && r.path === s) ??\n rules.find((r) => {\n if (!isPattern(r.path)) return false;\n const dir = ruleDir(r.path);\n return s.startsWith(dir) && s.length > dir.length && !s.slice(dir.length).includes(\"/\");\n })\n );\n}\n\n/** Uploads of the read under rule (attached, in manifest order). */\nexport function filesFor(read: ReadResult | null | undefined, rule: { path: string }, rules: readonly { path: string }[] = [rule]) {\n return (read?.files ?? []).filter((f) => f.upload && !f.unattached && ruleFor(rules, f.path)?.path === rule.path);\n}\n\nconst BY_EXTENSION: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n avif: \"image/avif\",\n mp4: \"video/mp4\",\n m4v: \"video/mp4\",\n mov: \"video/quicktime\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n m4a: \"audio/mp4\",\n mp3: \"audio/mpeg\",\n vtt: \"text/vtt\",\n srt: \"application/x-subrip\",\n ass: \"text/x-ass\",\n ssa: \"text/x-ssa\",\n zip: \"application/zip\",\n};\n\n/** The file with a content type from its extension when the browser left it empty or nonstandard (.mkv, .mov, .srt). */\nexport function withMediaType(file: File, allowed?: readonly string[]): File {\n if (file.type && (!allowed || allowed.includes(file.type))) return file;\n const type = BY_EXTENSION[file.name.split(\".\").pop()?.toLowerCase() ?? \"\"];\n return type && type !== file.type ? new File([file], file.name, { type, lastModified: file.lastModified }) : file;\n}\n\n/** `accept` for a file input from the rules' types (plus the extensions browsers leave untyped). */\nexport function acceptOf(rules: readonly UploadRule[]): string | undefined {\n const types = [...new Set(rules.flatMap((r) => r.types))];\n if (!types.length) return undefined;\n const exts = Object.entries(BY_EXTENSION).filter(([, t]) => types.includes(t)).map(([e]) => `.${e}`);\n return [...types, ...exts].join(\",\");\n}\n\nexport interface Screened {\n /** Files to upload, each with its rule (typed by extension when needed). */\n accepted: { file: File; rule: UploadRule }[];\n /** Files the server would refuse, with the refusal it would answer. */\n refused: { file: File; error: ContentKitError }[];\n}\n\n/**\n * Checks files against the rules before any upload: a type some rule takes\n * (the first that does), its size, and the rule's file cap counting\n * have(rule) files already there. A rule without types, size or cap lets\n * that check through; the server decides.\n */\nexport function screenFiles(files: Iterable<File>, rules: readonly UploadRule[], have: (rule: UploadRule) => number = () => 0): Screened {\n const out: Screened = { accepted: [], refused: [] };\n const counts = new Map<UploadRule, number>();\n const allowed = [...new Set(rules.flatMap((r) => r.types))];\n for (const picked of files) {\n const file = withMediaType(picked, allowed);\n const rule = rules.find((r) => !r.types.length || r.types.includes(file.type));\n if (!rule) {\n const details: ErrorDetails = { type: file.type || undefined, allowed };\n out.refused.push({ file, error: new ContentKitError(\"type_not_allowed\", `${file.name}: type not allowed`, { status: 415, details }) });\n continue;\n }\n if (rule.max_bytes > 0 && file.size > rule.max_bytes) {\n const details: ErrorDetails = { type: file.type, size: file.size, max_bytes: rule.max_bytes };\n out.refused.push({ file, error: new ContentKitError(\"too_large\", `${file.name}: too large`, { status: 413, details }) });\n continue;\n }\n const n = counts.get(rule) ?? have(rule);\n if (rule.max && n >= rule.max) {\n const details: ErrorDetails = { max: rule.max };\n out.refused.push({ file, error: new ContentKitError(\"too_many_files\", `${file.name}: at most ${rule.max} files`, { status: 409, details }) });\n continue;\n }\n counts.set(rule, n + 1);\n out.accepted.push({ file, rule });\n }\n return out;\n}\n\n/** name, or name-2, name-3… so its stem is not among taken (names or paths without the directory). */\nexport function uniqueName(name: string, taken: Iterable<string>): string {\n const stems = new Set([...taken].map(stem));\n if (!stems.has(stem(name))) return name;\n const dot = name.lastIndexOf(\".\");\n const base = dot > 0 ? name.slice(0, dot) : name;\n const ext = dot > 0 ? name.slice(dot) : \"\";\n for (let n = 2; ; n++) {\n const candidate = `${base}-${n}${ext}`;\n if (!stems.has(stem(candidate))) return candidate;\n }\n}\n\n/** \"16:9\" style label of a width/height ratio: 2.4 → \"2.4:1\", 0.4167 → \"1:2.4\". */\nexport function ratioLabel(r: number): string {\n const round = (v: number) => String(Math.round(v * 100) / 100);\n return r >= 1 ? `${round(r)}:1` : `1:${round(1 / r)}`;\n}\n\n/** The kind's public preset for an upload path: the one named name, else the first with an aspect, else the first. */\nexport function presetFor(presets: readonly PresetRule[], kind: string, path: string, name?: string): PresetRule | undefined {\n const at = presets.filter((p) => p.kind === kind && ruleFor([{ path: p.from }], path));\n return (name ? at.find((p) => p.name === name) : undefined) ?? at.find((p) => p.aspect) ?? at[0];\n}\n\n/**\n * The kind's default image for a preset, at every width: what the gateway\n * serves for an item without its own (a URL from the template never names a\n * current file). null when the preset has no default (`rule.default`).\n */\nexport function defaultImage(rule: PresetRule, id: string): PublicPreset | null {\n if (!rule.default || rule.first || rule.to.includes(NAME)) return null;\n return logicalImage(rule, id);\n}\n\n/**\n * A Stable preset's image of an item (`rule.stable`, e.g. the content\n * model's `avatars`): URLs built from the id that show its current image,\n * else the default. null for any other preset.\n */\nexport function stableImage(rule: PresetRule, id: string): PublicPreset | null {\n return rule.stable ? logicalImage(rule, id) : null;\n}\n\nfunction logicalImage(rule: PresetRule, id: string): PublicPreset {\n const r = ratio(rule.aspect);\n return {\n preset: rule.name,\n aspect: rule.aspect,\n renditions: rule.widths.map((w) => ({ url: publicURL(rule.base, rule.namespace, rule.kind, id, fill(rule.to, { w })), w, ...(r ? { h: Math.round(w / r) } : {}) })),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ForegroundCue, ReaderMeta, ReaderPlan } from './generated/wire.js';
|
|
2
|
+
/** What a version's reader metadata may name: its pages in reading order and its audio tracks. */
|
|
3
|
+
export interface ReaderNames {
|
|
4
|
+
pages: string[];
|
|
5
|
+
files: string[];
|
|
6
|
+
}
|
|
7
|
+
/** The names an editor's reader plan lists: every page of every segment, in order, and the playback tracks. */
|
|
8
|
+
export declare function readerNames(plan: ReaderPlan): ReaderNames;
|
|
9
|
+
/** An empty schema-1 reader metadata. */
|
|
10
|
+
export declare const emptyReaderMeta: () => ReaderMeta;
|
|
11
|
+
export interface AudioMatch {
|
|
12
|
+
/** One cue per matched file: on the anchor its stem names, else the page. */
|
|
13
|
+
foreground: ForegroundCue[];
|
|
14
|
+
unmatchedPages: string[];
|
|
15
|
+
unmatchedFiles: string[];
|
|
16
|
+
}
|
|
17
|
+
/** One foreground cue per audio file whose stem names an anchor (first) or a page, case-insensitively ("0002.mp3" plays at page "0002"). */
|
|
18
|
+
export declare function matchAudio(names: ReaderNames, anchors?: readonly string[]): AudioMatch;
|
|
19
|
+
/** Where a problem is: an anchor, a cue or the defaults, by index. */
|
|
20
|
+
export interface ReaderMetaProblem {
|
|
21
|
+
at: "anchors" | "background" | "foreground" | "defaults";
|
|
22
|
+
index: number;
|
|
23
|
+
/** id: empty or repeated; offset: outside 0-100; duration, seconds: not positive; page, anchor, file: names nothing; target: a foreground cue without a page or an anchor. */
|
|
24
|
+
problem: "id" | "offset" | "duration" | "seconds" | "page" | "anchor" | "file" | "target";
|
|
25
|
+
}
|
|
26
|
+
/** What the server refuses, and a foreground cue without a place: so an editor sees it before saving. */
|
|
27
|
+
export declare function readerMetaProblems(meta: ReaderMeta, names: ReaderNames): ReaderMetaProblem[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//#region src/client/reader-meta.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return {
|
|
4
|
+
pages: e.segments.flatMap((e) => (e.pages ?? []).map((e) => e.name)),
|
|
5
|
+
files: (e.playback?.tracks ?? []).map((e) => e.name)
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
var t = () => ({
|
|
9
|
+
schema_version: 1,
|
|
10
|
+
anchors: [],
|
|
11
|
+
background: [],
|
|
12
|
+
foreground: []
|
|
13
|
+
}), n = (e) => {
|
|
14
|
+
let t = e.lastIndexOf(".");
|
|
15
|
+
return (t > 0 ? e.slice(0, t) : e).toLowerCase();
|
|
16
|
+
};
|
|
17
|
+
function r(e, t = []) {
|
|
18
|
+
let r = /* @__PURE__ */ new Map();
|
|
19
|
+
for (let t of e.files) r.has(n(t)) || r.set(n(t), t);
|
|
20
|
+
let i = /* @__PURE__ */ new Set(), a = [];
|
|
21
|
+
for (let e of t) {
|
|
22
|
+
let t = r.get(n(e));
|
|
23
|
+
t && !i.has(t) && (a.push({
|
|
24
|
+
file: t,
|
|
25
|
+
anchor: e
|
|
26
|
+
}), i.add(t));
|
|
27
|
+
}
|
|
28
|
+
let o = [];
|
|
29
|
+
for (let t of e.pages) {
|
|
30
|
+
let e = r.get(n(t));
|
|
31
|
+
e && !i.has(e) ? (a.push({
|
|
32
|
+
file: e,
|
|
33
|
+
page: t
|
|
34
|
+
}), i.add(e)) : o.push(t);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
foreground: a,
|
|
38
|
+
unmatchedPages: o,
|
|
39
|
+
unmatchedFiles: e.files.filter((e) => !i.has(e))
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function i(e, t) {
|
|
43
|
+
let n = [], r = new Set(t.pages), i = new Set(t.files), a = /* @__PURE__ */ new Set(), o = e.defaults?.auto_advance_seconds;
|
|
44
|
+
o !== void 0 && !(o > 0) && n.push({
|
|
45
|
+
at: "defaults",
|
|
46
|
+
index: 0,
|
|
47
|
+
problem: "seconds"
|
|
48
|
+
}), (e.anchors ?? []).forEach((e, t) => {
|
|
49
|
+
(!e.id.trim() || a.has(e.id)) && n.push({
|
|
50
|
+
at: "anchors",
|
|
51
|
+
index: t,
|
|
52
|
+
problem: "id"
|
|
53
|
+
}), a.add(e.id), r.has(e.page) || n.push({
|
|
54
|
+
at: "anchors",
|
|
55
|
+
index: t,
|
|
56
|
+
problem: "page"
|
|
57
|
+
}), e.offset_percent >= 0 && e.offset_percent <= 100 || n.push({
|
|
58
|
+
at: "anchors",
|
|
59
|
+
index: t,
|
|
60
|
+
problem: "offset"
|
|
61
|
+
}), e.duration_seconds !== void 0 && !(e.duration_seconds > 0) && n.push({
|
|
62
|
+
at: "anchors",
|
|
63
|
+
index: t,
|
|
64
|
+
problem: "duration"
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
let s = (e, t, i, o) => {
|
|
68
|
+
i !== void 0 && !r.has(i) && n.push({
|
|
69
|
+
at: e,
|
|
70
|
+
index: t,
|
|
71
|
+
problem: "page"
|
|
72
|
+
}), o !== void 0 && !a.has(o) && n.push({
|
|
73
|
+
at: e,
|
|
74
|
+
index: t,
|
|
75
|
+
problem: "anchor"
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
return (e.background ?? []).forEach((e, t) => {
|
|
79
|
+
i.has(e.file) || n.push({
|
|
80
|
+
at: "background",
|
|
81
|
+
index: t,
|
|
82
|
+
problem: "file"
|
|
83
|
+
}), s("background", t, e.from_page, e.from_anchor), s("background", t, e.to_page, e.to_anchor);
|
|
84
|
+
}), (e.foreground ?? []).forEach((e, t) => {
|
|
85
|
+
i.has(e.file) || n.push({
|
|
86
|
+
at: "foreground",
|
|
87
|
+
index: t,
|
|
88
|
+
problem: "file"
|
|
89
|
+
}), e.page === void 0 == (e.anchor === void 0) && n.push({
|
|
90
|
+
at: "foreground",
|
|
91
|
+
index: t,
|
|
92
|
+
problem: "target"
|
|
93
|
+
}), s("foreground", t, e.page, e.anchor);
|
|
94
|
+
}), n;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { t as emptyReaderMeta, r as matchAudio, i as readerMetaProblems, e as readerNames };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=reader-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-meta.js","names":[],"sources":["../../src/client/reader-meta.ts"],"sourcesContent":["import type { ForegroundCue, ReaderMeta, ReaderPlan } from \"./generated/wire.js\";\n\n/** What a version's reader metadata may name: its pages in reading order and its audio tracks. */\nexport interface ReaderNames {\n pages: string[];\n files: string[];\n}\n\n/** The names an editor's reader plan lists: every page of every segment, in order, and the playback tracks. */\nexport function readerNames(plan: ReaderPlan): ReaderNames {\n return { pages: plan.segments.flatMap((s) => (s.pages ?? []).map((p) => p.name)), files: (plan.playback?.tracks ?? []).map((t) => t.name) };\n}\n\n/** An empty schema-1 reader metadata. */\nexport const emptyReaderMeta = (): ReaderMeta => ({ schema_version: 1, anchors: [], background: [], foreground: [] });\n\nconst stem = (name: string) => {\n const i = name.lastIndexOf(\".\");\n return (i > 0 ? name.slice(0, i) : name).toLowerCase();\n};\n\nexport interface AudioMatch {\n /** One cue per matched file: on the anchor its stem names, else the page. */\n foreground: ForegroundCue[];\n unmatchedPages: string[];\n unmatchedFiles: string[];\n}\n\n/** One foreground cue per audio file whose stem names an anchor (first) or a page, case-insensitively (\"0002.mp3\" plays at page \"0002\"). */\nexport function matchAudio(names: ReaderNames, anchors: readonly string[] = []): AudioMatch {\n const byStem = new Map<string, string>();\n for (const f of names.files) if (!byStem.has(stem(f))) byStem.set(stem(f), f);\n const used = new Set<string>();\n const foreground: ForegroundCue[] = [];\n for (const anchor of anchors) {\n const file = byStem.get(stem(anchor));\n if (file && !used.has(file)) {\n foreground.push({ file, anchor });\n used.add(file);\n }\n }\n const unmatchedPages: string[] = [];\n for (const page of names.pages) {\n const file = byStem.get(stem(page));\n if (file && !used.has(file)) {\n foreground.push({ file, page });\n used.add(file);\n } else unmatchedPages.push(page);\n }\n return { foreground, unmatchedPages, unmatchedFiles: names.files.filter((f) => !used.has(f)) };\n}\n\n/** Where a problem is: an anchor, a cue or the defaults, by index. */\nexport interface ReaderMetaProblem {\n at: \"anchors\" | \"background\" | \"foreground\" | \"defaults\";\n index: number;\n /** id: empty or repeated; offset: outside 0-100; duration, seconds: not positive; page, anchor, file: names nothing; target: a foreground cue without a page or an anchor. */\n problem: \"id\" | \"offset\" | \"duration\" | \"seconds\" | \"page\" | \"anchor\" | \"file\" | \"target\";\n}\n\n/** What the server refuses, and a foreground cue without a place: so an editor sees it before saving. */\nexport function readerMetaProblems(meta: ReaderMeta, names: ReaderNames): ReaderMetaProblem[] {\n const out: ReaderMetaProblem[] = [];\n const pages = new Set(names.pages);\n const files = new Set(names.files);\n const ids = new Set<string>();\n const auto = meta.defaults?.auto_advance_seconds;\n if (auto !== undefined && !(auto > 0)) out.push({ at: \"defaults\", index: 0, problem: \"seconds\" });\n (meta.anchors ?? []).forEach((a, index) => {\n if (!a.id.trim() || ids.has(a.id)) out.push({ at: \"anchors\", index, problem: \"id\" });\n ids.add(a.id);\n if (!pages.has(a.page)) out.push({ at: \"anchors\", index, problem: \"page\" });\n if (!(a.offset_percent >= 0 && a.offset_percent <= 100)) out.push({ at: \"anchors\", index, problem: \"offset\" });\n if (a.duration_seconds !== undefined && !(a.duration_seconds > 0)) out.push({ at: \"anchors\", index, problem: \"duration\" });\n });\n const place = (at: ReaderMetaProblem[\"at\"], index: number, page?: string, anchor?: string) => {\n if (page !== undefined && !pages.has(page)) out.push({ at, index, problem: \"page\" });\n if (anchor !== undefined && !ids.has(anchor)) out.push({ at, index, problem: \"anchor\" });\n };\n (meta.background ?? []).forEach((c, index) => {\n if (!files.has(c.file)) out.push({ at: \"background\", index, problem: \"file\" });\n place(\"background\", index, c.from_page, c.from_anchor);\n place(\"background\", index, c.to_page, c.to_anchor);\n });\n (meta.foreground ?? []).forEach((c, index) => {\n if (!files.has(c.file)) out.push({ at: \"foreground\", index, problem: \"file\" });\n if ((c.page === undefined) === (c.anchor === undefined)) out.push({ at: \"foreground\", index, problem: \"target\" });\n place(\"foreground\", index, c.page, c.anchor);\n });\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Activity, MyStats, TopContents } from './generated/wire.js';
|
|
2
|
+
import { Period } from './discovery.js';
|
|
3
|
+
import { Http } from './http.js';
|
|
4
|
+
export interface ActivityQuery {
|
|
5
|
+
kind: string;
|
|
6
|
+
/** The last days, today included: 7 by default, at most 90. */
|
|
7
|
+
days?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TopQuery {
|
|
10
|
+
kind: string;
|
|
11
|
+
/** views (the default) or comments. */
|
|
12
|
+
metric?: "views" | "comments";
|
|
13
|
+
/** 30d by default. */
|
|
14
|
+
period?: Period;
|
|
15
|
+
access?: readonly ("public" | "tier" | "group" | "ppv" | "group_ppv")[];
|
|
16
|
+
tiers?: readonly string[];
|
|
17
|
+
/** Titles and paths in this language; the site's first by default. */
|
|
18
|
+
language?: string;
|
|
19
|
+
/** 10 by default, at most 100. */
|
|
20
|
+
limit?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Content statistics: staff activity and rankings (root:content:view-restricted), and the signed-in reader's own. */
|
|
23
|
+
export declare class StatsClient {
|
|
24
|
+
private readonly http;
|
|
25
|
+
constructor(http: Http);
|
|
26
|
+
/** A kind's views per UTC day, newest first: view sessions, distinct viewers and signed-in viewers. */
|
|
27
|
+
activity(q: ActivityQuery, signal?: AbortSignal): Promise<Activity>;
|
|
28
|
+
/** A kind's live, listed content ranked by views or published comments over a period. */
|
|
29
|
+
top(q: TopQuery, signal?: AbortSignal): Promise<TopContents>;
|
|
30
|
+
/** The signed-in reader's statistics: contents finished per kind, comments and the reactions they received. */
|
|
31
|
+
mine(signal?: AbortSignal): Promise<MyStats>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { call as e } from "./route.js";
|
|
2
|
+
//#region src/client/stats.ts
|
|
3
|
+
var t = class {
|
|
4
|
+
http;
|
|
5
|
+
constructor(e) {
|
|
6
|
+
this.http = e;
|
|
7
|
+
}
|
|
8
|
+
activity(t, n) {
|
|
9
|
+
return e(this.http, "GET", "/stats/activity", {
|
|
10
|
+
query: {
|
|
11
|
+
kind: t.kind,
|
|
12
|
+
days: t.days
|
|
13
|
+
},
|
|
14
|
+
signal: n
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
top(t, n) {
|
|
18
|
+
let { access: r, tiers: i, ...a } = t;
|
|
19
|
+
return e(this.http, "GET", "/stats/top", {
|
|
20
|
+
query: {
|
|
21
|
+
...a,
|
|
22
|
+
access: r?.join(","),
|
|
23
|
+
tiers: i?.join(",")
|
|
24
|
+
},
|
|
25
|
+
signal: n
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
mine(t) {
|
|
29
|
+
return e(this.http, "GET", "/me/stats", { signal: t });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { t as StatsClient };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","names":[],"sources":["../../src/client/stats.ts"],"sourcesContent":["import type { Activity, MyStats, TopContents } from \"./generated/wire.js\";\nimport type { Period } from \"./discovery.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\n\nexport interface ActivityQuery {\n kind: string;\n /** The last days, today included: 7 by default, at most 90. */\n days?: number;\n}\n\nexport interface TopQuery {\n kind: string;\n /** views (the default) or comments. */\n metric?: \"views\" | \"comments\";\n /** 30d by default. */\n period?: Period;\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n tiers?: readonly string[];\n /** Titles and paths in this language; the site's first by default. */\n language?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\n\n/** Content statistics: staff activity and rankings (root:content:view-restricted), and the signed-in reader's own. */\nexport class StatsClient {\n constructor(private readonly http: Http) {}\n\n /** A kind's views per UTC day, newest first: view sessions, distinct viewers and signed-in viewers. */\n activity(q: ActivityQuery, signal?: AbortSignal): Promise<Activity> {\n return call(this.http, \"GET\", \"/stats/activity\", { query: { kind: q.kind, days: q.days }, signal });\n }\n /** A kind's live, listed content ranked by views or published comments over a period. */\n top(q: TopQuery, signal?: AbortSignal): Promise<TopContents> {\n const { access, tiers, ...rest } = q;\n return call(this.http, \"GET\", \"/stats/top\", { query: { ...rest, access: access?.join(\",\"), tiers: tiers?.join(\",\") }, signal });\n }\n /** The signed-in reader's statistics: contents finished per kind, comments and the reactions they received. */\n mine(signal?: AbortSignal): Promise<MyStats> {\n return call(this.http, \"GET\", \"/me/stats\", { signal });\n }\n}\n"],"mappings":";;AA0BA,IAAa,IAAb,MAAyB;CACM;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,SAAS,GAAkB,GAAyC;EAClE,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,MAAM,EAAE;IAAM,MAAM,EAAE;GAAK;GAAG;EAAO,CAAC;CACpG;CAEA,IAAI,GAAa,GAA4C;EAC3D,IAAM,EAAE,WAAQ,UAAO,GAAG,MAAS;EACnC,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO;IAAE,GAAG;IAAM,QAAQ,GAAQ,KAAK,GAAG;IAAG,OAAO,GAAO,KAAK,GAAG;GAAE;GAAG;EAAO,CAAC;CAChI;CAEA,KAAK,GAAwC;EAC3C,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa,EAAE,UAAO,CAAC;CACvD;AACF"}
|
|
@@ -8,7 +8,7 @@ export declare class VersionsClient {
|
|
|
8
8
|
constructor(http: Http);
|
|
9
9
|
/** The version's reader plan for this viewer; Pending while it has no current publication. */
|
|
10
10
|
reader(content: string, version: string, signal?: AbortSignal): Promise<ReaderPlan | Pending>;
|
|
11
|
-
/** Editors: the version's anchors
|
|
11
|
+
/** Editors: replaces the version's reader metadata (anchors, audio cues, defaults). */
|
|
12
12
|
setReader(content: string, version: string, meta: ReaderMeta): Promise<void>;
|
|
13
13
|
/** Editors: fills a version without media from a sibling, in a durable job. */
|
|
14
14
|
fork(content: string, version: string, body: ForkBody): Promise<Pending>;
|
package/dist/client/versions.js
CHANGED
|
@@ -15,13 +15,18 @@ var r = (e) => !!e && typeof e == "object" && "status" in e && !("pages" in e),
|
|
|
15
15
|
signal: r
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
-
setReader(e, n, r) {
|
|
19
|
-
|
|
18
|
+
async setReader(e, n, r) {
|
|
19
|
+
let i = this.http.captureChanges();
|
|
20
|
+
await t(this.http, "PUT", "/contents/{id}/versions/{version}/reader", {
|
|
20
21
|
params: {
|
|
21
22
|
id: e,
|
|
22
23
|
version: n
|
|
23
24
|
},
|
|
24
25
|
body: r
|
|
26
|
+
}), i({
|
|
27
|
+
type: "reader.changed",
|
|
28
|
+
content: e,
|
|
29
|
+
version: n
|
|
25
30
|
});
|
|
26
31
|
}
|
|
27
32
|
fork(e, n, r) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.js","names":[],"sources":["../../src/client/versions.ts"],"sourcesContent":["import { ContentKitError } from \"./errors.js\";\nimport type { ForkBody, Pending, ReaderMeta, ReaderPlan } from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, routeURL } from \"./route.js\";\n\n/** A version's answer while it is prepared (202). */\nexport const isPending = (x: unknown): x is Pending => !!x && typeof x === \"object\" && \"status\" in x && !(\"pages\" in x);\n\n/** Content versions: reader plans, reader metadata, forks and downloads. */\nexport class VersionsClient {\n constructor(private readonly http: Http) {}\n\n /** The version's reader plan for this viewer; Pending while it has no current publication. */\n reader(content: string, version: string, signal?: AbortSignal): Promise<ReaderPlan | Pending> {\n return call(this.http, \"GET\", \"/contents/{id}/versions/{version}/reader\", { params: { id: content, version }, signal });\n }\n /** Editors: the version's anchors
|
|
1
|
+
{"version":3,"file":"versions.js","names":[],"sources":["../../src/client/versions.ts"],"sourcesContent":["import { ContentKitError } from \"./errors.js\";\nimport type { ForkBody, Pending, ReaderMeta, ReaderPlan } from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, routeURL } from \"./route.js\";\n\n/** A version's answer while it is prepared (202). */\nexport const isPending = (x: unknown): x is Pending => !!x && typeof x === \"object\" && \"status\" in x && !(\"pages\" in x);\n\n/** Content versions: reader plans, reader metadata, forks and downloads. */\nexport class VersionsClient {\n constructor(private readonly http: Http) {}\n\n /** The version's reader plan for this viewer; Pending while it has no current publication. */\n reader(content: string, version: string, signal?: AbortSignal): Promise<ReaderPlan | Pending> {\n return call(this.http, \"GET\", \"/contents/{id}/versions/{version}/reader\", { params: { id: content, version }, signal });\n }\n /** Editors: replaces the version's reader metadata (anchors, audio cues, defaults). */\n async setReader(content: string, version: string, meta: ReaderMeta): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"PUT\", \"/contents/{id}/versions/{version}/reader\", { params: { id: content, version }, body: meta });\n emit({ type: \"reader.changed\", content, version });\n }\n /** Editors: fills a version without media from a sibling, in a durable job. */\n fork(content: string, version: string, body: ForkBody): Promise<Pending> {\n return call(this.http, \"POST\", \"/contents/{id}/versions/{version}/fork\", { params: { id: content, version }, body });\n }\n /**\n * The version's pages as a zip, charged to the viewer's download quota;\n * Pending while it is prepared (ask again).\n */\n async download(content: string, version: string, signal?: AbortSignal): Promise<Blob | Pending> {\n const url = routeURL(this.http, \"GET\", \"/contents/{id}/versions/{version}/download\", { params: { id: content, version } });\n const out = await this.http.request<Blob>(url, { signal, blob: true });\n if (!out.type.startsWith(\"application/json\")) return out;\n const body: unknown = JSON.parse(await out.text());\n if (isPending(body)) return body;\n throw new ContentKitError(\"internal_error\", \"unexpected download answer\");\n }\n}\n"],"mappings":";;;AAMA,IAAa,KAAa,MAA6B,CAAC,CAAC,KAAK,OAAO,KAAM,YAAY,YAAY,KAAK,EAAE,WAAW,IAGxG,IAAb,MAA4B;CACG;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,OAAO,GAAiB,GAAiB,GAAqD;EAC5F,OAAO,EAAK,KAAK,MAAM,OAAO,4CAA4C;GAAE,QAAQ;IAAE,IAAI;IAAS;GAAQ;GAAG;EAAO,CAAC;CACxH;CAEA,MAAM,UAAU,GAAiB,GAAiB,GAAiC;EACjF,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,OAAO,4CAA4C;GAAE,QAAQ;IAAE,IAAI;IAAS;GAAQ;GAAG,MAAM;EAAK,CAAC,GACzH,EAAK;GAAE,MAAM;GAAkB;GAAS;EAAQ,CAAC;CACnD;CAEA,KAAK,GAAiB,GAAiB,GAAkC;EACvE,OAAO,EAAK,KAAK,MAAM,QAAQ,0CAA0C;GAAE,QAAQ;IAAE,IAAI;IAAS;GAAQ;GAAG;EAAK,CAAC;CACrH;CAKA,MAAM,SAAS,GAAiB,GAAiB,GAA+C;EAC9F,IAAM,IAAM,EAAS,KAAK,MAAM,OAAO,8CAA8C,EAAE,QAAQ;GAAE,IAAI;GAAS;EAAQ,EAAE,CAAC,GACnH,IAAM,MAAM,KAAK,KAAK,QAAc,GAAK;GAAE;GAAQ,MAAM;EAAK,CAAC;EACrE,IAAI,CAAC,EAAI,KAAK,WAAW,kBAAkB,GAAG,OAAO;EACrD,IAAM,IAAgB,KAAK,MAAM,MAAM,EAAI,KAAK,CAAC;EACjD,IAAI,EAAU,CAAI,GAAG,OAAO;EAC5B,MAAM,IAAI,EAAgB,kBAAkB,4BAA4B;CAC1E;AACF"}
|
package/dist/client.js
CHANGED
|
@@ -10,31 +10,33 @@ import { sha256Hex as D } from "./client/media/hash.js";
|
|
|
10
10
|
import { Pacer as O } from "./client/media/pacer.js";
|
|
11
11
|
import { defaultTransport as k, fetchTransport as A, xhrTransport as j } from "./client/media/transport.js";
|
|
12
12
|
import { MediaClient as M, backoff as N, samePath as P, stem as F } from "./client/media/client.js";
|
|
13
|
-
import { acceptOf as I, defaultImage as L, filesFor as R, isPattern as z, ratioLabel as B, ruleDir as V, ruleFor as H, screenFiles as U,
|
|
14
|
-
import { centeredCrop as
|
|
15
|
-
import { encodeRemaining as
|
|
16
|
-
import { ItemUploadQueue as
|
|
17
|
-
import { decodeImage as
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { CatalogClient as
|
|
24
|
-
import { CodesClient as
|
|
25
|
-
import { BODY_SUFFIX as
|
|
26
|
-
import { BansClient as
|
|
27
|
-
import { CommentsClient as
|
|
28
|
-
import { Beacons as
|
|
29
|
-
import { DiscoveryClient as
|
|
30
|
-
import { FavoritesClient as
|
|
31
|
-
import { ModerationClient as
|
|
32
|
-
import { PollsClient as
|
|
33
|
-
import { ReactionsClient as
|
|
34
|
-
import { GroupsClient as
|
|
35
|
-
import { OffersClient as
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
|
|
13
|
+
import { acceptOf as I, defaultImage as L, filesFor as R, isPattern as z, ratioLabel as B, ruleDir as V, ruleFor as H, screenFiles as U, stableImage as W, uniqueName as G, uploadRules as K, withMediaType as q } from "./client/media/rules.js";
|
|
14
|
+
import { centeredCrop as J, constrainCrop as Y, editOf as X, editedSize as Z, fromRotated as Q, rotation as $, sameEdit as ee, toOriginal as te, toRotated as ne } from "./client/crop.js";
|
|
15
|
+
import { encodeRemaining as re } from "./client/media/encode.js";
|
|
16
|
+
import { ItemUploadQueue as ie } from "./client/media/queue.js";
|
|
17
|
+
import { decodeImage as ae, isAnimatedImage as oe } from "./client/image.js";
|
|
18
|
+
import { reactionVerb as se, sameTarget as ce } from "./client/content/types.js";
|
|
19
|
+
import { READ_CHUNK as le, chunkOffsets as ue, isProcessing as de, mergeReads as fe, processing as pe } from "./client/media/windows.js";
|
|
20
|
+
import { ABR_DEFAULTS as me, NETWORK_FAILURES_BEFORE_ERROR as he, REFRESH_BEFORE_EXPIRY as ge, abrHlsConfig as _e, capRung as ve, classifyHlsError as ye, classifyMediaError as be, expiryDelay as xe, hlsConfig as Se, initialEstimate as Ce, refreshable as we, startRung as Te, statusKind as Ee } from "./client/playback.js";
|
|
21
|
+
import { formatDuration as De, galleryItems as Oe, isAudioType as ke, isImageType as Ae, isSubtitleType as je, isVideoType as Me, stageAspect as Ne } from "./client/gallery.js";
|
|
22
|
+
import { PLAYBACK_SPEEDS as Pe, PLAYER_TRACKS_KEY as Fe, PLAYER_VOLUME_KEY as Ie, parseSpriteVtt as Le, playerKeyAction as Re, resumeAt as ze } from "./client/player.js";
|
|
23
|
+
import { CatalogClient as Be } from "./client/catalog.js";
|
|
24
|
+
import { CodesClient as Ve } from "./client/codes.js";
|
|
25
|
+
import { BODY_SUFFIX as He, ContentsClient as Ue, IMAGE_SCHEME as We, imageRef as Ge, imageRefs as Ke } from "./client/contents.js";
|
|
26
|
+
import { BansClient as qe } from "./client/content/bans.js";
|
|
27
|
+
import { CommentsClient as Je } from "./client/content/comments.js";
|
|
28
|
+
import { Beacons as Ye, eventId as Xe, readerSession as Ze } from "./client/beacons.js";
|
|
29
|
+
import { DiscoveryClient as Qe } from "./client/discovery.js";
|
|
30
|
+
import { FavoritesClient as $e } from "./client/content/favorites.js";
|
|
31
|
+
import { ModerationClient as et } from "./client/content/moderation.js";
|
|
32
|
+
import { PollsClient as tt } from "./client/content/polls.js";
|
|
33
|
+
import { ReactionsClient as nt } from "./client/content/reactions.js";
|
|
34
|
+
import { GroupsClient as rt } from "./client/groups.js";
|
|
35
|
+
import { OffersClient as it } from "./client/offers.js";
|
|
36
|
+
import { StatsClient as at } from "./client/stats.js";
|
|
37
|
+
import { VersionsClient as ot, isPending as st } from "./client/versions.js";
|
|
38
|
+
import { NotificationsClient as ct } from "./client/notifications.js";
|
|
39
|
+
import { createContentKitClient as lt } from "./client/client.js";
|
|
40
|
+
import { LETTERS as ut, attributeInput as dt, fold as ft, fromLocalInput as pt, humanize as mt, letterOf as ht, parseAttribute as gt, parseAttributes as _t, slugify as vt, toLocalInput as yt } from "./client/group-fields.js";
|
|
41
|
+
import { emptyReaderMeta as bt, matchAudio as xt, readerMetaProblems as St, readerNames as Ct } from "./client/reader-meta.js";
|
|
42
|
+
export { me as ABR_DEFAULTS, He as BODY_SUFFIX, qe as BansClient, Ye as Beacons, i as CLIENT_ERROR_CODES, Be as CatalogClient, Ve as CodesClient, Je as CommentsClient, a as ContentKitError, Ue as ContentsClient, e as DEFAULT_DENSITY, Qe as DiscoveryClient, o as ERROR_CODES, $e as FavoritesClient, rt as GroupsClient, We as IMAGE_SCHEME, ie as ItemUploadQueue, ut as LETTERS, w as MAX_PART_SIZE, T as MAX_SINGLE_PUT, E as MIN_PART_SIZE, C as MediaApi, M as MediaClient, et as ModerationClient, he as NETWORK_FAILURES_BEFORE_ERROR, ct as NotificationsClient, it as OffersClient, Pe as PLAYBACK_SPEEDS, Fe as PLAYER_TRACKS_KEY, Ie as PLAYER_VOLUME_KEY, O as Pacer, tt as PollsClient, le as READ_CHUNK, ge as REFRESH_BEFORE_EXPIRY, nt as ReactionsClient, at as StatsClient, ot as VersionsClient, _e as abrHlsConfig, I as acceptOf, h as aspectOf, dt as attributeInput, N as backoff, _ as call, ve as capRung, J as centeredCrop, y as checkRef, ue as chunkOffsets, ye as classifyHlsError, be as classifyMediaError, Y as constrainCrop, lt as createContentKitClient, ae as decodeImage, L as defaultImage, k as defaultTransport, t as densityFor, X as editOf, Z as editedSize, bt as emptyReaderMeta, re as encodeRemaining, Xe as eventId, xe as expiryDelay, s as failureError, A as fetchTransport, R as filesFor, d as fill, ft as fold, De as formatDuration, pt as fromLocalInput, Q as fromRotated, Oe as galleryItems, Se as hlsConfig, mt as humanize, Ge as imageRef, Ke as imageRefs, Ce as initialEstimate, oe as isAnimatedImage, ke as isAudioType, b as isContentId, c as isContentKitError, Ae as isImageType, z as isPattern, st as isPending, de as isProcessing, je as isSubtitleType, Me as isVideoType, x as itemKey, ht as letterOf, xt as matchAudio, fe as mergeReads, gt as parseAttribute, _t as parseAttributes, Le as parseSpriteVtt, n as pickRendition, Re as playerKeyAction, pe as processing, f as publicRenditions, p as publicURL, g as ratio, B as ratioLabel, se as reactionVerb, l as readContentKitError, St as readerMetaProblems, Ct as readerNames, Ze as readerSession, we as refreshable, ze as resumeAt, $ as rotation, v as routeURL, V as ruleDir, H as ruleFor, ee as sameEdit, S as sameItem, P as samePath, ce as sameTarget, U as screenFiles, D as sha256Hex, vt as slugify, r as sortRenditions, m as srcSet, W as stableImage, Ne as stageAspect, Te as startRung, Ee as statusKind, F as stem, u as toContentKitError, yt as toLocalInput, te as toOriginal, ne as toRotated, G as uniqueName, K as uploadRules, q as withMediaType, j as xhrTransport };
|