@ryusenkei/maezae 0.1.0 → 0.3.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.
Files changed (94) hide show
  1. package/README.md +14 -0
  2. package/dist/analytics.d.ts +1 -0
  3. package/dist/analytics.js +33 -0
  4. package/dist/body.d.ts +1 -24
  5. package/dist/body.js +40 -238
  6. package/dist/declared-9_jI3JXH.js +336 -0
  7. package/dist/delivery.d.ts +1 -211
  8. package/dist/integrations.d.ts +1 -0
  9. package/dist/integrations.js +16 -0
  10. package/dist/model.d.ts +1 -2
  11. package/dist/model.js +676 -1
  12. package/dist/preview.d.ts +1 -44
  13. package/dist/preview.js +1 -103
  14. package/dist/src-Baj-EC2G.js +205 -0
  15. package/dist/src-BrLwIWaT.js +7 -0
  16. package/dist/trace.d.ts +1 -0
  17. package/dist/trace.js +1 -0
  18. package/dist/types/access/src/index.d.ts +5 -0
  19. package/dist/types/access/src/jwt.d.ts +42 -0
  20. package/dist/types/access/src/oidc.d.ts +47 -0
  21. package/dist/types/access/src/signed.d.ts +47 -0
  22. package/dist/types/access/src/span.d.ts +23 -0
  23. package/dist/types/access/src/traceparent.d.ts +21 -0
  24. package/dist/types/analytics/src/day.d.ts +2 -0
  25. package/dist/types/analytics/src/index.d.ts +4 -0
  26. package/dist/types/analytics/src/record.d.ts +22 -0
  27. package/dist/types/analytics/src/schedule.d.ts +2 -0
  28. package/dist/types/analytics/src/source.d.ts +33 -0
  29. package/dist/types/analytics/src/store.d.ts +24 -0
  30. package/dist/types/body/src/components.d.ts +25 -0
  31. package/dist/types/body/src/declared.d.ts +34 -0
  32. package/dist/types/body/src/derive.d.ts +13 -0
  33. package/dist/types/body/src/description.d.ts +8 -0
  34. package/dist/types/body/src/href.d.ts +4 -0
  35. package/dist/types/body/src/index.d.ts +11 -0
  36. package/dist/types/body/src/library.d.ts +94 -0
  37. package/dist/types/body/src/markdown.d.ts +14 -0
  38. package/dist/types/body/src/normalize.d.ts +7 -0
  39. package/dist/types/body/src/profile.d.ts +48 -0
  40. package/dist/types/body/src/schema.d.ts +6 -0
  41. package/dist/types/body/src/standard.d.ts +20 -0
  42. package/dist/types/body/src/types.d.ts +91 -0
  43. package/dist/types/cms-contract/src/apiKeys.d.ts +13 -0
  44. package/dist/types/cms-contract/src/assets.d.ts +10 -0
  45. package/dist/types/cms-contract/src/builtins.d.ts +17 -0
  46. package/dist/types/cms-contract/src/embeds.d.ts +56 -0
  47. package/dist/types/cms-contract/src/errorLabels.d.ts +22 -0
  48. package/dist/types/cms-contract/src/errors.d.ts +97 -0
  49. package/dist/types/cms-contract/src/extensions.d.ts +75 -0
  50. package/dist/types/cms-contract/src/fieldRules.d.ts +37 -0
  51. package/dist/types/cms-contract/src/functions.d.ts +71 -0
  52. package/dist/types/cms-contract/src/index.d.ts +19 -0
  53. package/dist/types/cms-contract/src/labels.d.ts +6 -0
  54. package/dist/types/cms-contract/src/modules.d.ts +144 -0
  55. package/dist/types/cms-contract/src/paths.d.ts +6 -0
  56. package/dist/types/cms-contract/src/pending.d.ts +4 -0
  57. package/dist/types/cms-contract/src/permissions.d.ts +69 -0
  58. package/dist/types/cms-contract/src/roles.d.ts +35 -0
  59. package/dist/types/cms-contract/src/settings.d.ts +19 -0
  60. package/dist/types/cms-contract/src/transfer.d.ts +48 -0
  61. package/dist/types/cms-contract/src/views.d.ts +21 -0
  62. package/dist/types/cms-contract/src/webhooks.d.ts +45 -0
  63. package/dist/types/delivery-client/src/client.d.ts +62 -0
  64. package/dist/types/delivery-client/src/index.d.ts +5 -0
  65. package/dist/types/delivery-client/src/media.d.ts +12 -0
  66. package/dist/types/delivery-client/src/query.d.ts +68 -0
  67. package/dist/types/delivery-client/src/schema.d.ts +19 -0
  68. package/dist/types/delivery-client/src/values.d.ts +63 -0
  69. package/dist/types/model/src/asset.d.ts +89 -0
  70. package/dist/types/model/src/capabilities.d.ts +20 -0
  71. package/dist/types/model/src/changes.d.ts +23 -0
  72. package/dist/types/model/src/check.d.ts +10 -0
  73. package/dist/types/model/src/image.d.ts +38 -0
  74. package/dist/types/model/src/index.d.ts +14 -0
  75. package/dist/types/model/src/input.d.ts +26 -0
  76. package/dist/types/model/src/media.d.ts +5 -0
  77. package/dist/types/model/src/model.d.ts +190 -0
  78. package/dist/types/model/src/page.d.ts +47 -0
  79. package/dist/types/model/src/rules.d.ts +15 -0
  80. package/dist/types/model/src/schema.d.ts +3 -0
  81. package/dist/types/model/src/screens.d.ts +72 -0
  82. package/dist/types/model/src/time.d.ts +25 -0
  83. package/dist/types/model/src/values.d.ts +24 -0
  84. package/dist/types/sdk/src/analytics.d.ts +2 -0
  85. package/dist/types/sdk/src/body.d.ts +2 -0
  86. package/dist/types/sdk/src/defaults.d.ts +5 -0
  87. package/dist/types/sdk/src/delivery.d.ts +2 -0
  88. package/dist/types/sdk/src/integrations.d.ts +4 -0
  89. package/dist/types/sdk/src/model.d.ts +5 -0
  90. package/dist/types/sdk/src/preview.d.ts +2 -0
  91. package/dist/types/sdk/src/trace.d.ts +2 -0
  92. package/package.json +14 -2
  93. package/dist/index-57nirXxn.d.ts +0 -335
  94. package/dist/index-D11fUsoi.d.ts +0 -131
package/dist/model.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { a as coverPosition, c as croppedHotspot, d as createClock, f as isDateOnly, i as typeOf, l as objectPosition, n as publicPathOf, o as coverStart, s as cropRect, t as pagePathOf, u as stillOf } from "./page-UEKb7_gu.js";
2
+ import { i as CORE_COMPONENTS, t as declaredComponentsSchema } from "./declared-9_jI3JXH.js";
3
+ import "./src-BrLwIWaT.js";
2
4
  import { z } from "zod";
3
5
  // ../model/src/asset.ts
4
6
  const unit = z.number().min(0).max(1);
@@ -28,4 +30,677 @@ const mediaSchema = z.union([mediaBase.extend({ kind: z.literal("image") }), med
28
30
  poster: mediaBase
29
31
  })]);
30
32
  z.object({ asset: z.string().min(1) });
31
- export { coverPosition, coverStart, createClock, cropRect, croppedHotspot, isDateOnly, mediaSchema, objectPosition, pagePathOf, publicPathOf, stillOf, typeOf };
33
+ // ../model/src/media.ts
34
+ const file = [
35
+ {
36
+ key: "mime_type",
37
+ label: "形式",
38
+ type: "text",
39
+ required: "save",
40
+ description: "MIME タイプ"
41
+ },
42
+ {
43
+ key: "size",
44
+ label: "サイズ",
45
+ type: "number",
46
+ required: "save",
47
+ description: "バイト数"
48
+ },
49
+ {
50
+ key: "width",
51
+ label: "幅",
52
+ type: "number",
53
+ required: "save",
54
+ description: "px"
55
+ },
56
+ {
57
+ key: "height",
58
+ label: "高さ",
59
+ type: "number",
60
+ required: "save",
61
+ description: "px"
62
+ },
63
+ {
64
+ key: "alt",
65
+ label: "代替テキスト",
66
+ type: "text",
67
+ description: "使用する側の既定の値、使用する場所ごとに上書き可"
68
+ }
69
+ ];
70
+ /** 画像の型 */
71
+ const IMAGE_TYPE = {
72
+ id: "image",
73
+ label: "画像",
74
+ description: "見せたい点(hotspot)と切り抜き(crop)を画像ごとに1つ保持、切り抜く場所すべてで共通",
75
+ core: file,
76
+ fields: []
77
+ };
78
+ /** 動画の型(配信と再生は最初に使用する場所が決まったときに実装) */
79
+ const VIDEO_TYPE = {
80
+ id: "video",
81
+ label: "動画",
82
+ core: [
83
+ ...file,
84
+ {
85
+ key: "duration",
86
+ label: "長さ",
87
+ type: "number",
88
+ description: "秒"
89
+ },
90
+ {
91
+ key: "poster",
92
+ label: "ポスター画像",
93
+ type: "media",
94
+ accept: ["image"],
95
+ description: "読み込み前・動きを抑える設定・OG で代わりに表示"
96
+ }
97
+ ],
98
+ fields: []
99
+ };
100
+ // ../model/src/capabilities.ts
101
+ const NONE = {
102
+ editorial: false,
103
+ derived: false,
104
+ sources: false,
105
+ shared: false
106
+ };
107
+ const CORE_TABLES = {
108
+ articles: {
109
+ ...NONE,
110
+ editorial: true,
111
+ derived: true,
112
+ sources: true
113
+ },
114
+ sources: {
115
+ ...NONE,
116
+ shared: true
117
+ }
118
+ };
119
+ /** 表の性質(コアの表にない表はすべてなし) */
120
+ const tableTraitsOf = (table) => table && Object.hasOwn(CORE_TABLES, table) ? CORE_TABLES[table] : NONE;
121
+ // ../model/src/schema.ts
122
+ const key = z.string().regex(/^[a-z][a-z0-9_]*$/, "id は英小文字で始まり、英小文字・数字・アンダーラインだけ");
123
+ const fieldKey = z.string().regex(/^[a-z][a-zA-Z0-9_-]*$/, "key は英小文字で始まり、英字・数字・アンダーライン・ハイフンだけ");
124
+ const condition = z.object({
125
+ key: fieldKey,
126
+ in: z.array(z.string().min(1)).min(1)
127
+ });
128
+ const base = {
129
+ key: fieldKey,
130
+ label: z.string().min(1),
131
+ description: z.string().optional(),
132
+ required: z.union([
133
+ z.literal("save"),
134
+ z.literal("submit"),
135
+ z.literal("page"),
136
+ z.literal(false)
137
+ ]).optional(),
138
+ when: condition.optional(),
139
+ private: z.boolean().optional(),
140
+ adminOnly: z.boolean().optional(),
141
+ pendingUntil: fieldKey.optional()
142
+ };
143
+ const frame = z.object({
144
+ when: condition.optional(),
145
+ ratio: z.string().min(1),
146
+ size: z.object({
147
+ width: z.number().int().positive(),
148
+ height: z.number().int().positive()
149
+ }),
150
+ label: z.string().min(1)
151
+ });
152
+ const pageRule = z.union([
153
+ z.object({
154
+ key: z.string().min(1),
155
+ in: z.array(z.string())
156
+ }),
157
+ z.object({
158
+ key: z.string().min(1),
159
+ notIn: z.array(z.string())
160
+ }),
161
+ z.object({
162
+ key: z.string().min(1),
163
+ truthy: z.literal(true)
164
+ }),
165
+ z.object({
166
+ key: z.string().min(1),
167
+ notFuture: z.literal(true)
168
+ })
169
+ ]);
170
+ const page = z.object({
171
+ when: condition.optional(),
172
+ path: z.string().startsWith("/", "ページの雛形は / で始まる"),
173
+ public: z.array(pageRule).optional()
174
+ });
175
+ const field = z.lazy(() => fieldUnion);
176
+ const counts = {
177
+ minItems: z.number().int().nonnegative().optional(),
178
+ maxItems: z.number().int().positive().optional()
179
+ };
180
+ const regex = z.string().refine((p) => {
181
+ try {
182
+ new RegExp(p, "u");
183
+ return true;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }, "形式の正規表現が不正");
188
+ const fieldUnion = z.discriminatedUnion("type", [
189
+ z.object({
190
+ ...base,
191
+ ...counts,
192
+ type: z.enum(["text", "textarea"]),
193
+ maxLength: z.number().int().positive().optional(),
194
+ minLength: z.number().int().positive().optional(),
195
+ pattern: regex.optional(),
196
+ guideLength: z.number().int().positive().optional(),
197
+ multiple: z.boolean().optional()
198
+ }),
199
+ z.object({
200
+ ...base,
201
+ type: z.literal("number"),
202
+ min: z.number().optional(),
203
+ max: z.number().optional()
204
+ }),
205
+ z.object({
206
+ ...base,
207
+ type: z.literal("checkbox")
208
+ }),
209
+ z.object({
210
+ ...base,
211
+ type: z.enum(["date", "datetime"])
212
+ }),
213
+ z.object({
214
+ ...base,
215
+ ...counts,
216
+ type: z.literal("select"),
217
+ options: z.array(z.object({
218
+ value: z.string().min(1),
219
+ label: z.string().min(1),
220
+ tone: z.enum([
221
+ "positive",
222
+ "caution",
223
+ "neutral"
224
+ ]).optional(),
225
+ restricted: z.boolean().optional()
226
+ })).min(1),
227
+ initial: z.string().optional(),
228
+ multiple: z.boolean().optional(),
229
+ derive: z.object({
230
+ from: fieldKey,
231
+ map: z.record(z.string(), z.string()),
232
+ otherwise: z.string()
233
+ }).optional(),
234
+ fixedBy: z.object({
235
+ ref: fieldKey,
236
+ key: fieldKey,
237
+ map: z.record(z.string(), z.string())
238
+ }).optional()
239
+ }).refine((f) => f.initial === void 0 || f.options.some((o) => o.value === f.initial), {
240
+ message: "初期値が選択肢にない",
241
+ path: ["initial"]
242
+ }).refine((f) => !f.derive || [...Object.values(f.derive.map), f.derive.otherwise].every((v) => f.options.some((o) => o.value === v)), {
243
+ message: "決まる値が選択肢にない",
244
+ path: ["derive"]
245
+ }).refine((f) => !f.fixedBy || Object.values(f.fixedBy.map).every((v) => f.options.some((o) => o.value === v)), {
246
+ message: "固定する値が選択肢にない",
247
+ path: ["fixedBy"]
248
+ }),
249
+ z.object({
250
+ ...base,
251
+ ...counts,
252
+ type: z.literal("reference"),
253
+ to: z.string().min(1),
254
+ multiple: z.boolean().optional(),
255
+ where: z.record(z.string(), z.string()).optional(),
256
+ requests: z.boolean().optional(),
257
+ emptyLabel: z.string().min(1).optional(),
258
+ unique: z.boolean().optional(),
259
+ published: z.boolean().optional(),
260
+ linkedBy: fieldKey.optional()
261
+ }),
262
+ z.object({
263
+ ...base,
264
+ ...counts,
265
+ type: z.literal("media"),
266
+ accept: z.array(z.enum(["image", "video"])).optional(),
267
+ multiple: z.boolean().optional(),
268
+ frames: z.array(frame).optional()
269
+ }),
270
+ z.object({
271
+ ...base,
272
+ type: z.literal("body"),
273
+ profile: z.object({
274
+ headings: z.array(z.number()),
275
+ marks: z.array(z.string()),
276
+ components: z.array(z.string())
277
+ }).passthrough()
278
+ }),
279
+ z.object({
280
+ ...base,
281
+ ...counts,
282
+ type: z.literal("group"),
283
+ fields: z.array(field),
284
+ multiple: z.boolean().optional()
285
+ })
286
+ ]);
287
+ /** 条件が一覧にある選択肢の項目と値を指すか */
288
+ const conditionValid = (when, fields) => {
289
+ const target = when && fields.find((g) => g.key === when.key && g.type === "select");
290
+ return !when || target !== void 0 && when.in.every((v) => target.options.some((o) => o.value === v));
291
+ };
292
+ /** 条件が同じ階層の選択肢の項目と値を指すか(まとまりの中も) */
293
+ const conditionsValid = (fields) => fields.every((f) => conditionValid(f.when, fields) && (!f.derive || fields.some((g) => g.key === f.derive.from && g.type === "select")) && (!f.fixedBy || fields.some((g) => g.key === f.fixedBy.ref && g.type === "reference" && !g.multiple)) && (f.frames ?? []).every((fr) => conditionValid(fr.when, fields)) && conditionsValid(f.fields ?? []));
294
+ /** まとまりの中で決まる値・参照先で固定を持つ項目の key(宣言の適用 rules.ts が最上位の項目だけのため) */
295
+ const nestedRules = (fields) => fields.flatMap((f) => (f.fields ?? []).flatMap((c) => [...c.derive || c.fixedBy ? [`${f.key}.${c.key}`] : [], ...nestedRules([c]).map((k) => `${f.key}.${k}`)]));
296
+ /**
297
+ * システムの値と同じ名前で、項目の key に使用できない名前(API・配信が項目の値を上書きするため)
298
+ * - すべての型: 行の ID・作成と更新の日時・作成者、管理画面の一覧で参照する行の数 uses
299
+ * - 公開の管理がある型と、コアの表を持つ型の追加の項目: 編集の機能の列
300
+ * - 本文から作成する列を持つ表の型: excerpt・read_min・links
301
+ * - 対象外: API・配信の値に出ない列 type、導入先の設定で決まるアカウントの email・account_role
302
+ */
303
+ const RESERVED = [
304
+ "id",
305
+ "created_at",
306
+ "created_by",
307
+ "updated_at",
308
+ "uses"
309
+ ];
310
+ const RESERVED_EDITORIAL = [
311
+ "status",
312
+ "published_at",
313
+ "visibility",
314
+ "version",
315
+ "review_note",
316
+ "assignee"
317
+ ];
318
+ const RESERVED_DERIVED = [
319
+ "excerpt",
320
+ "read_min",
321
+ "links"
322
+ ];
323
+ /** 予約の名前と同じ最上位の key(コアの項目は表の列そのものなので、編集の機能の列は対象外) */
324
+ function reservedKeysOf(t) {
325
+ const derived = tableTraitsOf(t.table).derived ? RESERVED_DERIVED : [];
326
+ const editorial = (t.publishing ?? "none") !== "none" || t.table ? RESERVED_EDITORIAL : [];
327
+ const core = t.core ?? [];
328
+ const extra = [...t.fields.map((f) => f.key), ...(t.counts ?? []).map((c) => c.key)];
329
+ return [...core.map((f) => f.key).filter((k) => [...RESERVED, ...derived].includes(k)), ...extra.filter((k) => [
330
+ ...RESERVED,
331
+ ...derived,
332
+ ...editorial
333
+ ].includes(k))];
334
+ }
335
+ /** 本文の項目が使用する部品の名前(まとまりの中も、key は項目の key) */
336
+ const bodyComponents = (fields) => fields.flatMap((f) => f.type === "body" ? f.profile.components.map((name) => ({
337
+ key: f.key,
338
+ name
339
+ })) : bodyComponents(f.fields ?? []));
340
+ /** 参照の項目の参照先(まとまりの中も) */
341
+ const referenceTargets = (fields) => fields.flatMap((f) => f.type === "reference" ? [f.to] : referenceTargets(f.fields ?? []));
342
+ const contentType = z.object({
343
+ id: key,
344
+ label: z.string().min(1),
345
+ description: z.string().optional(),
346
+ format: z.enum(["list", "object"]).optional(),
347
+ publishing: z.enum([
348
+ "none",
349
+ "draft",
350
+ "review"
351
+ ]).optional(),
352
+ table: key.optional(),
353
+ core: z.array(field).optional(),
354
+ fields: z.array(field),
355
+ pages: z.array(page).optional(),
356
+ counts: z.array(z.object({
357
+ key: fieldKey,
358
+ from: key,
359
+ field: fieldKey
360
+ })).optional(),
361
+ og: z.string().startsWith("/", "OG 画像の雛形は / で始まる").optional()
362
+ }).refine((t) => t.format !== "object" || !t.core?.length, { message: "オブジェクト形式の型はコアの項目なし" }).refine((t) => new Set([...t.core ?? [], ...t.fields].map((f) => f.key)).size === (t.core?.length ?? 0) + t.fields.length, { message: "項目の key が重複(コアの項目を含む)" }).refine((t) => conditionsValid([...t.core ?? [], ...t.fields]), { message: "項目の条件が同じ階層の選択肢の項目と値を指していない" }).refine((t) => (t.pages ?? []).every((p) => conditionValid(p.when, [...t.core ?? [], ...t.fields])), { message: "ページの条件が型の選択肢の項目と値を指していない" }).superRefine((t, ctx) => {
363
+ for (const k of reservedKeysOf(t)) ctx.addIssue({
364
+ code: "custom",
365
+ message: `型 ${t.id} の key ${k} はシステムの値と同じ名前(別の key に)`
366
+ });
367
+ for (const k of nestedRules([...t.core ?? [], ...t.fields])) ctx.addIssue({
368
+ code: "custom",
369
+ message: `型 ${t.id} の項目 ${k}: まとまりの中の項目は決まる値 derive・参照先で固定 fixedBy なし`
370
+ });
371
+ });
372
+ const modelSchema = z.object({
373
+ components: declaredComponentsSchema.optional(),
374
+ types: z.array(contentType).refine((ts) => new Set(ts.map((t) => t.id)).size === ts.length, { message: "型の id が重複" }).superRefine((ts, ctx) => {
375
+ const ids = new Set(ts.map((t) => t.id));
376
+ for (const t of ts) for (const to of referenceTargets([...t.core ?? [], ...t.fields])) if (!ids.has(to)) ctx.addIssue({
377
+ code: "custom",
378
+ message: `型 ${t.id} の参照先 ${to} がスキーマにない`
379
+ });
380
+ for (const t of ts) {
381
+ const keys = new Set([...t.core ?? [], ...t.fields].map((f) => f.key));
382
+ for (const c of t.counts ?? []) {
383
+ const from = ts.find((x) => x.id === c.from);
384
+ const ref = from && [...from.core ?? [], ...from.fields].find((f) => f.key === c.field);
385
+ if (ref?.type !== "reference" || ref.to !== t.id) ctx.addIssue({
386
+ code: "custom",
387
+ message: `型 ${t.id} の被参照の件数 ${c.key}: 型 ${c.from} の参照の項目 ${c.field} がこの型を指していない`
388
+ });
389
+ if (keys.has(c.key)) ctx.addIssue({
390
+ code: "custom",
391
+ message: `型 ${t.id} の被参照の件数 ${c.key} が項目の key と重複`
392
+ });
393
+ }
394
+ }
395
+ })
396
+ }).superRefine((m, ctx) => {
397
+ const known = /* @__PURE__ */ new Set([...CORE_COMPONENTS.map((c) => c.name), ...(m.components ?? []).map((c) => c.name)]);
398
+ for (const t of m.types) for (const { key, name } of bodyComponents([...t.core ?? [], ...t.fields])) if (!known.has(name)) ctx.addIssue({
399
+ code: "custom",
400
+ message: `型 ${t.id} の本文の項目 ${key} の部品 ${name} がコアの部品にも宣言 components にもない`
401
+ });
402
+ });
403
+ /** コンテンツスキーマの JSON を検査して返却(形が合わなければ例外、理由は zod の issues) */
404
+ const parseModel = (json) => modelSchema.parse(json);
405
+ // ../model/src/screens.ts
406
+ /** メニューにある全項目の id(グループの中も、上から順) */
407
+ const menuItemsOf = (menu) => menu.flatMap((e) => typeof e === "string" ? [e] : e.items);
408
+ /** 編集画面に配置した項目の id(セクションの順) */
409
+ const placedItemsOf = (layout) => layout.sections.flatMap((s) => s.items);
410
+ const id = z.string().regex(/^[a-z][a-z0-9_:-]*$/, "id は英小文字で始まり、英小文字・数字・アンダーライン・コロン・ハイフンだけ");
411
+ const item = z.string().min(1);
412
+ const unique = (values) => new Set(values).size === values.length;
413
+ const group = z.object({
414
+ id,
415
+ label: z.string().trim().min(1),
416
+ collapsed: z.boolean().optional(),
417
+ items: z.array(item)
418
+ });
419
+ const section = z.object({
420
+ id,
421
+ label: z.string().trim(),
422
+ collapsed: z.boolean().optional(),
423
+ items: z.array(item)
424
+ });
425
+ const layout = z.object({
426
+ sections: z.array(section),
427
+ hidden: z.array(item).optional()
428
+ }).refine((l) => unique(l.sections.map((s) => s.id)), { message: "セクションの id が重複" }).refine((l) => unique([...placedItemsOf(l), ...l.hidden ?? []]), { message: "同じ項目を2か所に配置" });
429
+ const screensSchema = z.object({
430
+ menu: z.array(z.union([item, group])).refine((m) => unique(m.flatMap((e) => typeof e === "string" ? [] : [e.id])), { message: "メニューのグループの id が重複" }).refine((m) => unique(menuItemsOf(m)), { message: "メニューに同じ項目が2つ" }),
431
+ editors: z.record(z.string(), layout).default({}),
432
+ columns: z.record(z.string(), z.array(item).refine(unique, { message: "一覧に同じ列が2つ" })).default({})
433
+ });
434
+ /** 画面の設定の JSON を検査(形の誤りは ZodError) */
435
+ const parseScreens = (json) => screensSchema.parse(json);
436
+ // ../cms-contract/src/transfer.ts
437
+ /** スキーマと設定の形式の名前 */
438
+ const CONFIG_FORMAT = "cms-config";
439
+ // src/defaults.ts
440
+ /** 中立の既定のスキーマ(新しい媒体の作成で適用する型: post・author・tag・image・video、設定の移入の土台) */
441
+ const DEFAULT_SCHEMA = { "types": [
442
+ {
443
+ "id": "post",
444
+ "label": "投稿",
445
+ "table": "articles",
446
+ "publishing": "review",
447
+ "core": [
448
+ {
449
+ "key": "title",
450
+ "label": "タイトル",
451
+ "type": "text",
452
+ "required": "save"
453
+ },
454
+ {
455
+ "key": "writers",
456
+ "label": "著者",
457
+ "type": "reference",
458
+ "to": "author",
459
+ "multiple": true
460
+ },
461
+ {
462
+ "key": "image",
463
+ "label": "メインの画像",
464
+ "type": "media",
465
+ "accept": ["image"]
466
+ },
467
+ {
468
+ "key": "body",
469
+ "label": "本文",
470
+ "type": "body",
471
+ "profile": {
472
+ "headings": [
473
+ 2,
474
+ 3,
475
+ 4
476
+ ],
477
+ "marks": [
478
+ "strong",
479
+ "em",
480
+ "code",
481
+ "strike"
482
+ ],
483
+ "list": {
484
+ "ordered": true,
485
+ "nested": true
486
+ },
487
+ "quote": {
488
+ "multiple": true,
489
+ "cite": true
490
+ },
491
+ "code": { "caption": false },
492
+ "table": {
493
+ "head": "optional",
494
+ "caption": "optional"
495
+ },
496
+ "divider": true,
497
+ "components": ["gallery", "link-card"]
498
+ }
499
+ },
500
+ {
501
+ "key": "published_at",
502
+ "label": "公開日時",
503
+ "type": "datetime"
504
+ },
505
+ {
506
+ "key": "revised_at",
507
+ "label": "更新日時",
508
+ "type": "datetime"
509
+ }
510
+ ],
511
+ "fields": [{
512
+ "key": "tags",
513
+ "label": "タグ",
514
+ "type": "reference",
515
+ "to": "tag",
516
+ "multiple": true
517
+ }],
518
+ "pages": [{
519
+ "path": "/posts/{id}",
520
+ "public": [{
521
+ "key": "status",
522
+ "in": ["published"]
523
+ }, {
524
+ "key": "published_at",
525
+ "notFuture": true
526
+ }]
527
+ }]
528
+ },
529
+ {
530
+ "id": "author",
531
+ "label": "著者",
532
+ "fields": [
533
+ {
534
+ "key": "name",
535
+ "label": "名前",
536
+ "type": "text",
537
+ "required": "save"
538
+ },
539
+ {
540
+ "key": "bio",
541
+ "label": "紹介",
542
+ "type": "textarea"
543
+ },
544
+ {
545
+ "key": "portrait",
546
+ "label": "顔の画像",
547
+ "type": "media",
548
+ "accept": ["image"]
549
+ }
550
+ ],
551
+ "pages": [{ "path": "/authors/{id}" }]
552
+ },
553
+ {
554
+ "id": "tag",
555
+ "label": "タグ",
556
+ "fields": [{
557
+ "key": "label",
558
+ "label": "名前",
559
+ "type": "text",
560
+ "required": "save"
561
+ }]
562
+ },
563
+ {
564
+ "id": "image",
565
+ "label": "画像",
566
+ "description": "見せたい点(hotspot)と切り抜き(crop)を画像ごとに1つ保持、切り抜く場所すべてで共通",
567
+ "core": [
568
+ {
569
+ "key": "mime_type",
570
+ "label": "形式",
571
+ "type": "text",
572
+ "required": "save",
573
+ "description": "MIME タイプ"
574
+ },
575
+ {
576
+ "key": "size",
577
+ "label": "サイズ",
578
+ "type": "number",
579
+ "required": "save",
580
+ "description": "バイト数"
581
+ },
582
+ {
583
+ "key": "width",
584
+ "label": "幅",
585
+ "type": "number",
586
+ "required": "save",
587
+ "description": "px"
588
+ },
589
+ {
590
+ "key": "height",
591
+ "label": "高さ",
592
+ "type": "number",
593
+ "required": "save",
594
+ "description": "px"
595
+ },
596
+ {
597
+ "key": "alt",
598
+ "label": "代替テキスト",
599
+ "type": "text",
600
+ "description": "使用する側の既定の値、使用する場所ごとに上書き可"
601
+ }
602
+ ],
603
+ "fields": []
604
+ },
605
+ {
606
+ "id": "video",
607
+ "label": "動画",
608
+ "core": [
609
+ {
610
+ "key": "mime_type",
611
+ "label": "形式",
612
+ "type": "text",
613
+ "required": "save",
614
+ "description": "MIME タイプ"
615
+ },
616
+ {
617
+ "key": "size",
618
+ "label": "サイズ",
619
+ "type": "number",
620
+ "required": "save",
621
+ "description": "バイト数"
622
+ },
623
+ {
624
+ "key": "width",
625
+ "label": "幅",
626
+ "type": "number",
627
+ "required": "save",
628
+ "description": "px"
629
+ },
630
+ {
631
+ "key": "height",
632
+ "label": "高さ",
633
+ "type": "number",
634
+ "required": "save",
635
+ "description": "px"
636
+ },
637
+ {
638
+ "key": "alt",
639
+ "label": "代替テキスト",
640
+ "type": "text",
641
+ "description": "使用する側の既定の値、使用する場所ごとに上書き可"
642
+ },
643
+ {
644
+ "key": "duration",
645
+ "label": "長さ",
646
+ "type": "number",
647
+ "description": "秒"
648
+ },
649
+ {
650
+ "key": "poster",
651
+ "label": "ポスター画像",
652
+ "type": "media",
653
+ "accept": ["image"],
654
+ "description": "読み込み前・動きを抑える設定・OG で代わりに表示"
655
+ }
656
+ ],
657
+ "fields": []
658
+ }
659
+ ] };
660
+ /** 既定の画面設定(新しい媒体の作成で適用する中立の値、移入する screens の土台) */
661
+ const DEFAULT_SCREENS = {
662
+ "menu": [
663
+ "dashboard",
664
+ "calendar",
665
+ {
666
+ "id": "content",
667
+ "label": "コンテンツ",
668
+ "items": ["type:post", "media"]
669
+ },
670
+ {
671
+ "id": "masters",
672
+ "label": "マスタ",
673
+ "items": ["type:tag", "type:author"]
674
+ }
675
+ ],
676
+ "editors": { "post": { "sections": [
677
+ {
678
+ "id": "classify",
679
+ "label": "分類",
680
+ "items": ["writers", "tags"]
681
+ },
682
+ {
683
+ "id": "publish",
684
+ "label": "公開",
685
+ "items": ["published_at", "revised_at"]
686
+ },
687
+ {
688
+ "id": "excerpt",
689
+ "label": "説明",
690
+ "items": ["excerpt"]
691
+ },
692
+ {
693
+ "id": "image",
694
+ "label": "メインの画像",
695
+ "items": ["image"]
696
+ }
697
+ ] } },
698
+ "columns": { "post": [
699
+ "status",
700
+ "writers",
701
+ "assignee",
702
+ "published_at",
703
+ "updated_at"
704
+ ] }
705
+ };
706
+ export { CONFIG_FORMAT, DEFAULT_SCHEMA, DEFAULT_SCREENS, IMAGE_TYPE, VIDEO_TYPE, coverPosition, coverStart, createClock, cropRect, croppedHotspot, isDateOnly, mediaSchema, objectPosition, pagePathOf, parseModel, parseScreens, publicPathOf, stillOf, typeOf };