@orizm/cli 3.3.0-beta.1 → 3.3.0-beta.2
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/dist/cli/main.cjs +2 -2
- package/dist/cli/main.mjs +2 -2
- package/dist/docs/features/cms-sdk/install.md +1 -1
- package/dist/docs/features/consumer-sdk/install.md +1 -1
- package/dist/docs/features/dictionary.md +2 -2
- package/dist/docs/features/full-text-search/local-development.md +1 -1
- package/dist/docs/features/full-text-search.md +34 -4
- package/dist/docs/features/schema/database.md +4 -0
- package/dist/docs/index.md +1 -1
- package/dist/docs/manifest.json +2 -2
- package/dist/{push-BggkrD0g.cjs → push-BhpbLJSX.cjs} +31 -5
- package/dist/push-BhpbLJSX.cjs.map +1 -0
- package/dist/{push-xWnD9oov.mjs → push-DnPLP3dn.mjs} +31 -5
- package/dist/push-DnPLP3dn.mjs.map +1 -0
- package/dist/{schema-DlUWHV_t.mjs → schema-CuOgdgv_.mjs} +2 -2
- package/dist/{schema-DlUWHV_t.mjs.map → schema-CuOgdgv_.mjs.map} +1 -1
- package/dist/{schema-CUVTXrHk.cjs → schema-Da8mf7ME.cjs} +2 -2
- package/dist/{schema-CUVTXrHk.cjs.map → schema-Da8mf7ME.cjs.map} +1 -1
- package/package.json +7 -7
- package/dist/push-BggkrD0g.cjs.map +0 -1
- package/dist/push-xWnD9oov.mjs.map +0 -1
package/dist/cli/main.cjs
CHANGED
|
@@ -35,7 +35,7 @@ let valibot = require("valibot");
|
|
|
35
35
|
valibot = __toESM(valibot, 1);
|
|
36
36
|
|
|
37
37
|
//#region package.json
|
|
38
|
-
var version$1 = "3.
|
|
38
|
+
var version$1 = "3.2.0";
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/cli/version.ts
|
|
@@ -207,7 +207,7 @@ const main = defineCommand({
|
|
|
207
207
|
org: () => Promise.resolve().then(() => require("../org-Drj3VqIm.cjs")).then((m) => m.default),
|
|
208
208
|
project: () => Promise.resolve().then(() => require("../project-Dm7nr5NV.cjs")).then((m) => m.default),
|
|
209
209
|
codegen: () => Promise.resolve().then(() => require("../codegen-Cypg4O3L.cjs")).then((m) => m.default),
|
|
210
|
-
schema: () => Promise.resolve().then(() => require("../schema-
|
|
210
|
+
schema: () => Promise.resolve().then(() => require("../schema-Da8mf7ME.cjs")).then((m) => m.default),
|
|
211
211
|
key: () => Promise.resolve().then(() => require("../key-tRYRqYNo.cjs")).then((m) => m.default),
|
|
212
212
|
"project-user": () => Promise.resolve().then(() => require("../project-user-wLFnHFwB.cjs")).then((m) => m.default),
|
|
213
213
|
webhook: () => Promise.resolve().then(() => require("../webhook-CQeuAqec.cjs")).then((m) => m.default),
|
package/dist/cli/main.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { colors } from "consola/utils";
|
|
|
6
6
|
import * as v from "valibot";
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
|
-
var version$1 = "3.
|
|
9
|
+
var version$1 = "3.2.0";
|
|
10
10
|
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/cli/version.ts
|
|
@@ -178,7 +178,7 @@ const main = defineCommand$1({
|
|
|
178
178
|
org: () => import("../org-Co-rjfr-.mjs").then((m) => m.default),
|
|
179
179
|
project: () => import("../project-C7qWFjMV.mjs").then((m) => m.default),
|
|
180
180
|
codegen: () => import("../codegen-Dnjv7zfm.mjs").then((m) => m.default),
|
|
181
|
-
schema: () => import("../schema-
|
|
181
|
+
schema: () => import("../schema-CuOgdgv_.mjs").then((m) => m.default),
|
|
182
182
|
key: () => import("../key-CyVxu3Uk.mjs").then((m) => m.default),
|
|
183
183
|
"project-user": () => import("../project-user-D9h_QBc0.mjs").then((m) => m.default),
|
|
184
184
|
webhook: () => import("../webhook-BT3cHrq5.mjs").then((m) => m.default),
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# CMS SDKのインストール
|
|
4
4
|
|
|
5
5
|
> [!NOTE]
|
|
6
|
-
> このドキュメントは `@orizm/cms-sdk` の **v2.
|
|
6
|
+
> このドキュメントは `@orizm/cms-sdk` の **v2.5.0**
|
|
7
7
|
> に対応しています。プロジェクトにインストールされているバージョンが異なる場合、記載内容と実際の挙動が一致しないことがあります。
|
|
8
8
|
|
|
9
9
|
CMS SDKは、OrizmのCMS機能をWebサイトに組み込むためのJavaScriptライブラリです。このガイドでは、インストール手順とクライアントセットアップの方法を説明します。
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Consumer SDKのインストール
|
|
4
4
|
|
|
5
5
|
> [!NOTE]
|
|
6
|
-
> このドキュメントは `@orizm/consumer-sdk` の **v2.
|
|
6
|
+
> このドキュメントは `@orizm/consumer-sdk` の **v2.5.0**
|
|
7
7
|
> に対応しています。プロジェクトにインストールされているバージョンが異なる場合、記載内容と実際の挙動が一致しないことがあります。
|
|
8
8
|
|
|
9
9
|
Consumer SDKはOrizmのデータにアクセスするためのJavaScriptライブラリです。WebサイトやアプリケーションからOrizmのコンテンツを取得する際に使用します。
|
|
@@ -118,12 +118,12 @@ console.log(result._dictionary); // 辞書のヒット(最大 3 件)
|
|
|
118
118
|
|
|
119
119
|
辞書のヒットは `contents` とは別の枠で返り、`totalCount` にも含まれません。ページングもせず、`offset` を動かしても同じ内容が返ります。
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
辞書のヒットには近さの下限があり、クエリと関連の薄いエントリは `_dictionary` に含まれません。そのため説明付きの有効なエントリが 3 件以上あっても、辞書と関係の薄いクエリでは 0〜2 件になります。下限の値は埋め込みモデルに合わせて Orizm 側で持っており、呼び出し側から変えることはできません。返ってきたものをさらに絞りたい場合は `score` を見て呼び出し側で決めてください(`score` の目安になる値は埋め込みモデルによって変わります)。
|
|
122
122
|
|
|
123
123
|
`_dictionary` は状態によって 3 通りになります。
|
|
124
124
|
|
|
125
125
|
- **キーが無い**: `keyword` 検索、キーワード検索への劣化時、有効な説明付きのエントリが 1 つも無いプロジェクト(反映待ちの間を含む)、辞書の検索が一時的に失敗したとき、どのテーブルも読めないキーでの検索
|
|
126
|
-
- **空配列**:
|
|
126
|
+
- **空配列**: 辞書は引けたが、近さの下限を超えるエントリが無かった(辞書と関係の薄いクエリでは普通に起きます)
|
|
127
127
|
- **配列**: ヒットあり
|
|
128
128
|
|
|
129
129
|
キーの有無は設定だけで決まるものではないため、`_dictionary` は常に省略され得るものとして扱ってください。
|
|
@@ -118,7 +118,7 @@ push すると `semanticSearch: true` の列を持つテーブルにベクトル
|
|
|
118
118
|
|
|
119
119
|
### 有効化の直後は 403 になる
|
|
120
120
|
|
|
121
|
-
プロジェクトを有効化した直後や、テーブルの列に `semanticSearch: true`
|
|
121
|
+
プロジェクトを有効化した直後や、テーブルの列に `semanticSearch: true` を初めて付けた直後は、既存コンテンツ全件の埋め込みを作り直す再構築が走ります。完了するまで `semantic` / `hybrid` は 403 になり、完了すると使えるようになります。所要時間はコンテンツ量に比例します。フラグの付いた列が 1 つ以上残っている付け外しでも再構築は走りますが、こちらは既存の索引で検索を受け続けながら裏で作り直すので 403 にはなりません。`semanticSearch: true` の列が 1 つも無いテーブルはベクトルインデックス自体が作られず、`semantic` / `hybrid` は `search index not found (no column has semanticSearch: true)` の 403 になります。
|
|
122
122
|
|
|
123
123
|
### 本番とスコアは一致しない
|
|
124
124
|
|
|
@@ -99,7 +99,7 @@ export default defineConfig({
|
|
|
99
99
|
});
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
埋め込み(ベクトル)の元になる本文は、`semanticSearch: true` の列の値を定義順につないだテキストです。検索結果の `_semantic.chunks` に返るチャンクもこの本文を分割したもので、`keyword` の検索対象(全テキスト列)とは一致しません。埋め込みの費用はこの本文の量で決まるので、意味検索に要らない列(slug
|
|
102
|
+
埋め込み(ベクトル)の元になる本文は、`semanticSearch: true` の列の値を定義順につないだテキストです。検索結果の `_semantic.chunks` に返るチャンクもこの本文を分割したもので、`keyword` の検索対象(全テキスト列)とは一致しません。埋め込みの費用はこの本文の量で決まるので、意味検索に要らない列(slug・型番の一覧・機械的なコード)には付けないでください。列の付け外しは、そのテーブルのベクトルインデックスの再構築を伴います。テーブルに初めて `semanticSearch: true` を付けたときは索引が出来上がるまで `semantic` / `hybrid` は 403 になりますが、フラグの付いた列が 1 つ以上残っている付け外しでは既存の索引で検索を受け続けながら裏で作り直し、完了した時点で切り替わります(403 にはなりません)。最後の `semanticSearch: true` を外すとベクトルインデックスは削除されます。
|
|
103
103
|
|
|
104
104
|
> [!WARNING]
|
|
105
105
|
> 列を絞れば精度が上がる、というものではありません。意味検索は文脈の量で効きます。`title`
|
|
@@ -107,6 +107,33 @@ export default defineConfig({
|
|
|
107
107
|
> `title`、用途は
|
|
108
108
|
> `body`)も失われます。人が読んで内容を表す列(タイトル・本文・説明)はまとめて付け、識別子や機械的な値だけを外すのが目安です。
|
|
109
109
|
|
|
110
|
+
### 埋め込みモデルを選ぶ(`embeddingModel`)
|
|
111
|
+
|
|
112
|
+
埋め込みに使うモデルはテーブルごとに `embeddingModel` で選べます。省略すると標準モデル(AWS の標準的な埋め込みモデル)が使われ、通常は明示的な指定は不要です。指定できる名前は Orizm 側で用意した次のものだけで、無い名前を書いた push は失敗します。
|
|
113
|
+
|
|
114
|
+
| 名前 | 向いている内容 |
|
|
115
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
+
| (省略) | 標準モデル。製品名・型番・コードのような文字列の一致を含む、ほとんどのテーブルに向きます |
|
|
117
|
+
| `natural-language` | 自然文重視。記事・ブログ・説明文のように人が読む長い文章が中心のテーブルで、言い換えや文脈での一致が標準より強くなります。型番のような文字列の一致は標準より弱いので、型番で探す用途は `hybrid`(キーワード側が拾います)で使ってください |
|
|
118
|
+
|
|
119
|
+
```ts copy filename="orizm.config.ts" showLineNumbers {10}
|
|
120
|
+
export default defineConfig({
|
|
121
|
+
semanticSearch: { enabled: true },
|
|
122
|
+
tables: {
|
|
123
|
+
blog: {
|
|
124
|
+
columns: {
|
|
125
|
+
title: { type: "string", required: true, semanticSearch: true },
|
|
126
|
+
body: { type: "string", required: true, semanticSearch: true },
|
|
127
|
+
},
|
|
128
|
+
enableSearch: true,
|
|
129
|
+
embeddingModel: "natural-language", // テーブル単位で指定する。省略 = 標準モデル
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`embeddingModel` を変えて push すると、そのテーブルのベクトルインデックスは新しいモデルで作り直されます。既存の索引で検索を受け続けながら裏で作り直し、完了した時点で切り替わります(403 にはなりません)。既存コンテンツ全件の埋め込みを作り直すので、所要時間はコンテンツ量に比例し、埋め込みモデルの推論費用が掛かります。`orizm schema push --dry-run` では `embeddingModel:update` として表示されます。
|
|
136
|
+
|
|
110
137
|
> [!WARNING]
|
|
111
138
|
> セマンティック検索を利用できるかどうかは、プロジェクトの設定に加えてシステム側の設定でも決まります。システム側で有効になっていない環境では、`semanticSearch: { enabled: true }` の push は成功しますが `semantic` / `hybrid` の検索は利用できません(ベクトルインデックスが作られていない場合は 403 になり、過去に作られたインデックスが残っている場合は `keyword` の結果に劣化します)。利用中の環境で使えるかは管理者に確認してください。
|
|
112
139
|
|
|
@@ -179,12 +206,13 @@ for (const row of result.contents) {
|
|
|
179
206
|
| 非公開・削除 | Consumer SDK の検索(全 mode) | 1 秒未満(結果の行は DB から引き直すため、公開ビューから消えた行は即座に消えます) |
|
|
180
207
|
| セマンティック検索の有効化・テーブルへの `semanticSearch: true` の新規付与 | `semantic` / `hybrid` | コンテンツ量に比例(既存コンテンツ全件の埋め込みを作り直します。実測 76 件で約 1 分) |
|
|
181
208
|
| `semanticSearch: true` の列(モジュール内を含む)の追加・削除・順序変更 | `semantic` / `hybrid` | コンテンツ量に比例(索引を作り直します。埋め込みを作り直すのは本文が変わったコンテンツだけです) |
|
|
209
|
+
| テーブルの `embeddingModel` の変更 | `semantic` / `hybrid` | コンテンツ量に比例(索引を作り直します。既存コンテンツ全件の埋め込みを新しいモデルで作り直します) |
|
|
182
210
|
|
|
183
211
|
CMS SDK の検索は下書きも対象にするため、コンテンツを非公開にしても CMS SDK の検索結果からは消えません。公開状態で結果を絞りたい場合は Consumer SDK の検索を使ってください。
|
|
184
212
|
|
|
185
213
|
有効化の直後や、テーブルの列に `semanticSearch: true` を初めて付けた直後は、既存コンテンツ全件の埋め込みを作り直す処理(再構築)がバックグラウンドで走ります。完了するまで `semantic` / `hybrid` は 403 になり、完了すると利用できます。再構築の進み具合は、開発者コンソールのテーブル画面にある検索インデックス(`published_vector`。`enableDrafts` のテーブルでは `draft_vector` も)のドキュメント数で確認できます。`enableDrafts` を付けたり外したりしたときも `draft_vector` の作成・削除が走り、付けた直後は `draft_vector` が出来上がるまで CMS SDK の `semantic` / `hybrid` は 403 になります。付けたときは、そのテーブルの既存コンテンツ全件の埋め込みを作り直します(埋め込みモデルの推論費用が掛かります)。
|
|
186
214
|
|
|
187
|
-
`semanticSearch: true` の列(モジュール内の列を含む)を追加・削除したり順序を変えたりしたときも、フラグの付いた列が 1 つ以上残っていれば、スキーマを push すると再構築が走ります。こちらは既存の索引で検索を受け続けながら裏で作り直し、完了した時点で切り替わります(403 にはなりません)。切り替わるまでは、外した列の本文が `_semantic.chunks` に残ることがあります。フラグの無い列(`json` や他の型の列を含む)は本文に含まれないため、追加・削除しても再構築は走りません。最後のフラグを外すとベクトルインデックスは削除され、`semantic` / `hybrid` は 403
|
|
215
|
+
`semanticSearch: true` の列(モジュール内の列を含む)を追加・削除したり順序を変えたりしたときも、フラグの付いた列が 1 つ以上残っていれば、スキーマを push すると再構築が走ります。こちらは既存の索引で検索を受け続けながら裏で作り直し、完了した時点で切り替わります(403 にはなりません)。切り替わるまでは、外した列の本文が `_semantic.chunks` に残ることがあります。フラグの無い列(`json` や他の型の列を含む)は本文に含まれないため、追加・削除しても再構築は走りません。最後のフラグを外すとベクトルインデックスは削除され、`semantic` / `hybrid` は 403 になります(`orizm schema push --dry-run` では `vectorIndex:delete` として赤く表示されます。付け直すと既存コンテンツ全件の埋め込みを作り直します)。
|
|
188
216
|
|
|
189
217
|
[検索辞書](./dictionary.md) の反映タイミングは辞書の記事を参照してください(同義語による言い換えは即時、用語解説の検索対象化は非同期です)。
|
|
190
218
|
|
|
@@ -201,10 +229,12 @@ Orizm をセルフホストしている場合、セマンティック検索を
|
|
|
201
229
|
| `local` | OpenSearch クラスタ内のローカルモデル(多言語 MiniLM・384 次元)で推論します。開発や小規模な検証向けで、外部サービスは不要です |
|
|
202
230
|
| `bedrock` | Amazon Bedrock の埋め込みモデル(既定 `amazon.titan-embed-text-v2:0`・1024 次元)に推論を外出しします。本番向けの方式です |
|
|
203
231
|
|
|
204
|
-
- **`bedrock` のときの追加設定**: `AWS_REGION`、`BEDROCK_EMBEDDING_MODEL`、`BEDROCK_EMBEDDING_DIMENSION`
|
|
232
|
+
- **`bedrock` のときの追加設定**: `AWS_REGION`、`BEDROCK_EMBEDDING_MODEL`、`BEDROCK_EMBEDDING_DIMENSION` で標準モデル(テーブル定義で `embeddingModel` を省略したときに使われるモデル)と次元を指定します。マネージドの OpenSearch(Amazon OpenSearch Service)では、Bedrock を呼び出すときに OpenSearch 側が引き受ける IAM ロールを `BEDROCK_CONNECTOR_ROLE_ARN` に指定します。未設定の場合は `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` が接続情報として使われます
|
|
205
233
|
- **埋め込み基盤の準備**: 埋め込みモデルの登録・配備と取り込みパイプラインの作成は、マイグレーションタスクが行います(冪等で、既にあれば再利用します)。サーバーとワーカーを起動する前にマイグレーションを実行してください。推論経路やモデルの設定を変えたときも同様です。準備ができていない状態でサーバーを起動すると、起動ログにモデルが見つからない旨の error が出て、`semantic` / `hybrid` は 403 になります
|
|
234
|
+
- **ワーカーは 1 本で動かす**: 検索インデックスの同期と再構築は、ワーカーが 1 本で順番に処理することを前提にしています(再構築中に届いた更新が消えないための前提です)。ワーカーを複数起動しないでください。ワーカーをデプロイするときは、旧ワーカーを止めてから新ワーカーを起動してください(Orizm では ECS の設定でそうしています)。その間の 1〜2 分、同期や再構築のジョブはキューで待ち、新ワーカーが起動すると再開します
|
|
206
235
|
- **`off` は非破壊の停止スイッチ**: 一時的に `off` にしても既存のベクトルインデックスと埋め込み済みデータは残り、埋め込みの生成(書き込み時の同期と再構築)だけが止まります。この間の `semantic` / `hybrid` は `keyword` の結果に劣化して応答します。`local` / `bedrock` に戻してワーカーを起動すると、止まっていた間に古くなったインデックスは起動時の突き合わせで自動的に再構築されます
|
|
207
236
|
|
|
208
237
|
> [!WARNING]
|
|
209
238
|
> `local` と `bedrock`
|
|
210
|
-
>
|
|
239
|
+
> では埋め込みモデルも次元も違うため、スコアの値や順位は環境間で一致しません。品質の確認は本番と同じ推論経路で行ってください。推論経路や標準モデルを切り替えると、既存のベクトルインデックスはすべて(`embeddingModel` を省略しているテーブルの分が)作り直されます。
|
|
240
|
+
> テーブル定義の `embeddingModel` に書ける名前は `bedrock` のときだけ意味を持ち、`local` では名前を受け付けた上でクラスタ内のローカルモデルで動きます(同じ `orizm.config.ts` を開発環境にも push できます)。
|
|
@@ -348,6 +348,10 @@ ISO 8601 形式の文字列で構成されます。(例: `2025-01-01T00:00:00.
|
|
|
348
348
|
|
|
349
349
|
任意の文字列を設定できます。デフォルトは `undefined` です。
|
|
350
350
|
|
|
351
|
+
### 予約されている名前
|
|
352
|
+
|
|
353
|
+
上記の予約カラム名に加えて、`_semantic` もカラム名として使えません。テーブルには追加されませんが、[セマンティック検索](../full-text-search.md) の結果で各行に付くメタ情報のフィールド名として予約されています。
|
|
354
|
+
|
|
351
355
|
## インデックス
|
|
352
356
|
|
|
353
357
|
インデックスはデータの検索速度を向上させるための仕組みです。
|
package/dist/docs/index.md
CHANGED
package/dist/docs/manifest.json
CHANGED
|
@@ -63,10 +63,36 @@ function renderAuthoritySection(authorityChangeEvents, color) {
|
|
|
63
63
|
}
|
|
64
64
|
function renderSemanticSearchSection(semanticSearchChangeEvents, color) {
|
|
65
65
|
if (semanticSearchChangeEvents.length === 0) return [];
|
|
66
|
-
return [`⚫︎Semantic search\n${require_table.renderTable([
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
return [`⚫︎Semantic search\n${require_table.renderTable([
|
|
67
|
+
"Type",
|
|
68
|
+
"Name",
|
|
69
|
+
"Event"
|
|
70
|
+
], semanticSearchChangeEvents.map((event) => {
|
|
71
|
+
if (event.event === "vectorIndex:delete") return {
|
|
72
|
+
cells: [
|
|
73
|
+
"vectorIndex",
|
|
74
|
+
event.tableName,
|
|
75
|
+
event.event
|
|
76
|
+
],
|
|
77
|
+
color: "red"
|
|
78
|
+
};
|
|
79
|
+
if (event.event === "embeddingModel:update") return {
|
|
80
|
+
cells: [
|
|
81
|
+
"embeddingModel",
|
|
82
|
+
event.tableName,
|
|
83
|
+
event.event
|
|
84
|
+
],
|
|
85
|
+
color: "green"
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
cells: [
|
|
89
|
+
"semanticSearch",
|
|
90
|
+
"",
|
|
91
|
+
event.event
|
|
92
|
+
],
|
|
93
|
+
color: event.event === "semanticSearch:enable" ? "green" : "red"
|
|
94
|
+
};
|
|
95
|
+
}), color)}`];
|
|
70
96
|
}
|
|
71
97
|
function tableEventRow(event) {
|
|
72
98
|
switch (event.event) {
|
|
@@ -182,4 +208,4 @@ var push_default = require_cli_main.defineCommand({
|
|
|
182
208
|
|
|
183
209
|
//#endregion
|
|
184
210
|
exports.default = push_default;
|
|
185
|
-
//# sourceMappingURL=push-
|
|
211
|
+
//# sourceMappingURL=push-BhpbLJSX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-BhpbLJSX.cjs","names":["partial","renderTable","loadConfig","Result","configLoadFailure","toResult","apiErrorFailure","getOutputState","confirm","contextArgs","configFileArg","defineCommand","runWithContext"],"sources":["../src/cli/schema/change-events.ts","../src/cli/commands/schema/push.ts"],"sourcesContent":["import type { components } from \"@orizm/internal-shared/api-clients/dev-v2\";\nimport { renderTable, type RowColor, type TableRow } from \"../ui/table.js\";\n\ntype ChangeEvents = components[\"schemas\"][\"OrizmConfigChangeEvents\"];\ntype TableChangeEvent = ChangeEvents[\"tableChangeEvents\"][number];\ntype BucketChangeEvent = ChangeEvents[\"bucketChangeEvents\"][number];\ntype AuthorityChangeEvent = ChangeEvents[\"authorityChangeEvents\"][number];\ntype SemanticSearchChangeEvent =\n ChangeEvents[\"semanticSearchChangeEvents\"][number];\n\nexport function hasChanges(events: ChangeEvents): boolean {\n return (\n events.tableChangeEvents.length > 0 ||\n events.bucketChangeEvents.length > 0 ||\n events.authorityChangeEvents.length > 0 ||\n semanticSearchChangeEventsOf(events).length > 0\n );\n}\n\n// semanticSearchChangeEvents はこのフィールドを返さない旧サーバーに対しても落ちないよう、\n// 欠けていれば空として扱う(openapi-fetch は応答を実行時検証しない)\nfunction semanticSearchChangeEventsOf(\n events: ChangeEvents,\n): SemanticSearchChangeEvent[] {\n const partial: Partial<ChangeEvents> = events;\n return partial.semanticSearchChangeEvents ?? [];\n}\n\nexport function renderChangeEvents(\n events: ChangeEvents,\n color: boolean,\n): string {\n const sections = [\n ...renderTableSections(events.tableChangeEvents, color),\n ...renderBucketSection(events.bucketChangeEvents, color),\n ...renderAuthoritySection(events.authorityChangeEvents, color),\n ...renderSemanticSearchSection(semanticSearchChangeEventsOf(events), color),\n ];\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderTableSections(\n tableChangeEvents: TableChangeEvent[],\n color: boolean,\n): string[] {\n // 1 テーブルにつき 1 ブロックを出すため、対象テーブル名でまとめる。\n const eventsByTable = new Map<string, TableChangeEvent[]>();\n for (const event of tableChangeEvents) {\n const grouped = eventsByTable.get(event.tableDef.name) ?? [];\n grouped.push(event);\n eventsByTable.set(event.tableDef.name, grouped);\n }\n\n return [...eventsByTable.entries()].map(([tableName, events]) => {\n const rows = events.map(tableEventRow);\n return `⚫︎Table: ${tableName}\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`;\n });\n}\n\nfunction renderBucketSection(\n bucketChangeEvents: BucketChangeEvent[],\n color: boolean,\n): string[] {\n if (bucketChangeEvents.length === 0) return [];\n const rows = bucketChangeEvents.map(bucketEventRow);\n return [\n `⚫︎Buckets\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`,\n ];\n}\n\nfunction renderAuthoritySection(\n authorityChangeEvents: AuthorityChangeEvent[],\n color: boolean,\n): string[] {\n if (authorityChangeEvents.length === 0) return [];\n const rows = authorityChangeEvents.map((event): TableRow => ({\n cells: [\"authority\", event.event, JSON.stringify(event.authorityDef)],\n color: \"green\",\n }));\n return [\n `⚫︎Authorities\\n${renderTable([\"Type\", \"Event\", \"Detail\"], rows, color)}`,\n ];\n}\n\n// 有効化は追加のみ、無効化はベクトルインデックス(埋め込み済みデータ)を消すので赤。\n// テーブル単位のベクトルインデックスの削除(最後の semanticSearch: true の列を外す push)も、\n// テーブルのセクションには column:update(緑)しか出ないので、ここで赤く見せる。\n// 埋め込みモデルの変更(embeddingModel:update)は索引を裏で作り直すだけでデータを失わないので緑。\n// 未知のイベント(新しいサーバー)はデータを失う側に倒して赤\nfunction renderSemanticSearchSection(\n semanticSearchChangeEvents: SemanticSearchChangeEvent[],\n color: boolean,\n): string[] {\n if (semanticSearchChangeEvents.length === 0) return [];\n const rows = semanticSearchChangeEvents.map((event): TableRow => {\n if (event.event === \"vectorIndex:delete\") {\n return {\n cells: [\"vectorIndex\", event.tableName, event.event],\n color: \"red\",\n };\n }\n if (event.event === \"embeddingModel:update\") {\n return {\n cells: [\"embeddingModel\", event.tableName, event.event],\n color: \"green\",\n };\n }\n return {\n cells: [\"semanticSearch\", \"\", event.event],\n color: event.event === \"semanticSearch:enable\" ? \"green\" : \"red\",\n };\n });\n return [\n `⚫︎Semantic search\\n${renderTable([\"Type\", \"Name\", \"Event\"], rows, color)}`,\n ];\n}\n\n// create / update / enable は安全な変更なので緑、delete / disable はデータを失う危険な操作なので赤。\nfunction tableEventRow(event: TableChangeEvent): TableRow {\n switch (event.event) {\n case \"table:create\":\n case \"table:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"table:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n case \"column:create\":\n case \"column:update\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"column:delete\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"index:create\":\n case \"index:update\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"green\",\n );\n case \"index:delete\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"red\",\n );\n case \"module:create\":\n case \"module:update\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"module:delete\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"search:enable\":\n case \"search:update\":\n case \"history:enable\":\n case \"draft:enable\":\n case \"formSubmissions:create\":\n case \"formSubmissions:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"search:disable\":\n case \"history:disable\":\n case \"draft:disable\":\n case \"formSubmissions:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n }\n}\n\nfunction bucketEventRow(event: BucketChangeEvent): TableRow {\n const color: RowColor = event.event === \"bucket:delete\" ? \"red\" : \"green\";\n return row(\n \"bucket\",\n event.bucketDef.name,\n event.event,\n JSON.stringify(event.bucketDef),\n color,\n );\n}\n\nfunction row(\n type: string,\n name: string,\n event: string,\n detail: string,\n color: RowColor,\n): TableRow {\n return { cells: [type, name, event, detail], color };\n}\n","import type { ArgsDef } from \"citty\";\nimport { Result } from \"@praha/byethrow\";\nimport {\n defineCommand,\n type CommandFailure,\n type CommandOutput,\n} from \"../define-command.js\";\nimport { contextArgs, configFileArg, DEFAULT_CONFIG_FILE } from \"../shared.js\";\nimport { runWithContext, type CommandContext } from \"../run-with-context.js\";\nimport { configLoadFailure } from \"../config-errors.js\";\nimport { apiErrorFailure } from \"../api-errors.js\";\nimport { loadConfig } from \"../../config-loader.js\";\nimport { toResult } from \"../../api/errors.js\";\nimport { getOutputState, notice } from \"../../ui/output.js\";\nimport { confirm } from \"../../ui/prompt.js\";\nimport { hasChanges, renderChangeEvents } from \"../../schema/change-events.js\";\n\nexport type PushOptions = {\n configFile: string;\n dryRun: boolean;\n yes: boolean;\n force: boolean;\n};\n\nexport async function pushHandler(\n ctx: CommandContext,\n options: PushOptions,\n): Promise<Result.Result<CommandOutput | undefined, CommandFailure>> {\n const loaded = await loadConfig(options.configFile);\n if (Result.isFailure(loaded)) return configLoadFailure(loaded.error);\n\n const dryRun = await ctx.client.POST(\n \"/projects/{projectName}/definitions/dry-run\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const events = toResult(dryRun);\n if (Result.isFailure(events)) return apiErrorFailure(events.error);\n\n const changed = hasChanges(events.value);\n // --dry-run は --force の影響を受けない(apply しないので「差分なし」の表示のまま)\n if (options.dryRun) {\n return Result.succeed({\n json: events.value,\n text: () =>\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes to apply.\",\n });\n }\n\n if (!changed && !options.force) {\n return Result.succeed({\n json: events.value,\n text: () => \"No changes to apply.\",\n });\n }\n\n // --force は差分の有無に関わらず定義を送る(サーバー側の突き合わせ処理を同じ定義で再実行させるため)\n notice(\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes detected. Applying anyway (--force).\",\n );\n\n if (!options.yes) {\n if (process.stdin.isTTY !== true) {\n return Result.fail({\n message:\n \"Refusing to apply without confirmation in a non-interactive shell. Re-run with --yes.\",\n });\n }\n const approved = await confirm(\"Apply these changes?\");\n if (!approved) {\n return Result.succeed({\n json: { applied: false },\n text: () => \"Canceled.\",\n });\n }\n }\n\n const applyResponse = await ctx.client.POST(\n \"/projects/{projectName}/definitions\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const applied = toResult(applyResponse);\n if (Result.isFailure(applied)) return apiErrorFailure(applied.error);\n\n return Result.succeed({\n json: applied.value,\n text: () => \"Schema applied.\",\n });\n}\n\nconst pushArgs = {\n ...contextArgs,\n \"config-file\": configFileArg,\n \"dry-run\": {\n type: \"boolean\",\n description: \"Show change events without applying them\",\n default: false,\n },\n yes: {\n type: \"boolean\",\n description: \"Apply without the confirmation prompt\",\n default: false,\n },\n force: {\n type: \"boolean\",\n description:\n \"Apply even when no changes are detected (re-runs server-side reconciliation, e.g. semantic search indexes)\",\n default: false,\n },\n} satisfies ArgsDef;\n\nexport default defineCommand({\n meta: {\n name: \"push\",\n description: \"Build orizm.config.ts and apply the schema to the project\",\n },\n args: pushArgs,\n run: ({ args }) =>\n runWithContext(args, (ctx) =>\n pushHandler(ctx, {\n configFile: args[\"config-file\"] ?? DEFAULT_CONFIG_FILE,\n dryRun: args[\"dry-run\"] === true,\n yes: args.yes === true,\n force: args.force === true,\n }),\n ),\n});\n"],"mappings":"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"}
|
|
@@ -63,10 +63,36 @@ function renderAuthoritySection(authorityChangeEvents, color) {
|
|
|
63
63
|
}
|
|
64
64
|
function renderSemanticSearchSection(semanticSearchChangeEvents, color) {
|
|
65
65
|
if (semanticSearchChangeEvents.length === 0) return [];
|
|
66
|
-
return [`⚫︎Semantic search\n${renderTable([
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
return [`⚫︎Semantic search\n${renderTable([
|
|
67
|
+
"Type",
|
|
68
|
+
"Name",
|
|
69
|
+
"Event"
|
|
70
|
+
], semanticSearchChangeEvents.map((event) => {
|
|
71
|
+
if (event.event === "vectorIndex:delete") return {
|
|
72
|
+
cells: [
|
|
73
|
+
"vectorIndex",
|
|
74
|
+
event.tableName,
|
|
75
|
+
event.event
|
|
76
|
+
],
|
|
77
|
+
color: "red"
|
|
78
|
+
};
|
|
79
|
+
if (event.event === "embeddingModel:update") return {
|
|
80
|
+
cells: [
|
|
81
|
+
"embeddingModel",
|
|
82
|
+
event.tableName,
|
|
83
|
+
event.event
|
|
84
|
+
],
|
|
85
|
+
color: "green"
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
cells: [
|
|
89
|
+
"semanticSearch",
|
|
90
|
+
"",
|
|
91
|
+
event.event
|
|
92
|
+
],
|
|
93
|
+
color: event.event === "semanticSearch:enable" ? "green" : "red"
|
|
94
|
+
};
|
|
95
|
+
}), color)}`];
|
|
70
96
|
}
|
|
71
97
|
function tableEventRow(event) {
|
|
72
98
|
switch (event.event) {
|
|
@@ -182,4 +208,4 @@ var push_default = defineCommand({
|
|
|
182
208
|
|
|
183
209
|
//#endregion
|
|
184
210
|
export { push_default as default };
|
|
185
|
-
//# sourceMappingURL=push-
|
|
211
|
+
//# sourceMappingURL=push-DnPLP3dn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-DnPLP3dn.mjs","names":["partial"],"sources":["../src/cli/schema/change-events.ts","../src/cli/commands/schema/push.ts"],"sourcesContent":["import type { components } from \"@orizm/internal-shared/api-clients/dev-v2\";\nimport { renderTable, type RowColor, type TableRow } from \"../ui/table.js\";\n\ntype ChangeEvents = components[\"schemas\"][\"OrizmConfigChangeEvents\"];\ntype TableChangeEvent = ChangeEvents[\"tableChangeEvents\"][number];\ntype BucketChangeEvent = ChangeEvents[\"bucketChangeEvents\"][number];\ntype AuthorityChangeEvent = ChangeEvents[\"authorityChangeEvents\"][number];\ntype SemanticSearchChangeEvent =\n ChangeEvents[\"semanticSearchChangeEvents\"][number];\n\nexport function hasChanges(events: ChangeEvents): boolean {\n return (\n events.tableChangeEvents.length > 0 ||\n events.bucketChangeEvents.length > 0 ||\n events.authorityChangeEvents.length > 0 ||\n semanticSearchChangeEventsOf(events).length > 0\n );\n}\n\n// semanticSearchChangeEvents はこのフィールドを返さない旧サーバーに対しても落ちないよう、\n// 欠けていれば空として扱う(openapi-fetch は応答を実行時検証しない)\nfunction semanticSearchChangeEventsOf(\n events: ChangeEvents,\n): SemanticSearchChangeEvent[] {\n const partial: Partial<ChangeEvents> = events;\n return partial.semanticSearchChangeEvents ?? [];\n}\n\nexport function renderChangeEvents(\n events: ChangeEvents,\n color: boolean,\n): string {\n const sections = [\n ...renderTableSections(events.tableChangeEvents, color),\n ...renderBucketSection(events.bucketChangeEvents, color),\n ...renderAuthoritySection(events.authorityChangeEvents, color),\n ...renderSemanticSearchSection(semanticSearchChangeEventsOf(events), color),\n ];\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderTableSections(\n tableChangeEvents: TableChangeEvent[],\n color: boolean,\n): string[] {\n // 1 テーブルにつき 1 ブロックを出すため、対象テーブル名でまとめる。\n const eventsByTable = new Map<string, TableChangeEvent[]>();\n for (const event of tableChangeEvents) {\n const grouped = eventsByTable.get(event.tableDef.name) ?? [];\n grouped.push(event);\n eventsByTable.set(event.tableDef.name, grouped);\n }\n\n return [...eventsByTable.entries()].map(([tableName, events]) => {\n const rows = events.map(tableEventRow);\n return `⚫︎Table: ${tableName}\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`;\n });\n}\n\nfunction renderBucketSection(\n bucketChangeEvents: BucketChangeEvent[],\n color: boolean,\n): string[] {\n if (bucketChangeEvents.length === 0) return [];\n const rows = bucketChangeEvents.map(bucketEventRow);\n return [\n `⚫︎Buckets\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`,\n ];\n}\n\nfunction renderAuthoritySection(\n authorityChangeEvents: AuthorityChangeEvent[],\n color: boolean,\n): string[] {\n if (authorityChangeEvents.length === 0) return [];\n const rows = authorityChangeEvents.map((event): TableRow => ({\n cells: [\"authority\", event.event, JSON.stringify(event.authorityDef)],\n color: \"green\",\n }));\n return [\n `⚫︎Authorities\\n${renderTable([\"Type\", \"Event\", \"Detail\"], rows, color)}`,\n ];\n}\n\n// 有効化は追加のみ、無効化はベクトルインデックス(埋め込み済みデータ)を消すので赤。\n// テーブル単位のベクトルインデックスの削除(最後の semanticSearch: true の列を外す push)も、\n// テーブルのセクションには column:update(緑)しか出ないので、ここで赤く見せる。\n// 埋め込みモデルの変更(embeddingModel:update)は索引を裏で作り直すだけでデータを失わないので緑。\n// 未知のイベント(新しいサーバー)はデータを失う側に倒して赤\nfunction renderSemanticSearchSection(\n semanticSearchChangeEvents: SemanticSearchChangeEvent[],\n color: boolean,\n): string[] {\n if (semanticSearchChangeEvents.length === 0) return [];\n const rows = semanticSearchChangeEvents.map((event): TableRow => {\n if (event.event === \"vectorIndex:delete\") {\n return {\n cells: [\"vectorIndex\", event.tableName, event.event],\n color: \"red\",\n };\n }\n if (event.event === \"embeddingModel:update\") {\n return {\n cells: [\"embeddingModel\", event.tableName, event.event],\n color: \"green\",\n };\n }\n return {\n cells: [\"semanticSearch\", \"\", event.event],\n color: event.event === \"semanticSearch:enable\" ? \"green\" : \"red\",\n };\n });\n return [\n `⚫︎Semantic search\\n${renderTable([\"Type\", \"Name\", \"Event\"], rows, color)}`,\n ];\n}\n\n// create / update / enable は安全な変更なので緑、delete / disable はデータを失う危険な操作なので赤。\nfunction tableEventRow(event: TableChangeEvent): TableRow {\n switch (event.event) {\n case \"table:create\":\n case \"table:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"table:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n case \"column:create\":\n case \"column:update\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"column:delete\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"index:create\":\n case \"index:update\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"green\",\n );\n case \"index:delete\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"red\",\n );\n case \"module:create\":\n case \"module:update\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"module:delete\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"search:enable\":\n case \"search:update\":\n case \"history:enable\":\n case \"draft:enable\":\n case \"formSubmissions:create\":\n case \"formSubmissions:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"search:disable\":\n case \"history:disable\":\n case \"draft:disable\":\n case \"formSubmissions:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n }\n}\n\nfunction bucketEventRow(event: BucketChangeEvent): TableRow {\n const color: RowColor = event.event === \"bucket:delete\" ? \"red\" : \"green\";\n return row(\n \"bucket\",\n event.bucketDef.name,\n event.event,\n JSON.stringify(event.bucketDef),\n color,\n );\n}\n\nfunction row(\n type: string,\n name: string,\n event: string,\n detail: string,\n color: RowColor,\n): TableRow {\n return { cells: [type, name, event, detail], color };\n}\n","import type { ArgsDef } from \"citty\";\nimport { Result } from \"@praha/byethrow\";\nimport {\n defineCommand,\n type CommandFailure,\n type CommandOutput,\n} from \"../define-command.js\";\nimport { contextArgs, configFileArg, DEFAULT_CONFIG_FILE } from \"../shared.js\";\nimport { runWithContext, type CommandContext } from \"../run-with-context.js\";\nimport { configLoadFailure } from \"../config-errors.js\";\nimport { apiErrorFailure } from \"../api-errors.js\";\nimport { loadConfig } from \"../../config-loader.js\";\nimport { toResult } from \"../../api/errors.js\";\nimport { getOutputState, notice } from \"../../ui/output.js\";\nimport { confirm } from \"../../ui/prompt.js\";\nimport { hasChanges, renderChangeEvents } from \"../../schema/change-events.js\";\n\nexport type PushOptions = {\n configFile: string;\n dryRun: boolean;\n yes: boolean;\n force: boolean;\n};\n\nexport async function pushHandler(\n ctx: CommandContext,\n options: PushOptions,\n): Promise<Result.Result<CommandOutput | undefined, CommandFailure>> {\n const loaded = await loadConfig(options.configFile);\n if (Result.isFailure(loaded)) return configLoadFailure(loaded.error);\n\n const dryRun = await ctx.client.POST(\n \"/projects/{projectName}/definitions/dry-run\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const events = toResult(dryRun);\n if (Result.isFailure(events)) return apiErrorFailure(events.error);\n\n const changed = hasChanges(events.value);\n // --dry-run は --force の影響を受けない(apply しないので「差分なし」の表示のまま)\n if (options.dryRun) {\n return Result.succeed({\n json: events.value,\n text: () =>\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes to apply.\",\n });\n }\n\n if (!changed && !options.force) {\n return Result.succeed({\n json: events.value,\n text: () => \"No changes to apply.\",\n });\n }\n\n // --force は差分の有無に関わらず定義を送る(サーバー側の突き合わせ処理を同じ定義で再実行させるため)\n notice(\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes detected. Applying anyway (--force).\",\n );\n\n if (!options.yes) {\n if (process.stdin.isTTY !== true) {\n return Result.fail({\n message:\n \"Refusing to apply without confirmation in a non-interactive shell. Re-run with --yes.\",\n });\n }\n const approved = await confirm(\"Apply these changes?\");\n if (!approved) {\n return Result.succeed({\n json: { applied: false },\n text: () => \"Canceled.\",\n });\n }\n }\n\n const applyResponse = await ctx.client.POST(\n \"/projects/{projectName}/definitions\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const applied = toResult(applyResponse);\n if (Result.isFailure(applied)) return apiErrorFailure(applied.error);\n\n return Result.succeed({\n json: applied.value,\n text: () => \"Schema applied.\",\n });\n}\n\nconst pushArgs = {\n ...contextArgs,\n \"config-file\": configFileArg,\n \"dry-run\": {\n type: \"boolean\",\n description: \"Show change events without applying them\",\n default: false,\n },\n yes: {\n type: \"boolean\",\n description: \"Apply without the confirmation prompt\",\n default: false,\n },\n force: {\n type: \"boolean\",\n description:\n \"Apply even when no changes are detected (re-runs server-side reconciliation, e.g. semantic search indexes)\",\n default: false,\n },\n} satisfies ArgsDef;\n\nexport default defineCommand({\n meta: {\n name: \"push\",\n description: \"Build orizm.config.ts and apply the schema to the project\",\n },\n args: pushArgs,\n run: ({ args }) =>\n runWithContext(args, (ctx) =>\n pushHandler(ctx, {\n configFile: args[\"config-file\"] ?? DEFAULT_CONFIG_FILE,\n dryRun: args[\"dry-run\"] === true,\n yes: args.yes === true,\n force: args.force === true,\n }),\n ),\n});\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ var schema_default = defineCommand({
|
|
|
7
7
|
description: "Work with orizm.config.ts schema definitions"
|
|
8
8
|
},
|
|
9
9
|
subCommands: {
|
|
10
|
-
push: () => import("./push-
|
|
10
|
+
push: () => import("./push-DnPLP3dn.mjs").then((m) => m.default),
|
|
11
11
|
list: () => import("./list-BMnBuZMr.mjs").then((m) => m.default),
|
|
12
12
|
get: () => import("./get-1TtxSEoT.mjs").then((m) => m.default),
|
|
13
13
|
validate: () => import("./validate-COS4bfr0.mjs").then((m) => m.default),
|
|
@@ -17,4 +17,4 @@ var schema_default = defineCommand({
|
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
19
|
export { schema_default as default };
|
|
20
|
-
//# sourceMappingURL=schema-
|
|
20
|
+
//# sourceMappingURL=schema-CuOgdgv_.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-
|
|
1
|
+
{"version":3,"file":"schema-CuOgdgv_.mjs","names":[],"sources":["../src/cli/commands/schema/index.ts"],"sourcesContent":["import { defineCommand } from \"../define-command.js\";\n\nexport default defineCommand({\n meta: {\n name: \"schema\",\n description: \"Work with orizm.config.ts schema definitions\",\n },\n subCommands: {\n push: () => import(\"./push.js\").then((m) => m.default),\n list: () => import(\"./list.js\").then((m) => m.default),\n get: () => import(\"./get.js\").then((m) => m.default),\n validate: () => import(\"./validate.js\").then((m) => m.default),\n erd: () => import(\"./erd.js\").then((m) => m.default),\n },\n});\n"],"mappings":";;;AAEA,qBAAe,cAAc;CAC3B,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,aAAa;EACX,YAAY,OAAO,sBAAY,CAAC,MAAM,MAAM,EAAE,OAAO;EACrD,YAAY,OAAO,sBAAY,CAAC,MAAM,MAAM,EAAE,OAAO;EACrD,WAAW,OAAO,qBAAW,CAAC,MAAM,MAAM,EAAE,OAAO;EACnD,gBAAgB,OAAO,0BAAgB,CAAC,MAAM,MAAM,EAAE,OAAO;EAC7D,WAAW,OAAO,qBAAW,CAAC,MAAM,MAAM,EAAE,OAAO;CACrD;AACF,CAAC"}
|
|
@@ -7,7 +7,7 @@ var schema_default = require_cli_main.defineCommand({
|
|
|
7
7
|
description: "Work with orizm.config.ts schema definitions"
|
|
8
8
|
},
|
|
9
9
|
subCommands: {
|
|
10
|
-
push: () => Promise.resolve().then(() => require("./push-
|
|
10
|
+
push: () => Promise.resolve().then(() => require("./push-BhpbLJSX.cjs")).then((m) => m.default),
|
|
11
11
|
list: () => Promise.resolve().then(() => require("./list-E1sjTZLe.cjs")).then((m) => m.default),
|
|
12
12
|
get: () => Promise.resolve().then(() => require("./get-CdDZkUXV.cjs")).then((m) => m.default),
|
|
13
13
|
validate: () => Promise.resolve().then(() => require("./validate-BH8jqdqq.cjs")).then((m) => m.default),
|
|
@@ -17,4 +17,4 @@ var schema_default = require_cli_main.defineCommand({
|
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
19
|
exports.default = schema_default;
|
|
20
|
-
//# sourceMappingURL=schema-
|
|
20
|
+
//# sourceMappingURL=schema-Da8mf7ME.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-
|
|
1
|
+
{"version":3,"file":"schema-Da8mf7ME.cjs","names":["defineCommand"],"sources":["../src/cli/commands/schema/index.ts"],"sourcesContent":["import { defineCommand } from \"../define-command.js\";\n\nexport default defineCommand({\n meta: {\n name: \"schema\",\n description: \"Work with orizm.config.ts schema definitions\",\n },\n subCommands: {\n push: () => import(\"./push.js\").then((m) => m.default),\n list: () => import(\"./list.js\").then((m) => m.default),\n get: () => import(\"./get.js\").then((m) => m.default),\n validate: () => import(\"./validate.js\").then((m) => m.default),\n erd: () => import(\"./erd.js\").then((m) => m.default),\n },\n});\n"],"mappings":";;;AAEA,qBAAeA,+BAAc;CAC3B,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,aAAa;EACX,iDAAY,wBAAoB,MAAM,MAAM,EAAE,OAAO;EACrD,iDAAY,wBAAoB,MAAM,MAAM,EAAE,OAAO;EACrD,gDAAW,uBAAmB,MAAM,MAAM,EAAE,OAAO;EACnD,qDAAgB,4BAAwB,MAAM,MAAM,EAAE,OAAO;EAC7D,gDAAW,uBAAmB,MAAM,MAAM,EAAE,OAAO;CACrD;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orizm/cli",
|
|
3
|
-
"version": "3.3.0-beta.
|
|
3
|
+
"version": "3.3.0-beta.2",
|
|
4
4
|
"description": "Orizm CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"cli-table3": "0.6.5",
|
|
31
31
|
"consola": "3.4.2",
|
|
32
32
|
"esbuild": "0.28.2",
|
|
33
|
-
"hono": "4.13.
|
|
33
|
+
"hono": "4.13.9",
|
|
34
34
|
"jose": "6.2.12",
|
|
35
35
|
"mime-types": "3.0.2",
|
|
36
|
-
"open": "11.0.
|
|
37
|
-
"prettier": "3.9.
|
|
38
|
-
"valibot": "1.
|
|
39
|
-
"@orizm/common": "2.6.0-beta.
|
|
36
|
+
"open": "11.0.4",
|
|
37
|
+
"prettier": "3.9.9",
|
|
38
|
+
"valibot": "1.5.0",
|
|
39
|
+
"@orizm/common": "2.6.0-beta.2"
|
|
40
40
|
},
|
|
41
41
|
"bin": {
|
|
42
42
|
"orizm": "dist/cli/main.mjs"
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/mime-types": "3.0.1",
|
|
46
46
|
"tsdown": "0.22.14",
|
|
47
|
-
"@orizm/eslint-config": "0.0.0",
|
|
48
47
|
"@orizm/package-configs": "0.0.0",
|
|
48
|
+
"@orizm/eslint-config": "0.0.0",
|
|
49
49
|
"@orizm/internal-shared": "0.0.10"
|
|
50
50
|
},
|
|
51
51
|
"license": "Apache-2.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"push-BggkrD0g.cjs","names":["partial","renderTable","loadConfig","Result","configLoadFailure","toResult","apiErrorFailure","getOutputState","confirm","contextArgs","configFileArg","defineCommand","runWithContext"],"sources":["../src/cli/schema/change-events.ts","../src/cli/commands/schema/push.ts"],"sourcesContent":["import type { components } from \"@orizm/internal-shared/api-clients/dev-v2\";\nimport { renderTable, type RowColor, type TableRow } from \"../ui/table.js\";\n\ntype ChangeEvents = components[\"schemas\"][\"OrizmConfigChangeEvents\"];\ntype TableChangeEvent = ChangeEvents[\"tableChangeEvents\"][number];\ntype BucketChangeEvent = ChangeEvents[\"bucketChangeEvents\"][number];\ntype AuthorityChangeEvent = ChangeEvents[\"authorityChangeEvents\"][number];\ntype SemanticSearchChangeEvent =\n ChangeEvents[\"semanticSearchChangeEvents\"][number];\n\nexport function hasChanges(events: ChangeEvents): boolean {\n return (\n events.tableChangeEvents.length > 0 ||\n events.bucketChangeEvents.length > 0 ||\n events.authorityChangeEvents.length > 0 ||\n semanticSearchChangeEventsOf(events).length > 0\n );\n}\n\n// semanticSearchChangeEvents はこのフィールドを返さない旧サーバーに対しても落ちないよう、\n// 欠けていれば空として扱う(openapi-fetch は応答を実行時検証しない)\nfunction semanticSearchChangeEventsOf(\n events: ChangeEvents,\n): SemanticSearchChangeEvent[] {\n const partial: Partial<ChangeEvents> = events;\n return partial.semanticSearchChangeEvents ?? [];\n}\n\nexport function renderChangeEvents(\n events: ChangeEvents,\n color: boolean,\n): string {\n const sections = [\n ...renderTableSections(events.tableChangeEvents, color),\n ...renderBucketSection(events.bucketChangeEvents, color),\n ...renderAuthoritySection(events.authorityChangeEvents, color),\n ...renderSemanticSearchSection(semanticSearchChangeEventsOf(events), color),\n ];\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderTableSections(\n tableChangeEvents: TableChangeEvent[],\n color: boolean,\n): string[] {\n // 1 テーブルにつき 1 ブロックを出すため、対象テーブル名でまとめる。\n const eventsByTable = new Map<string, TableChangeEvent[]>();\n for (const event of tableChangeEvents) {\n const grouped = eventsByTable.get(event.tableDef.name) ?? [];\n grouped.push(event);\n eventsByTable.set(event.tableDef.name, grouped);\n }\n\n return [...eventsByTable.entries()].map(([tableName, events]) => {\n const rows = events.map(tableEventRow);\n return `⚫︎Table: ${tableName}\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`;\n });\n}\n\nfunction renderBucketSection(\n bucketChangeEvents: BucketChangeEvent[],\n color: boolean,\n): string[] {\n if (bucketChangeEvents.length === 0) return [];\n const rows = bucketChangeEvents.map(bucketEventRow);\n return [\n `⚫︎Buckets\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`,\n ];\n}\n\nfunction renderAuthoritySection(\n authorityChangeEvents: AuthorityChangeEvent[],\n color: boolean,\n): string[] {\n if (authorityChangeEvents.length === 0) return [];\n const rows = authorityChangeEvents.map((event): TableRow => ({\n cells: [\"authority\", event.event, JSON.stringify(event.authorityDef)],\n color: \"green\",\n }));\n return [\n `⚫︎Authorities\\n${renderTable([\"Type\", \"Event\", \"Detail\"], rows, color)}`,\n ];\n}\n\n// 有効化は追加のみ、無効化はベクトルインデックス(埋め込み済みデータ)を消すので赤。\nfunction renderSemanticSearchSection(\n semanticSearchChangeEvents: SemanticSearchChangeEvent[],\n color: boolean,\n): string[] {\n if (semanticSearchChangeEvents.length === 0) return [];\n const rows = semanticSearchChangeEvents.map((event): TableRow => ({\n cells: [\"semanticSearch\", event.event],\n color: event.event === \"semanticSearch:enable\" ? \"green\" : \"red\",\n }));\n return [`⚫︎Semantic search\\n${renderTable([\"Type\", \"Event\"], rows, color)}`];\n}\n\n// create / update / enable は安全な変更なので緑、delete / disable はデータを失う危険な操作なので赤。\nfunction tableEventRow(event: TableChangeEvent): TableRow {\n switch (event.event) {\n case \"table:create\":\n case \"table:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"table:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n case \"column:create\":\n case \"column:update\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"column:delete\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"index:create\":\n case \"index:update\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"green\",\n );\n case \"index:delete\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"red\",\n );\n case \"module:create\":\n case \"module:update\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"module:delete\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"search:enable\":\n case \"search:update\":\n case \"history:enable\":\n case \"draft:enable\":\n case \"formSubmissions:create\":\n case \"formSubmissions:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"search:disable\":\n case \"history:disable\":\n case \"draft:disable\":\n case \"formSubmissions:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n }\n}\n\nfunction bucketEventRow(event: BucketChangeEvent): TableRow {\n const color: RowColor = event.event === \"bucket:delete\" ? \"red\" : \"green\";\n return row(\n \"bucket\",\n event.bucketDef.name,\n event.event,\n JSON.stringify(event.bucketDef),\n color,\n );\n}\n\nfunction row(\n type: string,\n name: string,\n event: string,\n detail: string,\n color: RowColor,\n): TableRow {\n return { cells: [type, name, event, detail], color };\n}\n","import type { ArgsDef } from \"citty\";\nimport { Result } from \"@praha/byethrow\";\nimport {\n defineCommand,\n type CommandFailure,\n type CommandOutput,\n} from \"../define-command.js\";\nimport { contextArgs, configFileArg, DEFAULT_CONFIG_FILE } from \"../shared.js\";\nimport { runWithContext, type CommandContext } from \"../run-with-context.js\";\nimport { configLoadFailure } from \"../config-errors.js\";\nimport { apiErrorFailure } from \"../api-errors.js\";\nimport { loadConfig } from \"../../config-loader.js\";\nimport { toResult } from \"../../api/errors.js\";\nimport { getOutputState, notice } from \"../../ui/output.js\";\nimport { confirm } from \"../../ui/prompt.js\";\nimport { hasChanges, renderChangeEvents } from \"../../schema/change-events.js\";\n\nexport type PushOptions = {\n configFile: string;\n dryRun: boolean;\n yes: boolean;\n force: boolean;\n};\n\nexport async function pushHandler(\n ctx: CommandContext,\n options: PushOptions,\n): Promise<Result.Result<CommandOutput | undefined, CommandFailure>> {\n const loaded = await loadConfig(options.configFile);\n if (Result.isFailure(loaded)) return configLoadFailure(loaded.error);\n\n const dryRun = await ctx.client.POST(\n \"/projects/{projectName}/definitions/dry-run\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const events = toResult(dryRun);\n if (Result.isFailure(events)) return apiErrorFailure(events.error);\n\n const changed = hasChanges(events.value);\n // --dry-run は --force の影響を受けない(apply しないので「差分なし」の表示のまま)\n if (options.dryRun) {\n return Result.succeed({\n json: events.value,\n text: () =>\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes to apply.\",\n });\n }\n\n if (!changed && !options.force) {\n return Result.succeed({\n json: events.value,\n text: () => \"No changes to apply.\",\n });\n }\n\n // --force は差分の有無に関わらず定義を送る(サーバー側の突き合わせ処理を同じ定義で再実行させるため)\n notice(\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes detected. Applying anyway (--force).\",\n );\n\n if (!options.yes) {\n if (process.stdin.isTTY !== true) {\n return Result.fail({\n message:\n \"Refusing to apply without confirmation in a non-interactive shell. Re-run with --yes.\",\n });\n }\n const approved = await confirm(\"Apply these changes?\");\n if (!approved) {\n return Result.succeed({\n json: { applied: false },\n text: () => \"Canceled.\",\n });\n }\n }\n\n const applyResponse = await ctx.client.POST(\n \"/projects/{projectName}/definitions\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const applied = toResult(applyResponse);\n if (Result.isFailure(applied)) return apiErrorFailure(applied.error);\n\n return Result.succeed({\n json: applied.value,\n text: () => \"Schema applied.\",\n });\n}\n\nconst pushArgs = {\n ...contextArgs,\n \"config-file\": configFileArg,\n \"dry-run\": {\n type: \"boolean\",\n description: \"Show change events without applying them\",\n default: false,\n },\n yes: {\n type: \"boolean\",\n description: \"Apply without the confirmation prompt\",\n default: false,\n },\n force: {\n type: \"boolean\",\n description:\n \"Apply even when no changes are detected (re-runs server-side reconciliation, e.g. semantic search indexes)\",\n default: false,\n },\n} satisfies ArgsDef;\n\nexport default defineCommand({\n meta: {\n name: \"push\",\n description: \"Build orizm.config.ts and apply the schema to the project\",\n },\n args: pushArgs,\n run: ({ args }) =>\n runWithContext(args, (ctx) =>\n pushHandler(ctx, {\n configFile: args[\"config-file\"] ?? DEFAULT_CONFIG_FILE,\n dryRun: args[\"dry-run\"] === true,\n yes: args.yes === true,\n force: args.force === true,\n }),\n ),\n});\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"push-xWnD9oov.mjs","names":["partial"],"sources":["../src/cli/schema/change-events.ts","../src/cli/commands/schema/push.ts"],"sourcesContent":["import type { components } from \"@orizm/internal-shared/api-clients/dev-v2\";\nimport { renderTable, type RowColor, type TableRow } from \"../ui/table.js\";\n\ntype ChangeEvents = components[\"schemas\"][\"OrizmConfigChangeEvents\"];\ntype TableChangeEvent = ChangeEvents[\"tableChangeEvents\"][number];\ntype BucketChangeEvent = ChangeEvents[\"bucketChangeEvents\"][number];\ntype AuthorityChangeEvent = ChangeEvents[\"authorityChangeEvents\"][number];\ntype SemanticSearchChangeEvent =\n ChangeEvents[\"semanticSearchChangeEvents\"][number];\n\nexport function hasChanges(events: ChangeEvents): boolean {\n return (\n events.tableChangeEvents.length > 0 ||\n events.bucketChangeEvents.length > 0 ||\n events.authorityChangeEvents.length > 0 ||\n semanticSearchChangeEventsOf(events).length > 0\n );\n}\n\n// semanticSearchChangeEvents はこのフィールドを返さない旧サーバーに対しても落ちないよう、\n// 欠けていれば空として扱う(openapi-fetch は応答を実行時検証しない)\nfunction semanticSearchChangeEventsOf(\n events: ChangeEvents,\n): SemanticSearchChangeEvent[] {\n const partial: Partial<ChangeEvents> = events;\n return partial.semanticSearchChangeEvents ?? [];\n}\n\nexport function renderChangeEvents(\n events: ChangeEvents,\n color: boolean,\n): string {\n const sections = [\n ...renderTableSections(events.tableChangeEvents, color),\n ...renderBucketSection(events.bucketChangeEvents, color),\n ...renderAuthoritySection(events.authorityChangeEvents, color),\n ...renderSemanticSearchSection(semanticSearchChangeEventsOf(events), color),\n ];\n return sections.join(\"\\n\\n\");\n}\n\nfunction renderTableSections(\n tableChangeEvents: TableChangeEvent[],\n color: boolean,\n): string[] {\n // 1 テーブルにつき 1 ブロックを出すため、対象テーブル名でまとめる。\n const eventsByTable = new Map<string, TableChangeEvent[]>();\n for (const event of tableChangeEvents) {\n const grouped = eventsByTable.get(event.tableDef.name) ?? [];\n grouped.push(event);\n eventsByTable.set(event.tableDef.name, grouped);\n }\n\n return [...eventsByTable.entries()].map(([tableName, events]) => {\n const rows = events.map(tableEventRow);\n return `⚫︎Table: ${tableName}\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`;\n });\n}\n\nfunction renderBucketSection(\n bucketChangeEvents: BucketChangeEvent[],\n color: boolean,\n): string[] {\n if (bucketChangeEvents.length === 0) return [];\n const rows = bucketChangeEvents.map(bucketEventRow);\n return [\n `⚫︎Buckets\\n${renderTable(\n [\"Type\", \"Name\", \"Event\", \"Detail\"],\n rows,\n color,\n )}`,\n ];\n}\n\nfunction renderAuthoritySection(\n authorityChangeEvents: AuthorityChangeEvent[],\n color: boolean,\n): string[] {\n if (authorityChangeEvents.length === 0) return [];\n const rows = authorityChangeEvents.map((event): TableRow => ({\n cells: [\"authority\", event.event, JSON.stringify(event.authorityDef)],\n color: \"green\",\n }));\n return [\n `⚫︎Authorities\\n${renderTable([\"Type\", \"Event\", \"Detail\"], rows, color)}`,\n ];\n}\n\n// 有効化は追加のみ、無効化はベクトルインデックス(埋め込み済みデータ)を消すので赤。\nfunction renderSemanticSearchSection(\n semanticSearchChangeEvents: SemanticSearchChangeEvent[],\n color: boolean,\n): string[] {\n if (semanticSearchChangeEvents.length === 0) return [];\n const rows = semanticSearchChangeEvents.map((event): TableRow => ({\n cells: [\"semanticSearch\", event.event],\n color: event.event === \"semanticSearch:enable\" ? \"green\" : \"red\",\n }));\n return [`⚫︎Semantic search\\n${renderTable([\"Type\", \"Event\"], rows, color)}`];\n}\n\n// create / update / enable は安全な変更なので緑、delete / disable はデータを失う危険な操作なので赤。\nfunction tableEventRow(event: TableChangeEvent): TableRow {\n switch (event.event) {\n case \"table:create\":\n case \"table:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"table:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n case \"column:create\":\n case \"column:update\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"column:delete\":\n return row(\n \"column\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"index:create\":\n case \"index:update\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"green\",\n );\n case \"index:delete\":\n return row(\n \"index\",\n event.indexDef.columns.join(\", \"),\n event.event,\n JSON.stringify(event.indexDef),\n \"red\",\n );\n case \"module:create\":\n case \"module:update\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"green\",\n );\n case \"module:delete\":\n return row(\n \"module\",\n event.columnDef.name,\n event.event,\n JSON.stringify(event.columnDef),\n \"red\",\n );\n case \"search:enable\":\n case \"search:update\":\n case \"history:enable\":\n case \"draft:enable\":\n case \"formSubmissions:create\":\n case \"formSubmissions:update\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"green\");\n case \"search:disable\":\n case \"history:disable\":\n case \"draft:disable\":\n case \"formSubmissions:delete\":\n return row(\"table\", event.tableDef.name, event.event, \"\", \"red\");\n }\n}\n\nfunction bucketEventRow(event: BucketChangeEvent): TableRow {\n const color: RowColor = event.event === \"bucket:delete\" ? \"red\" : \"green\";\n return row(\n \"bucket\",\n event.bucketDef.name,\n event.event,\n JSON.stringify(event.bucketDef),\n color,\n );\n}\n\nfunction row(\n type: string,\n name: string,\n event: string,\n detail: string,\n color: RowColor,\n): TableRow {\n return { cells: [type, name, event, detail], color };\n}\n","import type { ArgsDef } from \"citty\";\nimport { Result } from \"@praha/byethrow\";\nimport {\n defineCommand,\n type CommandFailure,\n type CommandOutput,\n} from \"../define-command.js\";\nimport { contextArgs, configFileArg, DEFAULT_CONFIG_FILE } from \"../shared.js\";\nimport { runWithContext, type CommandContext } from \"../run-with-context.js\";\nimport { configLoadFailure } from \"../config-errors.js\";\nimport { apiErrorFailure } from \"../api-errors.js\";\nimport { loadConfig } from \"../../config-loader.js\";\nimport { toResult } from \"../../api/errors.js\";\nimport { getOutputState, notice } from \"../../ui/output.js\";\nimport { confirm } from \"../../ui/prompt.js\";\nimport { hasChanges, renderChangeEvents } from \"../../schema/change-events.js\";\n\nexport type PushOptions = {\n configFile: string;\n dryRun: boolean;\n yes: boolean;\n force: boolean;\n};\n\nexport async function pushHandler(\n ctx: CommandContext,\n options: PushOptions,\n): Promise<Result.Result<CommandOutput | undefined, CommandFailure>> {\n const loaded = await loadConfig(options.configFile);\n if (Result.isFailure(loaded)) return configLoadFailure(loaded.error);\n\n const dryRun = await ctx.client.POST(\n \"/projects/{projectName}/definitions/dry-run\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const events = toResult(dryRun);\n if (Result.isFailure(events)) return apiErrorFailure(events.error);\n\n const changed = hasChanges(events.value);\n // --dry-run は --force の影響を受けない(apply しないので「差分なし」の表示のまま)\n if (options.dryRun) {\n return Result.succeed({\n json: events.value,\n text: () =>\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes to apply.\",\n });\n }\n\n if (!changed && !options.force) {\n return Result.succeed({\n json: events.value,\n text: () => \"No changes to apply.\",\n });\n }\n\n // --force は差分の有無に関わらず定義を送る(サーバー側の突き合わせ処理を同じ定義で再実行させるため)\n notice(\n changed\n ? renderChangeEvents(events.value, getOutputState().color)\n : \"No changes detected. Applying anyway (--force).\",\n );\n\n if (!options.yes) {\n if (process.stdin.isTTY !== true) {\n return Result.fail({\n message:\n \"Refusing to apply without confirmation in a non-interactive shell. Re-run with --yes.\",\n });\n }\n const approved = await confirm(\"Apply these changes?\");\n if (!approved) {\n return Result.succeed({\n json: { applied: false },\n text: () => \"Canceled.\",\n });\n }\n }\n\n const applyResponse = await ctx.client.POST(\n \"/projects/{projectName}/definitions\",\n {\n params: { path: { projectName: ctx.projectName } },\n body: { definition: loaded.value },\n },\n );\n const applied = toResult(applyResponse);\n if (Result.isFailure(applied)) return apiErrorFailure(applied.error);\n\n return Result.succeed({\n json: applied.value,\n text: () => \"Schema applied.\",\n });\n}\n\nconst pushArgs = {\n ...contextArgs,\n \"config-file\": configFileArg,\n \"dry-run\": {\n type: \"boolean\",\n description: \"Show change events without applying them\",\n default: false,\n },\n yes: {\n type: \"boolean\",\n description: \"Apply without the confirmation prompt\",\n default: false,\n },\n force: {\n type: \"boolean\",\n description:\n \"Apply even when no changes are detected (re-runs server-side reconciliation, e.g. semantic search indexes)\",\n default: false,\n },\n} satisfies ArgsDef;\n\nexport default defineCommand({\n meta: {\n name: \"push\",\n description: \"Build orizm.config.ts and apply the schema to the project\",\n },\n args: pushArgs,\n run: ({ args }) =>\n runWithContext(args, (ctx) =>\n pushHandler(ctx, {\n configFile: args[\"config-file\"] ?? DEFAULT_CONFIG_FILE,\n dryRun: args[\"dry-run\"] === true,\n yes: args.yes === true,\n force: args.force === true,\n }),\n ),\n});\n"],"mappings":";;;;;;;;;AAUA,SAAgB,WAAW,QAA+B;CACxD,OACE,OAAO,kBAAkB,SAAS,KAClC,OAAO,mBAAmB,SAAS,KACnC,OAAO,sBAAsB,SAAS,KACtC,6BAA6B,MAAM,CAAC,CAAC,SAAS;AAElD;AAIA,SAAS,6BACP,QAC6B;CAE7B,OAAOA,OAAQ,8BAA8B,CAAC;AAChD;AAEA,SAAgB,mBACd,QACA,OACQ;CAOR,OAAO;EALL,GAAG,oBAAoB,OAAO,mBAAmB,KAAK;EACtD,GAAG,oBAAoB,OAAO,oBAAoB,KAAK;EACvD,GAAG,uBAAuB,OAAO,uBAAuB,KAAK;EAC7D,GAAG,4BAA4B,6BAA6B,MAAM,GAAG,KAAK;CAE9D,CAAC,CAAC,KAAK,MAAM;AAC7B;AAEA,SAAS,oBACP,mBACA,OACU;CAEV,MAAM,gCAAgB,IAAI,IAAgC;CAC1D,KAAK,MAAM,SAAS,mBAAmB;EACrC,MAAM,UAAU,cAAc,IAAI,MAAM,SAAS,IAAI,KAAK,CAAC;EAC3D,QAAQ,KAAK,KAAK;EAClB,cAAc,IAAI,MAAM,SAAS,MAAM,OAAO;CAChD;CAEA,OAAO,CAAC,GAAG,cAAc,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,YAAY;EAE/D,OAAO,YAAY,UAAU,IAAI,YAC/B;GAAC;GAAQ;GAAQ;GAAS;EAAQ,GAFvB,OAAO,IAAI,aAGnB,GACH,KACF;CACF,CAAC;AACH;AAEA,SAAS,oBACP,oBACA,OACU;CACV,IAAI,mBAAmB,WAAW,GAAG,OAAO,CAAC;CAE7C,OAAO,CACL,cAAc,YACZ;EAAC;EAAQ;EAAQ;EAAS;CAAQ,GAHzB,mBAAmB,IAAI,cAI7B,GACH,KACF,GACF;AACF;AAEA,SAAS,uBACP,uBACA,OACU;CACV,IAAI,sBAAsB,WAAW,GAAG,OAAO,CAAC;CAKhD,OAAO,CACL,kBAAkB,YAAY;EAAC;EAAQ;EAAS;CAAQ,GAL7C,sBAAsB,KAAK,WAAqB;EAC3D,OAAO;GAAC;GAAa,MAAM;GAAO,KAAK,UAAU,MAAM,YAAY;EAAC;EACpE,OAAO;CACT,EAEgE,GAAG,KAAK,GACxE;AACF;AAGA,SAAS,4BACP,4BACA,OACU;CACV,IAAI,2BAA2B,WAAW,GAAG,OAAO,CAAC;CAKrD,OAAO,CAAC,sBAAsB,YAAY,CAAC,QAAQ,OAAO,GAJ7C,2BAA2B,KAAK,WAAqB;EAChE,OAAO,CAAC,kBAAkB,MAAM,KAAK;EACrC,OAAO,MAAM,UAAU,0BAA0B,UAAU;CAC7D,EACgE,GAAG,KAAK,GAAG;AAC7E;AAGA,SAAS,cAAc,OAAmC;CACxD,QAAQ,MAAM,OAAd;EACE,KAAK;EACL,KAAK,gBACH,OAAO,IAAI,SAAS,MAAM,SAAS,MAAM,MAAM,OAAO,IAAI,OAAO;EACnE,KAAK,gBACH,OAAO,IAAI,SAAS,MAAM,SAAS,MAAM,MAAM,OAAO,IAAI,KAAK;EACjE,KAAK;EACL,KAAK,iBACH,OAAO,IACL,UACA,MAAM,UAAU,MAChB,MAAM,OACN,KAAK,UAAU,MAAM,SAAS,GAC9B,OACF;EACF,KAAK,iBACH,OAAO,IACL,UACA,MAAM,UAAU,MAChB,MAAM,OACN,KAAK,UAAU,MAAM,SAAS,GAC9B,KACF;EACF,KAAK;EACL,KAAK,gBACH,OAAO,IACL,SACA,MAAM,SAAS,QAAQ,KAAK,IAAI,GAChC,MAAM,OACN,KAAK,UAAU,MAAM,QAAQ,GAC7B,OACF;EACF,KAAK,gBACH,OAAO,IACL,SACA,MAAM,SAAS,QAAQ,KAAK,IAAI,GAChC,MAAM,OACN,KAAK,UAAU,MAAM,QAAQ,GAC7B,KACF;EACF,KAAK;EACL,KAAK,iBACH,OAAO,IACL,UACA,MAAM,UAAU,MAChB,MAAM,OACN,KAAK,UAAU,MAAM,SAAS,GAC9B,OACF;EACF,KAAK,iBACH,OAAO,IACL,UACA,MAAM,UAAU,MAChB,MAAM,OACN,KAAK,UAAU,MAAM,SAAS,GAC9B,KACF;EACF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACH,OAAO,IAAI,SAAS,MAAM,SAAS,MAAM,MAAM,OAAO,IAAI,OAAO;EACnE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACH,OAAO,IAAI,SAAS,MAAM,SAAS,MAAM,MAAM,OAAO,IAAI,KAAK;CACnE;AACF;AAEA,SAAS,eAAe,OAAoC;CAC1D,MAAM,QAAkB,MAAM,UAAU,kBAAkB,QAAQ;CAClE,OAAO,IACL,UACA,MAAM,UAAU,MAChB,MAAM,OACN,KAAK,UAAU,MAAM,SAAS,GAC9B,KACF;AACF;AAEA,SAAS,IACP,MACA,MACA,OACA,QACA,OACU;CACV,OAAO;EAAE,OAAO;GAAC;GAAM;GAAM;GAAO;EAAM;EAAG;CAAM;AACrD;;;;AC9KA,eAAsB,YACpB,KACA,SACmE;CACnE,MAAM,SAAS,MAAM,WAAW,QAAQ,UAAU;CAClD,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,kBAAkB,OAAO,KAAK;CASnE,MAAM,SAAS,SAAS,MAPH,IAAI,OAAO,KAC9B,+CACA;EACE,QAAQ,EAAE,MAAM,EAAE,aAAa,IAAI,YAAY,EAAE;EACjD,MAAM,EAAE,YAAY,OAAO,MAAM;CACnC,CACF,CAC8B;CAC9B,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,gBAAgB,OAAO,KAAK;CAEjE,MAAM,UAAU,WAAW,OAAO,KAAK;CAEvC,IAAI,QAAQ,QACV,OAAO,OAAO,QAAQ;EACpB,MAAM,OAAO;EACb,YACE,UACI,mBAAmB,OAAO,OAAO,eAAe,CAAC,CAAC,KAAK,IACvD;CACR,CAAC;CAGH,IAAI,CAAC,WAAW,CAAC,QAAQ,OACvB,OAAO,OAAO,QAAQ;EACpB,MAAM,OAAO;EACb,YAAY;CACd,CAAC;CAIH,OACE,UACI,mBAAmB,OAAO,OAAO,eAAe,CAAC,CAAC,KAAK,IACvD,iDACN;CAEA,IAAI,CAAC,QAAQ,KAAK;EAChB,IAAI,QAAQ,MAAM,UAAU,MAC1B,OAAO,OAAO,KAAK,EACjB,SACE,wFACJ,CAAC;EAGH,IAAI,CAAC,MADkB,QAAQ,sBAAsB,GAEnD,OAAO,OAAO,QAAQ;GACpB,MAAM,EAAE,SAAS,MAAM;GACvB,YAAY;EACd,CAAC;CAEL;CASA,MAAM,UAAU,SAAS,MAPG,IAAI,OAAO,KACrC,uCACA;EACE,QAAQ,EAAE,MAAM,EAAE,aAAa,IAAI,YAAY,EAAE;EACjD,MAAM,EAAE,YAAY,OAAO,MAAM;CACnC,CACF,CACsC;CACtC,IAAI,OAAO,UAAU,OAAO,GAAG,OAAO,gBAAgB,QAAQ,KAAK;CAEnE,OAAO,OAAO,QAAQ;EACpB,MAAM,QAAQ;EACd,YAAY;CACd,CAAC;AACH;AAEA,MAAM,WAAW;CACf,GAAG;CACH,eAAe;CACf,WAAW;EACT,MAAM;EACN,aAAa;EACb,SAAS;CACX;CACA,KAAK;EACH,MAAM;EACN,aAAa;EACb,SAAS;CACX;CACA,OAAO;EACL,MAAM;EACN,aACE;EACF,SAAS;CACX;AACF;AAEA,mBAAe,cAAc;CAC3B,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,MAAM;CACN,MAAM,EAAE,WACN,eAAe,OAAO,QACpB,YAAY,KAAK;EACf,YAAY,KAAK;EACjB,QAAQ,KAAK,eAAe;EAC5B,KAAK,KAAK,QAAQ;EAClB,OAAO,KAAK,UAAU;CACxB,CAAC,CACH;AACJ,CAAC"}
|