@ryusenkei/maezae 0.19.0 → 0.20.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 CHANGED
@@ -151,6 +151,9 @@ npx maezae-types --out src/cms-types.ts --check
151
151
 
152
152
  - 生成のファイル: 型ごとに `export type <PascalCase の型の id>`(`ItemOf` と同じ値の形)、全体の対応 `CmsTypes`、型の id の一覧 `TYPE_IDS`。表示名・上限・配信しない項目は含めないので、管理画面での表示名の変更ではずれにならない
153
153
  - 整形の対象から外す(`--check` は文字の一致で比べる。改行の CRLF は同じとみなす)
154
+ - 識別子でない key(`lab-note` など)は引用符で囲む(0.20.0 から)
155
+ - 終了の状態(0.20.0 から、`TYPES_EXIT_CODES`): 成功は `0`、ずれは `1`、引数と鍵の不足や CMS の拒否(4xx)は `2`、CMS に届かない(接続の失敗、5xx)ときは `3`。CMS が止まったときに検査を省くなら、`3` だけを通す
156
+ - 同じ処理を Node のスクリプトから呼ぶときは `runTypesCli`(`@ryusenkei/maezae/schema`)。戻り値が終了の状態で、例外を投げない
154
157
  - MCP の `get_schema` も同じ文を返す(`format: "typescript"`)。管理画面の「設定 → データ」の型定義のエクスポートも同じ文
155
158
 
156
159
  ## 公開のフォーム(お問い合わせ)
@@ -160,7 +163,6 @@ npx maezae-types --out src/cms-types.ts --check
160
163
  - `GET /content/v1/forms/contact`: 項目の宣言 `{ form, label, fields, labels, choices }`。`fields` は web/form の宣言のまま(`parseForm` と画面の検査にそのまま渡せる)、`labels` は項目の名前、`choices` は選択肢の表示。ご用件と上限は CMS の設定から作るので、サイトは写しを持たない
161
164
  - `POST /content/v1/forms/contact`: 本文は項目の値の JSON。受け付けは `200 { ok: true, requestId, id }`(`id` は受付の番号、転送だけの設定は `null`)、検査の失敗は `400 { ok: false, error: "invalid", problems }`(文言は web/form の `FORM_MESSAGES_JA` で引く)
162
165
  - ボット対策と送信元ごとの回数の制限はサイトの受け取りの処理で行う(web/form の `submissionHandler` の `botCheck`・`rateLimiter`、CMS への転送は `httpDestination` に API の鍵を付ける)
163
- - 今の `POST /content/v1/contacts`(`201 { id }`)は導入先が移るまで残す
164
166
 
165
167
  ## 回数の制限
166
168
 
package/dist/model.js CHANGED
@@ -1,6 +1,6 @@
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
2
  import { i as CORE_COMPONENTS, t as declaredComponentsSchema } from "./declared-Qo-mWnDd.js";
3
- import { t as typesSourceOf } from "./typesSource-BKPzvl--.js";
3
+ import { t as typesSourceOf } from "./typesSource-DKTe9a2-.js";
4
4
  import "./src-BrLwIWaT.js";
5
5
  import { z } from "zod";
6
6
  // ../model/src/asset.ts
@@ -0,0 +1,114 @@
1
+ import { t as typesSourceOf } from "./typesSource-DKTe9a2-.js";
2
+ import { t as DELIVERY_PATH } from "./client-ClY3gSSd.js";
3
+ import { readFileSync, writeFileSync } from "node:fs";
4
+ // src/schema.ts
5
+ /**
6
+ * maezae-types の終了の状態
7
+ * - drift: --check でファイルが CMS のスキーマと違う
8
+ * - failed: 引数と鍵の不足、CMS の拒否(4xx)、応答の形の誤り、ファイルの読み書きの失敗
9
+ * - unreachable: CMS に届かない(接続の失敗、5xx)
10
+ */
11
+ const TYPES_EXIT_CODES = {
12
+ ok: 0,
13
+ drift: 1,
14
+ failed: 2,
15
+ unreachable: 3
16
+ };
17
+ /** 標準出力と標準エラーの文 */
18
+ const MESSAGES = {
19
+ usage: "usage: maezae-types --out <file> [--check] [--url <delivery API URL>](API key: MAEZAE_API_KEY)",
20
+ missingUrl: "missing_url: --url か MAEZAE_DELIVERY_URL に配信 API の URL(例: https://example.com/<媒体>)",
21
+ missingKey: "missing_api_key: 環境変数 MAEZAE_API_KEY",
22
+ fetchFailed: (status, body) => `fetch_failed: ${status} ${body.slice(0, 200)}`,
23
+ invalidResponse: "invalid_response: 配信 API の /types の応答に types がない",
24
+ unreachable: (url, reason) => `cms_unreachable: ${url} ${reason}`,
25
+ drift: (file) => `schema_drift: ${file} が CMS のスキーマと違う(maezae-types --out ${file} で生成し直す)`,
26
+ upToDate: (file) => `up_to_date: ${file}`,
27
+ written: (file) => `written: ${file}`
28
+ };
29
+ /** 引数(--name value と --flag) */
30
+ function argsOf(argv) {
31
+ const value = (name) => {
32
+ const i = argv.indexOf(name);
33
+ return i >= 0 ? argv[i + 1] : void 0;
34
+ };
35
+ return {
36
+ out: value("--out"),
37
+ url: value("--url"),
38
+ check: argv.includes("--check")
39
+ };
40
+ }
41
+ /** 配信 API の /types の応答の失敗(status は HTTP の状態) */
42
+ var TypesResponseError = class extends Error {
43
+ status;
44
+ constructor(message, status) {
45
+ super(message);
46
+ this.status = status;
47
+ }
48
+ };
49
+ /**
50
+ * 配信 API の型の一覧(/types、表示名・選択肢・上限・ページの URL の形を含む。実行時に読む値もここから)
51
+ * - 例外: 2xx でなければ `fetch_failed: <状態> <本文>`、本文に types がなければ `invalid_response`、接続の失敗は fetcher の例外のまま
52
+ */
53
+ async function fetchDeliveredTypes(url, apiKey, fetcher = fetch) {
54
+ const res = await fetcher(`${url.replace(/\/+$/, "")}${DELIVERY_PATH}/types`, { headers: { authorization: `Bearer ${apiKey}` } });
55
+ if (!res.ok) throw new TypesResponseError(MESSAGES.fetchFailed(res.status, await res.text().catch(() => "")), res.status);
56
+ const types = (await res.json().catch(() => null))?.types;
57
+ if (!Array.isArray(types)) throw new TypesResponseError(MESSAGES.invalidResponse, res.status);
58
+ return types;
59
+ }
60
+ const messageOf = (e) => {
61
+ const cause = e instanceof Error && e.cause instanceof Error ? `(${e.cause.message})` : "";
62
+ return `${e instanceof Error ? e.message : String(e)}${cause}`;
63
+ };
64
+ /** 取得の失敗の終了の状態と文(接続の失敗と 5xx は unreachable) */
65
+ function fetchFailureOf(url, e) {
66
+ if (!(e instanceof TypesResponseError)) return {
67
+ code: TYPES_EXIT_CODES.unreachable,
68
+ message: MESSAGES.unreachable(url, messageOf(e))
69
+ };
70
+ return {
71
+ code: e.status >= 500 ? TYPES_EXIT_CODES.unreachable : TYPES_EXIT_CODES.failed,
72
+ message: e.message
73
+ };
74
+ }
75
+ /** 生成の結果をファイルに書くか、--check でファイルと比べる(戻り値は終了の状態) */
76
+ function writeOrCheck(out, source, check) {
77
+ if (!check) {
78
+ writeFileSync(out, source);
79
+ console.log(MESSAGES.written(out));
80
+ return TYPES_EXIT_CODES.ok;
81
+ }
82
+ let current = null;
83
+ try {
84
+ current = readFileSync(out, "utf8").replace(/\r\n/g, "\n");
85
+ } catch {
86
+ current = null;
87
+ }
88
+ if (current !== source) return console.error(MESSAGES.drift(out)), TYPES_EXIT_CODES.drift;
89
+ console.log(MESSAGES.upToDate(out));
90
+ return TYPES_EXIT_CODES.ok;
91
+ }
92
+ /** CLI の本体(戻り値は終了の状態 TYPES_EXIT_CODES、失敗は例外にせず標準エラーと終了の状態で返す) */
93
+ async function runTypesCli(argv, env, fetcher = fetch) {
94
+ const { out, url = env.MAEZAE_DELIVERY_URL, check } = argsOf(argv);
95
+ if (!out) return console.error(MESSAGES.usage), TYPES_EXIT_CODES.failed;
96
+ if (!url) return console.error(MESSAGES.missingUrl), TYPES_EXIT_CODES.failed;
97
+ const apiKey = env.MAEZAE_API_KEY;
98
+ if (!apiKey) return console.error(MESSAGES.missingKey), TYPES_EXIT_CODES.failed;
99
+ let types;
100
+ try {
101
+ types = await fetchDeliveredTypes(url, apiKey, fetcher);
102
+ } catch (e) {
103
+ const failure = fetchFailureOf(url, e);
104
+ console.error(failure.message);
105
+ return failure.code;
106
+ }
107
+ try {
108
+ return writeOrCheck(out, typesSourceOf({ types }), check);
109
+ } catch (e) {
110
+ console.error(messageOf(e));
111
+ return TYPES_EXIT_CODES.failed;
112
+ }
113
+ }
114
+ export { fetchDeliveredTypes as n, runTypesCli as r, TYPES_EXIT_CODES as t };
package/dist/schema.js CHANGED
@@ -1,56 +1 @@
1
- import { t as typesSourceOf } from "./typesSource-BKPzvl--.js";
2
- import { t as DELIVERY_PATH } from "./client-ClY3gSSd.js";
3
- import { readFileSync, writeFileSync } from "node:fs";
4
- // src/schema.ts
5
- /** 標準出力と標準エラーの文 */
6
- const MESSAGES = {
7
- usage: "usage: maezae-types --out <file> [--check] [--url <delivery API URL>](API key: MAEZAE_API_KEY)",
8
- missingUrl: "missing_url: --url か MAEZAE_DELIVERY_URL に配信 API の URL(例: https://example.com/<媒体>)",
9
- missingKey: "missing_api_key: 環境変数 MAEZAE_API_KEY",
10
- fetchFailed: (status, body) => `fetch_failed: ${status} ${body.slice(0, 200)}`,
11
- drift: (file) => `schema_drift: ${file} が CMS のスキーマと違う(maezae-types --out ${file} で生成し直す)`,
12
- upToDate: (file) => `up_to_date: ${file}`,
13
- written: (file) => `written: ${file}`
14
- };
15
- /** 引数(--name value と --flag) */
16
- function argsOf(argv) {
17
- const value = (name) => {
18
- const i = argv.indexOf(name);
19
- return i >= 0 ? argv[i + 1] : void 0;
20
- };
21
- return {
22
- out: value("--out"),
23
- url: value("--url"),
24
- check: argv.includes("--check")
25
- };
26
- }
27
- /** 配信 API の型の一覧(/types、表示名・選択肢・上限・ページの URL の形を含む。実行時に読む値もここから) */
28
- async function fetchDeliveredTypes(url, apiKey, fetcher = fetch) {
29
- const res = await fetcher(`${url.replace(/\/+$/, "")}${DELIVERY_PATH}/types`, { headers: { authorization: `Bearer ${apiKey}` } });
30
- if (!res.ok) throw new Error(MESSAGES.fetchFailed(res.status, await res.text().catch(() => "")));
31
- return (await res.json()).types;
32
- }
33
- /** CLI の本体(戻り値は終了の状態) */
34
- async function runTypesCli(argv, env, fetcher = fetch) {
35
- const { out, url = env.MAEZAE_DELIVERY_URL, check } = argsOf(argv);
36
- if (!out) return console.error(MESSAGES.usage), 1;
37
- if (!url) return console.error(MESSAGES.missingUrl), 1;
38
- const apiKey = env.MAEZAE_API_KEY;
39
- if (!apiKey) return console.error(MESSAGES.missingKey), 1;
40
- const source = typesSourceOf({ types: await fetchDeliveredTypes(url, apiKey, fetcher) });
41
- if (check) {
42
- let current = null;
43
- try {
44
- current = readFileSync(out, "utf8").replace(/\r\n/g, "\n");
45
- } catch {
46
- current = null;
47
- }
48
- if (current !== source) return console.error(MESSAGES.drift(out)), 1;
49
- console.log(MESSAGES.upToDate(out));
50
- return 0;
51
- }
52
- writeFileSync(out, source);
53
- console.log(MESSAGES.written(out));
54
- return 0;
55
- }
56
- export { fetchDeliveredTypes, runTypesCli };
1
+ export { t as TYPES_EXIT_CODES, n as fetchDeliveredTypes, r as runTypesCli } from "./schema-CUrLZWsb.js";
@@ -1,5 +1,20 @@
1
1
  import { type ContentType } from '../../model/src/index.js';
2
- /** 配信 API の型の一覧(/types、表示名・選択肢・上限・ページの URL の形を含む。実行時に読む値もここから) */
2
+ /**
3
+ * maezae-types の終了の状態
4
+ * - drift: --check でファイルが CMS のスキーマと違う
5
+ * - failed: 引数と鍵の不足、CMS の拒否(4xx)、応答の形の誤り、ファイルの読み書きの失敗
6
+ * - unreachable: CMS に届かない(接続の失敗、5xx)
7
+ */
8
+ export declare const TYPES_EXIT_CODES: {
9
+ readonly ok: 0;
10
+ readonly drift: 1;
11
+ readonly failed: 2;
12
+ readonly unreachable: 3;
13
+ };
14
+ /**
15
+ * 配信 API の型の一覧(/types、表示名・選択肢・上限・ページの URL の形を含む。実行時に読む値もここから)
16
+ * - 例外: 2xx でなければ `fetch_failed: <状態> <本文>`、本文に types がなければ `invalid_response`、接続の失敗は fetcher の例外のまま
17
+ */
3
18
  export declare function fetchDeliveredTypes(url: string, apiKey: string, fetcher?: typeof fetch): Promise<readonly ContentType[]>;
4
- /** CLI の本体(戻り値は終了の状態) */
19
+ /** CLI の本体(戻り値は終了の状態 TYPES_EXIT_CODES、失敗は例外にせず標準エラーと終了の状態で返す) */
5
20
  export declare function runTypesCli(argv: readonly string[], env: Readonly<Record<string, string | undefined>>, fetcher?: typeof fetch): Promise<number>;
package/dist/types-cli.js CHANGED
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { runTypesCli } from "./schema.js";
2
+ import { r as runTypesCli } from "./schema-CUrLZWsb.js";
3
3
  // src/types-cli.ts
4
- runTypesCli(process.argv.slice(2), process.env).then((code) => process.exit(code), (e) => {
5
- console.error(e instanceof Error ? e.message : String(e));
6
- process.exit(1);
7
- });
4
+ process.exitCode = await runTypesCli(process.argv.slice(2), process.env);
@@ -9,6 +9,8 @@ const deliveredFields = (fields) => fields.filter((f) => !f.private).map((f) =>
9
9
  const SDK = "@ryusenkei/maezae";
10
10
  /** 型の import の行(指定は JSON の文字列の形) */
11
11
  const importLine = (names, spec) => `import type { ${names.join(", ")} } from ${JSON.stringify(spec)};`;
12
+ /** プロパティの名前(識別子でない key は JSON の文字列の形) */
13
+ const propertyName = (key) => /^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key);
12
14
  const pascal = (id) => id.replace(/(^|[-_])(\w)/g, (_, __, ch) => ch.toUpperCase());
13
15
  /** 項目の1つ分の値の型(SDK の SingleValue と同じ) */
14
16
  function singleOf(f, indent) {
@@ -34,7 +36,7 @@ const valueOf = (f, indent) => {
34
36
  };
35
37
  /** 配信する項目の種類(まとまりの中を含む) */
36
38
  const kindsOf = (fields) => deliveredFields(fields).flatMap((f) => [f.type, ...f.type === "group" ? kindsOf(f.fields) : []]);
37
- const fieldsOf = (fields, indent) => fields.map((f) => `${indent}readonly ${f.key}: ${valueOf(f, indent)};`).join("\n");
39
+ const fieldsOf = (fields, indent) => fields.map((f) => `${indent}readonly ${propertyName(f.key)}: ${valueOf(f, indent)};`).join("\n");
38
40
  const typeOf = (t) => `export type ${pascal(t.id)} = Readonly<{\n id: string;\n _meta: ItemMeta;\n${fieldsOf(deliveredFields(t.fields), " ")}\n}>;`;
39
41
  /**
40
42
  * スキーマから配信 API の値の型の定義(TypeScript のモジュールの文)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryusenkei/maezae",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "maezae の導入先のサイト向けの SDK(配信 API の取得・本文の型・プレビューの署名・手元の開発の配信 API)",
5
5
  "license": "UNLICENSED",
6
6
  "author": "RYUSENKEI",