@ryusenkei/maezae 0.16.0 → 0.18.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 +44 -0
- package/dist/body.js +312 -10
- package/dist/{declared-9_jI3JXH.js → declared-Qo-mWnDd.js} +154 -15
- package/dist/delivery.js +43 -2
- package/dist/model.js +4 -2
- package/dist/types/analytics/src/index.d.ts +1 -0
- package/dist/types/analytics/src/shared.d.ts +21 -0
- package/dist/types/body/src/derive.d.ts +1 -1
- package/dist/types/body/src/embed.d.ts +14 -0
- package/dist/types/body/src/html.d.ts +31 -0
- package/dist/types/body/src/index.d.ts +5 -2
- package/dist/types/body/src/library.d.ts +19 -0
- package/dist/types/body/src/markdown.d.ts +1 -1
- package/dist/types/body/src/normalize.d.ts +1 -1
- package/dist/types/body/src/profile.d.ts +34 -2
- package/dist/types/body/src/render.d.ts +29 -0
- package/dist/types/body/src/types.d.ts +46 -5
- package/dist/types/cms-contract/src/apiKeys.d.ts +27 -0
- package/dist/types/cms-contract/src/assets.d.ts +2 -0
- package/dist/types/cms-contract/src/errors.d.ts +5 -3
- package/dist/types/cms-contract/src/import.d.ts +33 -1
- package/dist/types/cms-contract/src/index.d.ts +6 -5
- package/dist/types/cms-contract/src/labels.d.ts +5 -0
- package/dist/types/cms-contract/src/roles.d.ts +3 -1
- package/dist/types/cms-contract/src/views.d.ts +2 -2
- package/dist/types/cms-contract/src/workflow.d.ts +49 -0
- package/dist/types/model/src/asset.d.ts +8 -2
- package/dist/types/model/src/index.d.ts +2 -2
- package/dist/types/model/src/mediaUrl.d.ts +20 -2
- package/dist/types/model/src/model.d.ts +12 -4
- package/dist/types/sdk/src/body.d.ts +2 -2
- package/dist/types/sdk/src/delivery/index.d.ts +1 -1
- package/dist/types/sdk/src/delivery/values.d.ts +6 -2
- package/dist/types/sdk/src/delivery.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,99 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
// ../body/src/components.ts
|
|
3
3
|
/** 予約の名前(ブロック) */
|
|
4
|
-
const RESERVED_BLOCK_NAMES = /* @__PURE__ */ new Set([
|
|
4
|
+
const RESERVED_BLOCK_NAMES = /* @__PURE__ */ new Set([
|
|
5
|
+
"block",
|
|
6
|
+
"table",
|
|
7
|
+
"file"
|
|
8
|
+
]);
|
|
5
9
|
/** 予約の名前(インライン) */
|
|
6
10
|
const RESERVED_INLINE_NAMES = /* @__PURE__ */ new Set([
|
|
7
11
|
"mark",
|
|
8
12
|
"sup",
|
|
9
13
|
"sub",
|
|
10
|
-
"link"
|
|
14
|
+
"link",
|
|
15
|
+
"u",
|
|
16
|
+
"span"
|
|
11
17
|
]);
|
|
18
|
+
// ../body/src/embed.ts
|
|
19
|
+
/** 埋め込めるサービス */
|
|
20
|
+
const EMBED_PROVIDERS = [
|
|
21
|
+
"youtube",
|
|
22
|
+
"vimeo",
|
|
23
|
+
"x",
|
|
24
|
+
"instagram",
|
|
25
|
+
"spotify",
|
|
26
|
+
"apple-podcasts"
|
|
27
|
+
];
|
|
28
|
+
const ID = /^[\w-]{1,64}$/;
|
|
29
|
+
/** 開始の秒(YouTube の t=1m30s・t=90・start=90) */
|
|
30
|
+
function startOf(params) {
|
|
31
|
+
const raw = params.get("t") ?? params.get("start");
|
|
32
|
+
if (!raw) return void 0;
|
|
33
|
+
const m = raw.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?$/);
|
|
34
|
+
if (!m) return void 0;
|
|
35
|
+
const sec = Number(m[1] ?? 0) * 3600 + Number(m[2] ?? 0) * 60 + Number(m[3] ?? 0);
|
|
36
|
+
return sec > 0 ? sec : void 0;
|
|
37
|
+
}
|
|
38
|
+
/** URL から埋め込み(埋め込めるサービスでなければ null) */
|
|
39
|
+
function embedOfUrl(raw) {
|
|
40
|
+
const url = URL.parse(raw);
|
|
41
|
+
if (!url || url.protocol !== "https:") return null;
|
|
42
|
+
const host = url.hostname.replace(/^(www|m)\./, "");
|
|
43
|
+
const path = url.pathname.split("/").filter(Boolean);
|
|
44
|
+
const pick = (provider, id, start) => id && ID.test(id) ? {
|
|
45
|
+
provider,
|
|
46
|
+
id,
|
|
47
|
+
...start ? { start } : {}
|
|
48
|
+
} : null;
|
|
49
|
+
switch (host) {
|
|
50
|
+
case "youtube.com":
|
|
51
|
+
case "youtube-nocookie.com":
|
|
52
|
+
if (path[0] === "watch") return pick("youtube", url.searchParams.get("v") ?? void 0, startOf(url.searchParams));
|
|
53
|
+
if (path[0] === "embed" || path[0] === "shorts" || path[0] === "live") return pick("youtube", path[1], startOf(url.searchParams));
|
|
54
|
+
return null;
|
|
55
|
+
case "youtu.be": return pick("youtube", path[0], startOf(url.searchParams));
|
|
56
|
+
case "vimeo.com": return pick("vimeo", path.find((p) => /^\d+$/.test(p)));
|
|
57
|
+
case "player.vimeo.com": return path[0] === "video" ? pick("vimeo", path[1]) : null;
|
|
58
|
+
case "twitter.com":
|
|
59
|
+
case "x.com": return path[1] === "status" ? pick("x", path[2]) : null;
|
|
60
|
+
case "instagram.com": return path[0] === "p" || path[0] === "reel" ? pick("instagram", path[1]) : null;
|
|
61
|
+
case "open.spotify.com": {
|
|
62
|
+
const [kind, id] = (path[0] === "embed" ? path.slice(1) : path).filter((p) => !p.startsWith("intl-"));
|
|
63
|
+
return kind && [
|
|
64
|
+
"track",
|
|
65
|
+
"album",
|
|
66
|
+
"playlist",
|
|
67
|
+
"episode",
|
|
68
|
+
"show"
|
|
69
|
+
].includes(kind) && id && ID.test(id) ? {
|
|
70
|
+
provider: "spotify",
|
|
71
|
+
id: `${kind}/${id}`
|
|
72
|
+
} : null;
|
|
73
|
+
}
|
|
74
|
+
case "podcasts.apple.com": {
|
|
75
|
+
const show = path.find((p) => /^id\d+$/.test(p));
|
|
76
|
+
const episode = url.searchParams.get("i");
|
|
77
|
+
return show ? pick("apple-podcasts", episode ? `${show}-${episode}` : show) : null;
|
|
78
|
+
}
|
|
79
|
+
default: return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** 描画の iframe の URL(サービスと ID から組み立てる、X と Instagram は埋め込みのページ) */
|
|
83
|
+
function embedSrcOf(target) {
|
|
84
|
+
const id = encodeURIComponent(target.id).replaceAll("%2F", "/");
|
|
85
|
+
switch (target.provider) {
|
|
86
|
+
case "youtube": return `https://www.youtube-nocookie.com/embed/${id}${target.start ? `?start=${target.start}` : ""}`;
|
|
87
|
+
case "vimeo": return `https://player.vimeo.com/video/${id}${target.start ? `#t=${target.start}s` : ""}`;
|
|
88
|
+
case "x": return `https://platform.twitter.com/embed/Tweet.html?id=${id}`;
|
|
89
|
+
case "instagram": return `https://www.instagram.com/p/${id}/embed`;
|
|
90
|
+
case "spotify": return `https://open.spotify.com/embed/${id}`;
|
|
91
|
+
case "apple-podcasts": {
|
|
92
|
+
const [show, episode] = target.id.split("-");
|
|
93
|
+
return `https://embed.podcasts.apple.com/podcast/${encodeURIComponent(show ?? "")}${episode ? `?i=${encodeURIComponent(episode)}` : ""}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
12
97
|
// ../body/src/library.ts
|
|
13
98
|
/** 注のコンポーネントの名前(本文からの算出 citesOf が参照) */
|
|
14
99
|
const CITE = "cite";
|
|
@@ -71,9 +156,9 @@ const CORE_COMPONENTS = [
|
|
|
71
156
|
{
|
|
72
157
|
name: CITE,
|
|
73
158
|
inline: true,
|
|
74
|
-
description: "
|
|
159
|
+
description: "注(参考資料の ID を参照、参考資料はコンテンツに結び付けが必要)",
|
|
75
160
|
example: ":cite{#出典の ID}",
|
|
76
|
-
props: () => z.object({ id: z.string().min(1, "
|
|
161
|
+
props: () => z.object({ id: z.string().min(1, "注の参考資料が未入力") })
|
|
77
162
|
},
|
|
78
163
|
{
|
|
79
164
|
name: "pending",
|
|
@@ -151,18 +236,20 @@ const CORE_COMPONENTS = [
|
|
|
151
236
|
},
|
|
152
237
|
{
|
|
153
238
|
name: "link-card",
|
|
154
|
-
description: "
|
|
239
|
+
description: "リンクのカード(外のページは https と題名、site・desc・画像は任意。中のコンテンツは ref=\"型の id/行の ID\")",
|
|
155
240
|
example: ":::link-card{href=\"https://…\" title=\"ページの題\" site=\"サイト名\" desc=\"説明\"}\n:::",
|
|
156
241
|
markdown: {
|
|
157
242
|
to: (p) => {
|
|
158
|
-
const { href, title, site, desc, image } = p;
|
|
243
|
+
const { href, ref, title, site, desc, image, fetchedAt } = p;
|
|
159
244
|
return {
|
|
160
|
-
attributes: {
|
|
245
|
+
attributes: Object.fromEntries(Object.entries({
|
|
161
246
|
href,
|
|
247
|
+
ref,
|
|
162
248
|
title,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
249
|
+
site,
|
|
250
|
+
desc,
|
|
251
|
+
"fetched-at": fetchedAt
|
|
252
|
+
}).filter((e) => typeof e[1] === "string" && e[1] !== "")),
|
|
166
253
|
content: image ? [{
|
|
167
254
|
type: "image",
|
|
168
255
|
media: image
|
|
@@ -170,18 +257,70 @@ const CORE_COMPONENTS = [
|
|
|
170
257
|
};
|
|
171
258
|
},
|
|
172
259
|
from: (attrs, content) => ({
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
260
|
+
...optional(attrs, [
|
|
261
|
+
"href",
|
|
262
|
+
"ref",
|
|
263
|
+
"title",
|
|
264
|
+
"site",
|
|
265
|
+
"desc"
|
|
266
|
+
]),
|
|
267
|
+
...attrs["fetched-at"] ? { fetchedAt: attrs["fetched-at"] } : {},
|
|
176
268
|
...content[0]?.type === "image" ? { image: content[0].media } : {}
|
|
177
269
|
})
|
|
178
270
|
},
|
|
179
|
-
props: (media) => z.object({
|
|
271
|
+
props: (media) => z.union([z.object({
|
|
180
272
|
href: https,
|
|
181
273
|
title: z.string().min(1, "リンクのタイトルが未入力"),
|
|
182
274
|
site: z.string().optional(),
|
|
183
275
|
desc: z.string().optional(),
|
|
276
|
+
image: media.optional(),
|
|
277
|
+
fetchedAt: z.string().optional()
|
|
278
|
+
}), z.object({
|
|
279
|
+
ref: z.string().regex(/^[\w-]+\/[\w-]+$/, "ref は「型の id/行の ID」"),
|
|
280
|
+
title: z.string().optional(),
|
|
281
|
+
site: z.string().optional(),
|
|
282
|
+
desc: z.string().optional(),
|
|
184
283
|
image: media.optional()
|
|
284
|
+
})])
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "embed",
|
|
288
|
+
description: `外のサービスの埋め込み(provider は ${EMBED_PROVIDERS.join("・")}、id はサービスの中の ID、url は元の URL、start は開始の秒)`,
|
|
289
|
+
example: ":::embed{provider=youtube id=\"xxxxxxxxxxx\" url=\"https://www.youtube.com/watch?v=xxxxxxxxxxx\"}\n:::",
|
|
290
|
+
markdown: {
|
|
291
|
+
to: (p) => {
|
|
292
|
+
const { provider, id, url, width, height, start, title } = p;
|
|
293
|
+
return { attributes: Object.fromEntries(Object.entries({
|
|
294
|
+
provider,
|
|
295
|
+
id,
|
|
296
|
+
url,
|
|
297
|
+
width,
|
|
298
|
+
height,
|
|
299
|
+
start,
|
|
300
|
+
title
|
|
301
|
+
}).filter(([, v]) => v !== void 0).map(([k, v]) => [k, String(v)])) };
|
|
302
|
+
},
|
|
303
|
+
from: (attrs) => {
|
|
304
|
+
const num = (k) => attrs[k] !== void 0 && attrs[k] !== "" ? { [k]: Number(attrs[k]) } : {};
|
|
305
|
+
return {
|
|
306
|
+
provider: attrs.provider,
|
|
307
|
+
id: attrs.id,
|
|
308
|
+
url: attrs.url,
|
|
309
|
+
...num("width"),
|
|
310
|
+
...num("height"),
|
|
311
|
+
...num("start"),
|
|
312
|
+
...optional(attrs, ["title"])
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
props: () => z.object({
|
|
317
|
+
provider: z.enum(EMBED_PROVIDERS),
|
|
318
|
+
id: z.string().regex(/^[\w-]+(\/[\w-]+)?$/, "埋め込みの ID が不正"),
|
|
319
|
+
url: https,
|
|
320
|
+
width: z.number().int().positive().optional(),
|
|
321
|
+
height: z.number().int().positive().optional(),
|
|
322
|
+
start: z.number().int().nonnegative().optional(),
|
|
323
|
+
title: z.string().optional()
|
|
185
324
|
})
|
|
186
325
|
},
|
|
187
326
|
{
|
|
@@ -333,4 +472,4 @@ function declaredPropsZod(d) {
|
|
|
333
472
|
if (!schema) propsCache.set(d, schema = objectZod(d.props.properties, d.props.required ?? [], { error: () => `${d.label}の引数の形が不正` }));
|
|
334
473
|
return schema;
|
|
335
474
|
}
|
|
336
|
-
export { coreComponentOf as a, CORE_COMPONENTS as i, declaredPropsZod as n, CITE as r, declaredComponentsSchema as t };
|
|
475
|
+
export { coreComponentOf as a, embedSrcOf as c, CORE_COMPONENTS as i, declaredPropsZod as n, EMBED_PROVIDERS as o, CITE as r, embedOfUrl as s, declaredComponentsSchema as t };
|
package/dist/delivery.js
CHANGED
|
@@ -250,12 +250,22 @@ const EXTENSIONS = {
|
|
|
250
250
|
"image/webp": "webp",
|
|
251
251
|
"image/gif": "gif",
|
|
252
252
|
"video/mp4": "mp4",
|
|
253
|
-
"video/webm": "webm"
|
|
253
|
+
"video/webm": "webm",
|
|
254
|
+
"application/pdf": "pdf",
|
|
255
|
+
"application/zip": "zip",
|
|
256
|
+
"text/plain": "txt",
|
|
257
|
+
"text/csv": "csv",
|
|
258
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
259
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
260
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx"
|
|
254
261
|
};
|
|
262
|
+
/** 形式の実体の種類 */
|
|
263
|
+
const mediaKindOf = (mimeType) => mimeType.startsWith("image/") ? "image" : mimeType.startsWith("video/") ? "video" : "file";
|
|
255
264
|
/** 拡張子の形式(配信しない拡張子は null) */
|
|
256
265
|
const mediaMimeOf = (ext) => Object.entries(EXTENSIONS).find(([, e]) => e === ext)?.[0] ?? null;
|
|
257
266
|
const IMAGE_PATH = /^\/images\/([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)-(\d+)x(\d+)\.([a-z0-9]+)$/;
|
|
258
267
|
const VIDEO_PATH = /^\/videos\/([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)\.([a-z0-9]+)$/;
|
|
268
|
+
const FILE_PATH = /^\/files\/([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)\.([a-z0-9]+)$/;
|
|
259
269
|
/** 配信 URL のパスを解析(形が違えば null) */
|
|
260
270
|
function parseMediaPath(pathname) {
|
|
261
271
|
const image = IMAGE_PATH.exec(pathname);
|
|
@@ -280,8 +290,39 @@ function parseMediaPath(pathname) {
|
|
|
280
290
|
ext
|
|
281
291
|
} : null;
|
|
282
292
|
}
|
|
293
|
+
const file = FILE_PATH.exec(pathname);
|
|
294
|
+
if (file) {
|
|
295
|
+
const [, project, id, ext] = file;
|
|
296
|
+
const mime = mediaMimeOf(ext);
|
|
297
|
+
return mime && mediaKindOf(mime) === "file" ? {
|
|
298
|
+
kind: "file",
|
|
299
|
+
project,
|
|
300
|
+
id,
|
|
301
|
+
ext
|
|
302
|
+
} : null;
|
|
303
|
+
}
|
|
283
304
|
return null;
|
|
284
305
|
}
|
|
306
|
+
const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
307
|
+
/** 署名(HMAC-SHA256、対象はパスと期限、変換のクエリは含めない) */
|
|
308
|
+
async function signatureOf(secret, pathname, exp) {
|
|
309
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), {
|
|
310
|
+
name: "HMAC",
|
|
311
|
+
hash: "SHA-256"
|
|
312
|
+
}, false, ["sign"]);
|
|
313
|
+
return hex(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${pathname}:${exp}`)));
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 非公開のメディアの署名つきの URL(exp と sig のクエリを付ける、ほかのクエリは残す)
|
|
317
|
+
* - secret: 媒体の非公開のメディアの署名の鍵、expiresInSec: 期限までの秒、now: 今の UNIX ミリ秒
|
|
318
|
+
*/
|
|
319
|
+
async function signMediaUrl(url, secret, options = {}) {
|
|
320
|
+
const u = new URL(url);
|
|
321
|
+
const exp = Math.floor((options.now ?? Date.now()) / 1e3) + (options.expiresInSec ?? 3600);
|
|
322
|
+
u.searchParams.set("exp", String(exp));
|
|
323
|
+
u.searchParams.set("sig", await signatureOf(secret, u.pathname, exp));
|
|
324
|
+
return u.toString();
|
|
325
|
+
}
|
|
285
326
|
/** 幅・高さの上限(画素) */
|
|
286
327
|
const IMAGE_SIZE_MAX = 4096;
|
|
287
328
|
const FITS = [
|
|
@@ -432,4 +473,4 @@ function createDeliveryClock(timeZone) {
|
|
|
432
473
|
})
|
|
433
474
|
};
|
|
434
475
|
}
|
|
435
|
-
export { DELIVERY_PATH, DeliveryError, cachedDelivery, createDeliveryClient, createDeliveryClock, deliveredImageUrl, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, memoryDeliveryCache, optionLabels, pagePath, parseMediaPath, readChanges, subPath, withImageParams };
|
|
476
|
+
export { DELIVERY_PATH, DeliveryError, cachedDelivery, createDeliveryClient, createDeliveryClock, deliveredImageUrl, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, memoryDeliveryCache, optionLabels, pagePath, parseMediaPath, readChanges, signMediaUrl, subPath, withImageParams };
|
package/dist/model.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as coverStart, d as objectPosition, f as stillOf, i as acceptsRole, l as cropRect, m as isDateOnly, n as publicPathOf, o as typeOf, p as createClock, r as FIELD_ROLES, s as coverPosition, t as pagePathOf, u as croppedHotspot } from "./page-BnwHL-LC.js";
|
|
2
|
-
import { i as CORE_COMPONENTS, t as declaredComponentsSchema } from "./declared-
|
|
2
|
+
import { i as CORE_COMPONENTS, t as declaredComponentsSchema } from "./declared-Qo-mWnDd.js";
|
|
3
3
|
import "./src-BrLwIWaT.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
// ../model/src/asset.ts
|
|
@@ -344,11 +344,13 @@ const fieldUnion = z.discriminatedUnion("type", [
|
|
|
344
344
|
type: z.literal("media"),
|
|
345
345
|
accept: z.array(z.enum(["image", "video"])).optional(),
|
|
346
346
|
multiple: z.boolean().optional(),
|
|
347
|
-
frames: z.array(frame).optional()
|
|
347
|
+
frames: z.array(frame).optional(),
|
|
348
|
+
visibility: z.literal("private").optional()
|
|
348
349
|
}),
|
|
349
350
|
z.object({
|
|
350
351
|
...base,
|
|
351
352
|
type: z.literal("body"),
|
|
353
|
+
visibility: z.literal("private").optional(),
|
|
352
354
|
profile: z.object({
|
|
353
355
|
headings: z.array(z.number()),
|
|
354
356
|
marks: z.array(z.string()),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { recordView, type Classify, type Dataset } from './record.js';
|
|
2
2
|
export { syncCron } from './schedule.js';
|
|
3
|
+
export { isViewsCron, sharedViews, viewsFor, viewsSince, viewsSyncOf } from './shared.js';
|
|
3
4
|
export { analyticsEngine, type DailyRow, fixedSource, type Source, projectDaily, type ProjectSource, renamedSource } from './source.js';
|
|
4
5
|
export { readDaily, syncDaily } from './store.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type DailyRow, type ProjectSource, type Source } from './source.js';
|
|
2
|
+
/** 全媒体を問い合わせる範囲の最初の日付(UTC の日付、どの時間帯の媒体の直近3日も含む) */
|
|
3
|
+
export declare const viewsSince: (now: number) => string;
|
|
4
|
+
/** 全媒体の問い合わせを行う Cron か(minutes は集計の間隔の分) */
|
|
5
|
+
export declare const isViewsCron: (minutes: string | undefined, cron: string) => boolean;
|
|
6
|
+
/** 全媒体の閲覧数の問い合わせ(対象の Cron で設定があるときだけ、なしは null) */
|
|
7
|
+
export declare const viewsSyncOf: (source: ProjectSource | null, minutes: string | undefined, cron: string, scheduledTime: number) => Readonly<{
|
|
8
|
+
source: ProjectSource;
|
|
9
|
+
since: string;
|
|
10
|
+
}> | null;
|
|
11
|
+
/** 媒体ごとの読み込み元の作成 */
|
|
12
|
+
type ViewsOf = (projectId: string) => Source | null;
|
|
13
|
+
/**
|
|
14
|
+
* Queues なしの構成の媒体ごとの読み込み元(全媒体の問い合わせ結果の行)
|
|
15
|
+
* - 結果にない媒体: 行なし
|
|
16
|
+
* - 問い合わせの失敗: 各媒体の集計の処理が失敗として記録(ログ・実行の記録)
|
|
17
|
+
*/
|
|
18
|
+
export declare function sharedViews(source: ProjectSource, since: string): Promise<ViewsOf>;
|
|
19
|
+
/** メッセージに載った行の読み込み元(なしは媒体ごとの問い合わせ live) */
|
|
20
|
+
export declare const viewsFor: (rows: readonly DailyRow[] | undefined, live: ViewsOf) => ViewsOf;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** 埋め込めるサービス */
|
|
2
|
+
export declare const EMBED_PROVIDERS: readonly ["youtube", "vimeo", "x", "instagram", "spotify", "apple-podcasts"];
|
|
3
|
+
/** 埋め込めるサービス */
|
|
4
|
+
export type EmbedProvider = (typeof EMBED_PROVIDERS)[number];
|
|
5
|
+
/** URL から決めた埋め込み(start は開始の秒) */
|
|
6
|
+
export type EmbedTarget = Readonly<{
|
|
7
|
+
provider: EmbedProvider;
|
|
8
|
+
id: string;
|
|
9
|
+
start?: number;
|
|
10
|
+
}>;
|
|
11
|
+
/** URL から埋め込み(埋め込めるサービスでなければ null) */
|
|
12
|
+
export declare function embedOfUrl(raw: string): EmbedTarget | null;
|
|
13
|
+
/** 描画の iframe の URL(サービスと ID から組み立てる、X と Instagram は埋め込みのページ) */
|
|
14
|
+
export declare function embedSrcOf(target: EmbedTarget): `https://${string}`;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Body } from './types.js';
|
|
2
|
+
/** 変換の設定 */
|
|
3
|
+
export type HtmlOptions<M> = Readonly<{
|
|
4
|
+
/** 画像の参照(src は元の URL) */
|
|
5
|
+
image(src: string, info: Readonly<{
|
|
6
|
+
alt?: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
}>): M;
|
|
10
|
+
/** ファイルの参照(href は元の URL、mime は data-mime-type) */
|
|
11
|
+
file?(href: string, info: Readonly<{
|
|
12
|
+
mime?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}>): M;
|
|
15
|
+
/** 名前のついた装飾として残す class(なしは class をすべて捨てる) */
|
|
16
|
+
styleClass?(name: string): boolean;
|
|
17
|
+
/** 元の CSS の値に対応する名前(本文の設定の宣言、なしは名前なし) */
|
|
18
|
+
nameOf?(kind: 'color' | 'background' | 'size', value: string): string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
/** 変換で捨てたもの(what は要素・属性・style の項目・class、count は回数) */
|
|
21
|
+
export type Dropped = Readonly<{
|
|
22
|
+
what: string;
|
|
23
|
+
count: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** 変換の結果 */
|
|
26
|
+
export type HtmlResult<M> = Readonly<{
|
|
27
|
+
body: Body<M>;
|
|
28
|
+
dropped: readonly Dropped[];
|
|
29
|
+
}>;
|
|
30
|
+
/** HTML を本文に(写せないものは dropped) */
|
|
31
|
+
export declare function bodyFromHtml<M>(html: string, options: HtmlOptions<M>): HtmlResult<M>;
|
|
@@ -4,8 +4,11 @@ export { DESCRIPTION_MAX, descriptionOf } from './description.js';
|
|
|
4
4
|
export { citesOf, excerptOf, linksOf, plainOf, READING_SPEED, readMinOf } from './derive.js';
|
|
5
5
|
export { CORE_COMPONENTS, coreComponentOf, type Chart, type ChartColor, type ChartDatum, type ComponentName, type ComponentProps, type CoreComponent } from './library.js';
|
|
6
6
|
export { isSafeHref } from './href.js';
|
|
7
|
+
export { bodyFromHtml, type Dropped, type HtmlOptions, type HtmlResult } from './html.js';
|
|
8
|
+
export { EMBED_PROVIDERS, embedOfUrl, embedSrcOf, type EmbedProvider, type EmbedTarget } from './embed.js';
|
|
7
9
|
export { fromMarkdown, toMarkdown, type MarkdownOptions } from './markdown.js';
|
|
8
|
-
export { checkBody, describeMarkdown, type BodyProfile } from './profile.js';
|
|
10
|
+
export { checkBody, describeMarkdown, isSafeCssValue, type BodyProfile, type NamedStyle, type NamedValue } from './profile.js';
|
|
11
|
+
export { bodyToHtml, type RenderedImage, type RenderOptions } from './render.js';
|
|
9
12
|
export { blockSchemaOf } from './schema.js';
|
|
10
13
|
export { bodyFromMarkdown, bodyToMarkdown, markdownGuide, type MarkdownResult, type MediaSyntax } from './standard.js';
|
|
11
|
-
export type
|
|
14
|
+
export { cellParagraphs, isTableCell, type Align, type Block, type Body, type Cell, type ComponentBlock, type HeadingLevel, type ImageAlign, type Inline, type InlineComponent, type Link, type List, type ListItem, type Mark, type Named, type Rich, type StandardBlock, type TableCell, type Text, type TextStyle } from './types.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { ComponentDef } from './components.js';
|
|
3
|
+
import { type EmbedProvider } from './embed.js';
|
|
3
4
|
/** グラフの項目と値 */
|
|
4
5
|
export type ChartDatum = Readonly<{
|
|
5
6
|
label: string;
|
|
@@ -63,12 +64,30 @@ export type ComponentProps<M> = Readonly<{
|
|
|
63
64
|
caption?: string;
|
|
64
65
|
}>[];
|
|
65
66
|
}>;
|
|
67
|
+
/** 外のページは href と保存のときの OGP の写し(fetchedAt は取得の日時)、中のコンテンツは ref(型の id/行の ID、題名と画像は配信のときに参照先から) */
|
|
66
68
|
'link-card': Readonly<{
|
|
67
69
|
href: `https://${string}`;
|
|
68
70
|
title: string;
|
|
69
71
|
site?: string;
|
|
70
72
|
desc?: string;
|
|
71
73
|
image?: M;
|
|
74
|
+
fetchedAt?: string;
|
|
75
|
+
}> | Readonly<{
|
|
76
|
+
ref: string;
|
|
77
|
+
title?: string;
|
|
78
|
+
site?: string;
|
|
79
|
+
desc?: string;
|
|
80
|
+
image?: M;
|
|
81
|
+
}>;
|
|
82
|
+
/** 外のサービスの埋め込み(id はサービスの中の ID、url は元の URL、start は開始の秒) */
|
|
83
|
+
embed: Readonly<{
|
|
84
|
+
provider: EmbedProvider;
|
|
85
|
+
id: string;
|
|
86
|
+
url: `https://${string}`;
|
|
87
|
+
width?: number;
|
|
88
|
+
height?: number;
|
|
89
|
+
start?: number;
|
|
90
|
+
title?: string;
|
|
72
91
|
}>;
|
|
73
92
|
links: Readonly<{
|
|
74
93
|
label?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentDef } from './components.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type Body, type HeadingLevel, type Mark } from './types.js';
|
|
3
3
|
/** 本文の設定 */
|
|
4
4
|
export type BodyProfile = Readonly<{
|
|
5
5
|
headings: readonly HeadingLevel[];
|
|
@@ -10,6 +10,17 @@ export type BodyProfile = Readonly<{
|
|
|
10
10
|
linkRels?: readonly string[];
|
|
11
11
|
/** 装飾・リンク・インラインのコンポーネントを使用できない場所 */
|
|
12
12
|
plain?: readonly ('heading' | 'tableHead')[];
|
|
13
|
+
/** リンクを別のタブで開く指定(既定は不可) */
|
|
14
|
+
linkTargets?: true;
|
|
15
|
+
/** 文字の見た目(色・背景・大きさは名前の宣言、項目がないものは使用不可、styles は名前のついた装飾の宣言) */
|
|
16
|
+
textStyle?: Readonly<{
|
|
17
|
+
color?: readonly NamedValue[];
|
|
18
|
+
background?: readonly NamedValue[];
|
|
19
|
+
size?: readonly NamedValue[];
|
|
20
|
+
styles?: readonly NamedStyle[];
|
|
21
|
+
}>;
|
|
22
|
+
/** 段落と見出しの揃え */
|
|
23
|
+
align?: true;
|
|
13
24
|
list?: Readonly<{
|
|
14
25
|
ordered: boolean;
|
|
15
26
|
nested: boolean;
|
|
@@ -19,28 +30,49 @@ export type BodyProfile = Readonly<{
|
|
|
19
30
|
multiple: boolean;
|
|
20
31
|
cite: boolean;
|
|
21
32
|
}>;
|
|
22
|
-
/** コード(componentLangs はコンポーネントで書く言語とコンポーネントの名前、例: mermaid は図、その言語のフェンスだけのコードは不可) */
|
|
33
|
+
/** コード(filename はファイル名、componentLangs はコンポーネントで書く言語とコンポーネントの名前、例: mermaid は図、その言語のフェンスだけのコードは不可) */
|
|
23
34
|
code?: Readonly<{
|
|
24
35
|
caption: boolean;
|
|
36
|
+
filename?: boolean;
|
|
25
37
|
componentLangs?: Readonly<Record<string, string>>;
|
|
26
38
|
}>;
|
|
39
|
+
/** 画像(link は画像のリンク、align は横の位置) */
|
|
27
40
|
image?: Readonly<{
|
|
28
41
|
variants?: readonly string[];
|
|
42
|
+
link?: boolean;
|
|
43
|
+
align?: boolean;
|
|
29
44
|
}>;
|
|
45
|
+
/** 表(cells は属性つきのセル: 見出しのセル・結合・揃え・複数の段落) */
|
|
30
46
|
table?: Readonly<{
|
|
31
47
|
head: 'required' | 'optional';
|
|
32
48
|
caption: 'required' | 'optional' | 'none';
|
|
49
|
+
cells?: boolean;
|
|
33
50
|
}>;
|
|
51
|
+
/** ファイル(実体の参照とリンクの文字) */
|
|
52
|
+
file?: true;
|
|
34
53
|
divider?: true;
|
|
35
54
|
html?: true;
|
|
36
55
|
/** 使用できるコンポーネントの名前(ブロック・インライン) */
|
|
37
56
|
components: readonly string[];
|
|
38
57
|
}>;
|
|
58
|
+
/** 名前のついた値の宣言(label は管理画面の表示名、value はサイトが対応を持たないときの既定の CSS の値) */
|
|
59
|
+
export type NamedValue = Readonly<{
|
|
60
|
+
name: string;
|
|
61
|
+
label: string;
|
|
62
|
+
value?: string;
|
|
63
|
+
}>;
|
|
64
|
+
/** 名前のついた装飾の宣言(見せ方はサイトが名前で決める) */
|
|
65
|
+
export type NamedStyle = Readonly<{
|
|
66
|
+
name: string;
|
|
67
|
+
label: string;
|
|
68
|
+
}>;
|
|
39
69
|
/** 本文の設定に合わない箇所(index は本文のブロックの位置、0から) */
|
|
40
70
|
type BodyProblem = Readonly<{
|
|
41
71
|
index: number;
|
|
42
72
|
message: string;
|
|
43
73
|
}>;
|
|
74
|
+
/** 元の CSS の値として描画してよいか(kind は色か大きさ) */
|
|
75
|
+
export declare const isSafeCssValue: (kind: "color" | "background" | "size", value: string) => boolean;
|
|
44
76
|
/** 本文を設定で検査(合わない箇所をすべて返却) */
|
|
45
77
|
export declare function checkBody<M>(body: Body<M>, profile: BodyProfile): BodyProblem[];
|
|
46
78
|
/** 書き手と AI への Markdown の説明(使用できるものを記法の例つきで列挙し、使用できないものは末尾) */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Body, type ComponentBlock } from './types.js';
|
|
2
|
+
/** 画像の描画の値 */
|
|
3
|
+
export type RenderedImage = Readonly<{
|
|
4
|
+
url: string;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
alt?: string;
|
|
8
|
+
}>;
|
|
9
|
+
/** 描画の設定 */
|
|
10
|
+
export type RenderOptions<M> = Readonly<{
|
|
11
|
+
/** 画像の URL と寸法(null は描画しない) */
|
|
12
|
+
image(media: M): RenderedImage | null;
|
|
13
|
+
/** ファイルの URL(なし・null は描画しない) */
|
|
14
|
+
file?(media: M): Readonly<{
|
|
15
|
+
url: string;
|
|
16
|
+
}> | null;
|
|
17
|
+
/** 名前に対応する CSS の値(undefined は元の値) */
|
|
18
|
+
named?(kind: 'color' | 'background' | 'size', name: string): string | undefined;
|
|
19
|
+
/** 名前のついた装飾の class */
|
|
20
|
+
styleClass?(name: string): string;
|
|
21
|
+
/** コンポーネントの描画(null は既定の描画) */
|
|
22
|
+
component?(block: ComponentBlock, render: (body: Body<M>) => string): string | null;
|
|
23
|
+
/** インラインのコンポーネントの描画(null は中の文字) */
|
|
24
|
+
inlineComponent?(component: string, props: Readonly<Record<string, string>>, text: string): string | null;
|
|
25
|
+
/** HTML のブロックを生のまま描画 */
|
|
26
|
+
allowHtml?: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
/** 本文を HTML に */
|
|
29
|
+
export declare function bodyToHtml<M>(body: Body<M>, options: RenderOptions<M>): string;
|