@ryusenkei/maezae 0.15.0 → 0.17.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 +57 -0
- package/dist/body.js +312 -10
- package/dist/{declared-9_jI3JXH.js → declared-Qo-mWnDd.js} +154 -15
- package/dist/delivery.js +26 -2
- package/dist/model.js +1 -1
- package/dist/types/access/src/handoff.d.ts +1 -1
- 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/errors.d.ts +5 -3
- package/dist/types/cms-contract/src/import.d.ts +32 -1
- package/dist/types/cms-contract/src/index.d.ts +5 -4
- 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/index.d.ts +1 -1
- package/dist/types/model/src/model.d.ts +8 -2
- package/dist/types/sdk/src/body.d.ts +2 -2
- package/dist/types/sdk/src/delivery/changes.d.ts +23 -0
- package/dist/types/sdk/src/delivery/client.d.ts +4 -0
- package/dist/types/sdk/src/delivery/index.d.ts +1 -0
- package/dist/types/sdk/src/delivery.d.ts +2 -2
- 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
|
@@ -91,6 +91,21 @@ function memoryDeliveryCache(maxEntries = 500) {
|
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
+
// src/delivery/changes.ts
|
|
95
|
+
/**
|
|
96
|
+
* since から more がなくなるまで読み、ページごとに apply を呼ぶ(戻り値は次回の since)
|
|
97
|
+
* - apply が成功してから次のページへ進む(失敗したら例外で止まり、保存済みの since から再開できる)
|
|
98
|
+
* - 410(保存の期間を過ぎた cursor): DeliveryError、since を null にして全件から取り直す
|
|
99
|
+
*/
|
|
100
|
+
async function readChanges(client, since, apply, { limit } = {}) {
|
|
101
|
+
let cursor = since;
|
|
102
|
+
for (;;) {
|
|
103
|
+
const page = await client.changes(cursor, limit === void 0 ? {} : { limit });
|
|
104
|
+
await apply(page);
|
|
105
|
+
cursor = page.next;
|
|
106
|
+
if (!page.more) return cursor;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
94
109
|
// src/delivery/query.ts
|
|
95
110
|
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
96
111
|
const OPS = [
|
|
@@ -211,11 +226,20 @@ function createDeliveryClient(delivery, { drafts = false, at } = {}) {
|
|
|
211
226
|
partial: res.headers.has("x-partial")
|
|
212
227
|
};
|
|
213
228
|
};
|
|
229
|
+
/**
|
|
230
|
+
* 変更の一覧の1ページ(since は前回の next、null は全件の取得から)
|
|
231
|
+
* - 410(保存の期間を過ぎた cursor)は DeliveryError、since なしで取り直す
|
|
232
|
+
*/
|
|
233
|
+
const changes = (since, { limit } = {}) => get("/changes", {
|
|
234
|
+
...since ? { since } : {},
|
|
235
|
+
...limit !== void 0 ? { limit: String(limit) } : {}
|
|
236
|
+
});
|
|
214
237
|
return {
|
|
215
238
|
find,
|
|
216
239
|
get,
|
|
217
240
|
page,
|
|
218
|
-
prefetch
|
|
241
|
+
prefetch,
|
|
242
|
+
changes
|
|
219
243
|
};
|
|
220
244
|
}
|
|
221
245
|
// ../model/src/mediaUrl.ts
|
|
@@ -408,4 +432,4 @@ function createDeliveryClock(timeZone) {
|
|
|
408
432
|
})
|
|
409
433
|
};
|
|
410
434
|
}
|
|
411
|
-
export { DELIVERY_PATH, DeliveryError, cachedDelivery, createDeliveryClient, createDeliveryClock, deliveredImageUrl, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, memoryDeliveryCache, optionLabels, pagePath, parseMediaPath, subPath, withImageParams };
|
|
435
|
+
export { DELIVERY_PATH, DeliveryError, cachedDelivery, createDeliveryClient, createDeliveryClock, deliveredImageUrl, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, memoryDeliveryCache, optionLabels, pagePath, parseMediaPath, readChanges, 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
|
|
@@ -24,6 +24,6 @@ export type HandoffLoginOptions = Readonly<{
|
|
|
24
24
|
export declare function handoffLogin({ secret, loginUrl, consume, basePath, sessionTtlSec }: HandoffLoginOptions): LoginAuth;
|
|
25
25
|
/**
|
|
26
26
|
* 2つのログインを順に試す(emailOf は first が null なら second、入り口とログアウトは first)
|
|
27
|
-
* - 用途:
|
|
27
|
+
* - 用途: 引き継ぎのログインに加えて、運営者のログイン(apps/api の operator.ts)を受け付ける
|
|
28
28
|
*/
|
|
29
29
|
export declare function eitherLogin(first: LoginAuth, second: LoginAuth): LoginAuth;
|
|
@@ -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;
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
/** インラインの装飾(strong・em・code・s・mark・sup・sub) */
|
|
2
|
-
export type Mark = 'strong' | 'em' | 'code' | 'strike' | 'mark' | 'sup' | 'sub';
|
|
3
|
-
/** リンク(HTML の a の href・title・rel
|
|
1
|
+
/** インラインの装飾(strong・em・code・s・mark・sup・sub・u) */
|
|
2
|
+
export type Mark = 'strong' | 'em' | 'code' | 'strike' | 'mark' | 'sup' | 'sub' | 'underline';
|
|
3
|
+
/** リンク(HTML の a の href・title・rel、target は別のタブ) */
|
|
4
4
|
export type Link = Readonly<{
|
|
5
5
|
href: string;
|
|
6
6
|
title?: string;
|
|
7
7
|
rel?: string;
|
|
8
|
+
target?: '_blank';
|
|
8
9
|
}>;
|
|
9
|
-
/**
|
|
10
|
+
/** 名前のついた値(name は本文の設定で宣言した名前、value は元の CSS の値、どちらか1つ以上) */
|
|
11
|
+
export type Named = Readonly<{
|
|
12
|
+
name?: string;
|
|
13
|
+
value?: string;
|
|
14
|
+
}>;
|
|
15
|
+
/** 文字の見た目(styles は本文の設定で宣言した名前のついた装飾、例: microCMS のカスタム class) */
|
|
16
|
+
export type TextStyle = Readonly<{
|
|
17
|
+
color?: Named;
|
|
18
|
+
background?: Named;
|
|
19
|
+
size?: Named;
|
|
20
|
+
styles?: readonly string[];
|
|
21
|
+
}>;
|
|
22
|
+
/** 装飾・リンク・見た目つきの文字列 */
|
|
10
23
|
export type Text = Readonly<{
|
|
11
24
|
text: string;
|
|
12
25
|
marks?: readonly Mark[];
|
|
13
26
|
link?: Link;
|
|
27
|
+
style?: TextStyle;
|
|
14
28
|
}>;
|
|
15
29
|
/** インラインのコンポーネント(例: 注、属性は文字列だけ、text は中の文字) */
|
|
16
30
|
export type InlineComponent = Readonly<{
|
|
@@ -24,6 +38,20 @@ export type Inline = string | Text | InlineComponent;
|
|
|
24
38
|
export type Rich = readonly Inline[];
|
|
25
39
|
/** 見出しの段階 */
|
|
26
40
|
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
41
|
+
/** 揃え(段落・見出し・表のセル) */
|
|
42
|
+
export type Align = 'left' | 'center' | 'right' | 'justify';
|
|
43
|
+
/** 画像の横の位置 */
|
|
44
|
+
export type ImageAlign = 'left' | 'center' | 'right';
|
|
45
|
+
/** 属性つきの表のセル(content はセルの中の段落、header は見出しのセル、colspan・rowspan は結合) */
|
|
46
|
+
export type TableCell = Readonly<{
|
|
47
|
+
content: readonly Rich[];
|
|
48
|
+
header?: boolean;
|
|
49
|
+
colspan?: number;
|
|
50
|
+
rowspan?: number;
|
|
51
|
+
align?: Align;
|
|
52
|
+
}>;
|
|
53
|
+
/** 表のセル(断片の列だけのセルか、属性つきのセル) */
|
|
54
|
+
export type Cell = Rich | TableCell;
|
|
27
55
|
/** リストの1項目(children は入れ子のリスト) */
|
|
28
56
|
export type ListItem = Readonly<{
|
|
29
57
|
content: Rich;
|
|
@@ -40,12 +68,14 @@ export type List = Readonly<{
|
|
|
40
68
|
export type StandardBlock<M> = Readonly<{
|
|
41
69
|
type: 'paragraph';
|
|
42
70
|
content: Rich;
|
|
71
|
+
align?: Align;
|
|
43
72
|
variant?: string;
|
|
44
73
|
}> | Readonly<{
|
|
45
74
|
type: 'heading';
|
|
46
75
|
level: HeadingLevel;
|
|
47
76
|
content: Rich;
|
|
48
77
|
id?: string;
|
|
78
|
+
align?: Align;
|
|
49
79
|
variant?: string;
|
|
50
80
|
}> | List | Readonly<{
|
|
51
81
|
type: 'quote';
|
|
@@ -60,19 +90,26 @@ export type StandardBlock<M> = Readonly<{
|
|
|
60
90
|
code: string;
|
|
61
91
|
lang?: string;
|
|
62
92
|
caption?: string;
|
|
93
|
+
filename?: string;
|
|
63
94
|
variant?: string;
|
|
64
95
|
}> | Readonly<{
|
|
65
96
|
type: 'image';
|
|
66
97
|
media: M;
|
|
67
98
|
alt?: string;
|
|
68
99
|
caption?: string;
|
|
100
|
+
link?: Link;
|
|
101
|
+
align?: ImageAlign;
|
|
69
102
|
variant?: string;
|
|
70
103
|
}> | Readonly<{
|
|
71
104
|
type: 'table';
|
|
72
105
|
head?: readonly Rich[];
|
|
73
|
-
rows: readonly (readonly
|
|
106
|
+
rows: readonly (readonly Cell[])[];
|
|
74
107
|
caption?: string;
|
|
75
108
|
variant?: string;
|
|
109
|
+
}> | Readonly<{
|
|
110
|
+
type: 'file';
|
|
111
|
+
media: M;
|
|
112
|
+
label?: string;
|
|
76
113
|
}> | Readonly<{
|
|
77
114
|
type: 'divider';
|
|
78
115
|
}> | Readonly<{
|
|
@@ -89,3 +126,7 @@ export type ComponentBlock = Readonly<{
|
|
|
89
126
|
export type Block<M> = StandardBlock<M> | ComponentBlock;
|
|
90
127
|
/** 本文 */
|
|
91
128
|
export type Body<M> = readonly Block<M>[];
|
|
129
|
+
/** 属性つきのセルか */
|
|
130
|
+
export declare const isTableCell: (cell: Cell) => cell is TableCell;
|
|
131
|
+
/** セルの中の段落(断片の列だけのセルは1段落) */
|
|
132
|
+
export declare const cellParagraphs: (cell: Cell) => readonly Rich[];
|