hikoutei 0.10.18-dev → 0.10.20-dev
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.ja.md +74 -216
- package/README.ko.md +57 -196
- package/README.md +65 -332
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
+
<img src="assets/hikoutei-icon.png" alt="Hikoutei" width="220" />
|
|
6
|
+
|
|
5
7
|
# Hikoutei
|
|
6
8
|
|
|
7
9
|
**SQLite でアプリは高速に、Google Sheets でワークフローを見えるままに。**
|
|
@@ -12,7 +14,7 @@ Google Sheets を利用する MVP 向けの型付きリポジトリであり、
|
|
|
12
14
|
Sheets へ非同期で投影されます。
|
|
13
15
|
|
|
14
16
|
<a href="https://www.npmjs.com/package/hikoutei">npm</a> ·
|
|
15
|
-
<a href="
|
|
17
|
+
<a href="https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md">クイックスタート</a> ·
|
|
16
18
|
<a href="https://github.com/ManddarinShop/Hikoutei/issues">Issues</a>
|
|
17
19
|
|
|
18
20
|
[](https://www.npmjs.com/package/hikoutei)
|
|
@@ -34,7 +36,53 @@ Hikoutei は、TypeScript アプリケーションにローカル SQLite を基
|
|
|
34
36
|
> Google Sheets を権威あるアプリケーションデータベースとして扱いません。
|
|
35
37
|
> SQLite が真実の源泉であり、Sheets は人向けの画面です。
|
|
36
38
|
|
|
37
|
-
##
|
|
39
|
+
## Hikoutei を使う理由
|
|
40
|
+
|
|
41
|
+
- シート行を手動で変換する代わりに、型付きエンティティを定義できます。
|
|
42
|
+
- Google Sheets を待たずにローカル SQLite で読み書きできます。
|
|
43
|
+
- コミットされた変更をバックグラウンドで Sheets に同期します。
|
|
44
|
+
- 想定外の列変更や重複ヘッダーを検出します。
|
|
45
|
+
- 競合時に新しいシート編集を上書きしません。
|
|
46
|
+
|
|
47
|
+
Hikoutei は `google-spreadsheet` や `@googleapis/sheets` の代わりではなく、
|
|
48
|
+
その一段上に位置します。生のスプレッドシートアクセスだけが必要なら API
|
|
49
|
+
クライアントを直接使ってください。
|
|
50
|
+
|
|
51
|
+
| 機能 | Hikoutei | google-spreadsheet | @googleapis/sheets |
|
|
52
|
+
| --- | :-: | :-: | :-: |
|
|
53
|
+
| 型付きエンティティモデル | ✅ | ❌ | ❌ |
|
|
54
|
+
| 高速なローカルアプリ読み取り | ✅ | ❌ | ❌ |
|
|
55
|
+
| Sheets への非同期投影 | ✅ | ❌ | ❌ |
|
|
56
|
+
| 耐久性のある書き込みリトライと重複排除 | ✅ | ❌ | ❌ |
|
|
57
|
+
| 競合を考慮したシート更新 | ✅ | ❌ | ❌ |
|
|
58
|
+
| 行・セルの直接操作 | 限定的 | ✅ | ✅ |
|
|
59
|
+
| Google Sheets API へのフルアクセス | Provider 経由 | 一部 | ✅ |
|
|
60
|
+
|
|
61
|
+
## インストール
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
npm install hikoutei @mikro-orm/core @mikro-orm/sql
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
ライブラリのインストールだけでは Google Cloud には何も作られません —
|
|
68
|
+
デフォルトはローカル専用(SQLite)で動作します。シート同期が必要なときだけ
|
|
69
|
+
下の setup を実行してください。
|
|
70
|
+
|
|
71
|
+
## セットアップ(Google Sheets 同期)
|
|
72
|
+
|
|
73
|
+
一回きりの対話操作です。gcloud CLI をインストールしてから:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
npx hikoutei setup
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Cloud プロジェクト・サービスアカウント・キー・スプレッドシートを作成し、
|
|
80
|
+
`.env` まで書き出します。`HIKOUTEI_SYNC_SPREADSHEET_URL` がなければ
|
|
81
|
+
`createTypedSheets()` はローカル専用(SQLite)のままです。詳細な設定、
|
|
82
|
+
クレデンシャルプール、クォータガイド、手動セットアップ:
|
|
83
|
+
[Google Sheets の設定](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/setup.md)。
|
|
84
|
+
|
|
85
|
+
## 使い方
|
|
38
86
|
|
|
39
87
|
スカラーエンティティを定義し、リクエストローカルなマネージャーでローカル
|
|
40
88
|
SQLite の権威を利用します。
|
|
@@ -48,6 +96,8 @@ const User = defineTypedSheetsEntity({
|
|
|
48
96
|
properties: {
|
|
49
97
|
id: { type: "string", primary: true },
|
|
50
98
|
name: { type: "string" },
|
|
99
|
+
age: { type: "number" },
|
|
100
|
+
active: { type: "boolean" },
|
|
51
101
|
},
|
|
52
102
|
});
|
|
53
103
|
|
|
@@ -57,231 +107,39 @@ const hikoutei = await createTypedSheets({
|
|
|
57
107
|
});
|
|
58
108
|
|
|
59
109
|
const em = hikoutei.em.fork();
|
|
60
|
-
const user = em.create(User, { id: "u1", name: "Ada" });
|
|
110
|
+
const user = em.create(User, { id: "u1", name: "Ada", age: 36, active: true });
|
|
61
111
|
em.persist(user);
|
|
62
112
|
await em.flush();
|
|
63
113
|
|
|
64
114
|
user.name = "Ada Lovelace";
|
|
65
115
|
await em.flush();
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**シートには何が起きるのか?** 書き込みは即座にローカル SQLite へコミット
|
|
69
|
-
されます — アプリケーションのリクエストは Google を待ちません。同期サービスが
|
|
70
|
-
有効な場合、Hikoutei は後でエンティティを登録済みの Google Sheet へバック
|
|
71
|
-
グラウンドで投影します。シート上で行われた人の編集は観察・検証され、SQLite
|
|
72
|
-
へ受け入れられるか競合として記録されるかのどちらかで、決して静かに上書き
|
|
73
|
-
されません。
|
|
74
|
-
|
|
75
|
-
## Hikoutei を使う理由
|
|
76
|
-
|
|
77
|
-
- シートの行を手動で変換する代わりに、型付きエンティティを定義する。
|
|
78
|
-
- Google Sheets を待たずにローカル SQLite で読み書きする。
|
|
79
|
-
- コミットされた変更を Sheets へバックグラウンドで同期する。
|
|
80
|
-
- 予期しない列の変更や重複ヘッダーを検出する。
|
|
81
|
-
- 競合時に新しいシート編集を上書きしない。
|
|
82
|
-
|
|
83
|
-
## Hikoutei が適しているケース
|
|
84
|
-
|
|
85
|
-
Hikoutei は以下のケースに適しています。
|
|
86
|
-
|
|
87
|
-
- 製品ワークフローの一部にスプレッドシートがある MVP・プロトタイプ
|
|
88
|
-
- 社内ツールや低トラフィックの管理アプリケーション
|
|
89
|
-
- 人が Sheets を簡単に確認しつつ、型付きのアプリケーションデータを扱いたい
|
|
90
|
-
チーム
|
|
91
|
-
- SQLite をローカルで使い、非同期のシート更新を受け入れられるサービス
|
|
92
|
-
|
|
93
|
-
## 他のツールを選ぶべきケース
|
|
94
|
-
|
|
95
|
-
次の要件がある場合は、通常のデータベースと Google API を直接使ってください。
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- Google Sheets をアプリケーションの主要データベースとして使う場合
|
|
103
|
-
|
|
104
|
-
## Hikoutei はあなたに合った抽象化か?
|
|
105
|
-
|
|
106
|
-
Hikoutei は `google-spreadsheet` や `@googleapis/sheets` を置き換えるものでは
|
|
107
|
-
ありません — その一段上に位置します。生のスプレッドシート操作だけが必要なら、
|
|
108
|
-
API クライアントを直接使ってください。
|
|
109
|
-
|
|
110
|
-
| 機能 | Hikoutei | google-spreadsheet | @googleapis/sheets |
|
|
111
|
-
| --- | :-: | :-: | :-: |
|
|
112
|
-
| 型付きエンティティモデル | ✅ | ❌ | ❌ |
|
|
113
|
-
| 高速なローカルアプリケーション読み取り | ✅ | ❌ | ❌ |
|
|
114
|
-
| Sheets への非同期投影 | ✅ | ❌ | ❌ |
|
|
115
|
-
| 永続的な書き込み再試行と重複排除 | ✅ | ❌ | ❌ |
|
|
116
|
-
| 競合を考慮したシート更新 | ✅ | ❌ | ❌ |
|
|
117
|
-
| 行・セルの直接操作 | 限定的 | ✅ | ✅ |
|
|
118
|
-
| Google Sheets API へのフルアクセス | Provider 経由 | 部分的 | ✅ |
|
|
119
|
-
|
|
120
|
-
## Google Sheets の設定
|
|
121
|
-
|
|
122
|
-
Google Sheets の同期はサービス側の関心事です。アプリケーションは provider
|
|
123
|
-
クライアントを import したり、`createTypedSheets()` にシートルートを渡したり、
|
|
124
|
-
書き込みごとに操作を選んだりしません — ルート API が受け取るのは `dbName` と
|
|
125
|
-
`entities` だけです。同期ランタイムはサービスアカウントを使う単一の内部
|
|
126
|
-
Google Sheets API provider を使用します — Apps Script のデプロイは不要です。
|
|
127
|
-
同期の自動開始は `HIKOUTEI_SYNC_SPREADSHEET_URL` と
|
|
128
|
-
`GOOGLE_APPLICATION_CREDENTIALS` で選択され、設定する公開
|
|
129
|
-
`googleSheetsApi` bootstrap オプションはありません。
|
|
130
|
-
|
|
131
|
-
### 環境変数による同期の自動開始
|
|
132
|
-
|
|
133
|
-
スプレッドシートの URL を環境変数に設定すると、`createTypedSheets()` が内部で
|
|
134
|
-
Sheets 同期を開始します — `flush()` はセットアップコードなしで outbox
|
|
135
|
-
ワーカー経由で Google Sheets に流れます:
|
|
136
|
-
|
|
137
|
-
```sh
|
|
138
|
-
HIKOUTEI_SYNC_SPREADSHEET_URL=https://docs.google.com/spreadsheets/d/<ID>/edit
|
|
139
|
-
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json
|
|
117
|
+
const loaded = await em.findOne(User, { id: "u1" });
|
|
118
|
+
if (loaded !== null) {
|
|
119
|
+
em.remove(loaded);
|
|
120
|
+
await em.flush();
|
|
121
|
+
}
|
|
140
122
|
```
|
|
141
123
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```
|
|
124
|
+
読み取り・トランザクション・演算子の詳細:
|
|
125
|
+
[クイックスタート](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md)。
|
|
145
126
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
### 手動でのサービスアカウント設定
|
|
152
|
-
|
|
153
|
-
1. **サービスアカウントを作成する。** Cloud プロジェクトで Google Sheets API
|
|
154
|
-
を有効化し、`https://www.googleapis.com/auth/spreadsheets` スコープの
|
|
155
|
-
サービスアカウントを作成して、対象スプレッドシートをそのメールアドレスに
|
|
156
|
-
**編集者(Editor)**として共有します。provider がタブを作成し、効果行と
|
|
157
|
-
receipt レコードを書き、行アンカーを管理するため、閲覧者権限では不十分
|
|
158
|
-
です。
|
|
159
|
-
2. **キーはサーバー側に置く。** サービスアカウントのキーパスはサーバーの
|
|
160
|
-
`GOOGLE_APPLICATION_CREDENTIALS` に、スプレッドシート ID は追跡されない
|
|
161
|
-
シークレットストアに置きます。キーをブラウザコードや Git に入れないで
|
|
162
|
-
ください。
|
|
163
|
-
3. **アプリケーションを通常どおり実行する。** `GOOGLE_APPLICATION_CREDENTIALS`
|
|
164
|
-
と `HIKOUTEI_SYNC_SPREADSHEET_URL` を設定した状態でアプリを起動すると、
|
|
165
|
-
`createTypedSheets()` がそれを検出して内部 sync bootstrap を開始します —
|
|
166
|
-
登録済みタブのヘッダーを作成・検証した後、outbox 配信と User_Input
|
|
167
|
-
ポーリングを開始します。渡す provider オプションも、手動で開始する内部
|
|
168
|
-
bootstrap もありません。
|
|
169
|
-
|
|
170
|
-
> **レガシースプレッドシートの注意。** 旧 Apps Script provider が developer
|
|
171
|
-
> metadata の行アンカーでプロビジョニングしたスプレッドシートは移行されませ
|
|
172
|
-
> ん。`User_Input` タブは `__hikoutei_row_id` システム列を必要とするため、
|
|
173
|
-
> レガシータブは再プロビジョニングが必要です。
|
|
174
|
-
|
|
175
|
-
Hikoutei は、永続的なローカル outbox・冪等な配信・競合を考慮した更新を使う
|
|
176
|
-
ため、一時的な API 障害でコミット済みのアプリケーション書き込みが失われる
|
|
177
|
-
ことはありません。provider は資格情報・スプレッドシート ID・URL・ペイロードを
|
|
178
|
-
ログに残さず、Google の割り当て枠に収まるようリクエスト開始間隔を調整します。
|
|
179
|
-
詳細な状態機械と復旧ルールは[内部整合性モデル](website/guide/internal-consistency.md)を
|
|
180
|
-
参照してください。
|
|
181
|
-
|
|
182
|
-
ライブの Google 呼び出しはオプトインであり、通常の検証経路はフェイク
|
|
183
|
-
provider と SQLite フィクスチャです。詳細なセットアップとトラブルシューティン
|
|
184
|
-
グは[クイックスタート](website/guide/quick-start.md)を参照してください。
|
|
127
|
+
書き込みは即座にローカル SQLite にコミットされます — リクエストは Google を
|
|
128
|
+
待ちません。シートで人が編集するとポーリングで戻ってきて、SQLite に取り込まれる
|
|
129
|
+
か競合として記録され、黙って上書きされることはありません。パイプライン全体
|
|
130
|
+
(outbox、配信、競合処理)は
|
|
131
|
+
[書き込みと同期フロー](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md) を参照してください。
|
|
185
132
|
|
|
186
|
-
##
|
|
187
|
-
|
|
188
|
-
プロジェクト名と npm パッケージ名はどちらも `hikoutei` です。現在の組み込み
|
|
189
|
-
SQLite provider は MikroORM を必要とします。
|
|
190
|
-
|
|
191
|
-
```sh
|
|
192
|
-
npm install hikoutei @mikro-orm/core @mikro-orm/sql
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
MikroORM は実装の詳細であり、Hikoutei の公開エンティティ API には現れません。
|
|
196
|
-
|
|
197
|
-
## ドキュメント
|
|
198
|
-
|
|
199
|
-
- [クイックスタート](website/guide/quick-start.md) — インストール、ORM ライフサイクル、
|
|
200
|
-
サービス側の同期設定
|
|
201
|
-
- [アーキテクチャ](website/guide/architecture.md) — ローカルストアとシートビューの関係
|
|
202
|
-
- [書き込みと同期フロー](website/guide/sync-flow.md) — 非同期配信と
|
|
203
|
-
復旧動作
|
|
204
|
-
- [内部整合性モデル](website/guide/internal-consistency.md) — 永続的な outbox、
|
|
205
|
-
冪等な配信、競合を考慮した更新
|
|
206
|
-
- [開発](website/guide/contributing.md) — ローカル開発とテストコマンド
|
|
207
|
-
- [ベンチマークノート](website/guide/benchmarks.md) — 日付付きの測定と
|
|
208
|
-
その限界
|
|
209
|
-
|
|
210
|
-
## 制限事項
|
|
211
|
-
|
|
212
|
-
- Google Sheets には割り当て枠・レイテンシ・API レート制限があります。
|
|
213
|
-
- シートの更新は非同期であり、アプリケーションはローカル状態を読むべきです。
|
|
214
|
-
- SQLite はサービスのローカルのみであり、分散調整レイヤーではありません。
|
|
215
|
-
- スキーマ変更・手動編集・競合更新には、依然としてアプリケーションの運用
|
|
216
|
-
ポリシーが必要です。
|
|
217
|
-
|
|
218
|
-
## ローカルクエリ
|
|
219
|
-
|
|
220
|
-
読み取りは Hikoutei 独自の型付き演算子を使い、常に SQLite で実行されます。
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
const [users, total] = await em.findAndCount(
|
|
224
|
-
User,
|
|
225
|
-
{
|
|
226
|
-
name: { like: "Ada%" },
|
|
227
|
-
age: { gte: 18, lt: 65 },
|
|
228
|
-
active: { in: [true] },
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
orderBy: { age: "desc", name: "asc" },
|
|
232
|
-
limit: 20,
|
|
233
|
-
offset: 0,
|
|
234
|
-
},
|
|
235
|
-
);
|
|
236
|
-
```
|
|
133
|
+
## さらに読む
|
|
237
134
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
## プロジェクトステータス
|
|
247
|
-
|
|
248
|
-
Hikoutei は活発に開発中です。現在の EntityManager は、スカラーエンティティの
|
|
249
|
-
ライフサイクル操作、型付きローカルフィルターと並び順、`limit` / `offset`
|
|
250
|
-
ページネーション、`count()`、スナップショット整合性のある `findAndCount()`、
|
|
251
|
-
コールバック形式の `transactional()` をサポートします。通常の読み取り元は
|
|
252
|
-
常に SQLite であり、Google Sheets ではありません。シート編集の取り込みと
|
|
253
|
-
競合表示はまだ発展途上です。マイナーバージョンのアップグレード前にリリース
|
|
254
|
-
ノートを確認してください。
|
|
255
|
-
|
|
256
|
-
## ロードマップ
|
|
257
|
-
|
|
258
|
-
最初の EntityManager 段階である豊富なローカル読み取りは完了しました。残る段階は
|
|
259
|
-
以下の実装順序に従い、日付やリリース番号は約束しません。
|
|
260
|
-
|
|
261
|
-
1. **ライフサイクル安全な書き込み**
|
|
262
|
-
- `upsert` と direct/bulk mutation 機能は、エンティティテーブル、
|
|
263
|
-
canonical state、永続的な Sheet effect outbox を 1 つの SQLite
|
|
264
|
-
トランザクションで処理する Hikoutei 独自の契約を通じてのみ追加します。
|
|
265
|
-
- この原子的なライフサイクルを迂回し得る、生の `nativeInsert`、
|
|
266
|
-
`nativeUpdate`、`nativeDelete`、または SQL パススルー API は約束しません。
|
|
267
|
-
2. **リレーションとロード**
|
|
268
|
-
- many-to-one、one-to-many、`populate()` 機能を追加します。
|
|
269
|
-
- 公開前に、リレーションの SQLite マッピング、Sheets プロジェクション
|
|
270
|
-
表現、スキーマ動作、競合セマンティクスを一体として設計します。
|
|
271
|
-
3. **スキーマ運用**
|
|
272
|
-
- マイグレーションとスキーマドリフト管理を追加します。
|
|
273
|
-
- 検証と運用フローを既存のセットアップツールと統合します。
|
|
274
|
-
|
|
275
|
-
### 同期と運用
|
|
276
|
-
|
|
277
|
-
以下の作業は EntityManager の各段階と並行して進めます。
|
|
278
|
-
|
|
279
|
-
- Google Sheets からの意図的なユーザー編集の取り込みを完了する
|
|
280
|
-
- 更新・削除の競合処理と表示を改善する
|
|
281
|
-
|
|
282
|
-
現在の作業は[オープンな Issues](https://github.com/ManddarinShop/Hikoutei/issues)を
|
|
283
|
-
参照してください。
|
|
135
|
+
- [クイックスタート](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md) — インストール、ORM
|
|
136
|
+
ライフサイクル、同期セットアップ
|
|
137
|
+
- [アーキテクチャ](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/architecture.md) — ローカルストアとシート
|
|
138
|
+
画面の連携
|
|
139
|
+
- [書き込みと同期フロー](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md) — 非同期配信とリカバリ
|
|
140
|
+
- [制限事項](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/limitations.md) — 他のツールを選ぶべきケース
|
|
141
|
+
- [プロジェクトステータスとロードマップ](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/status.md) — 完了済みと次の作業
|
|
284
142
|
|
|
285
143
|
## ライセンス
|
|
286
144
|
|
|
287
|
-
Hikoutei は [MIT ライセンス](LICENSE)
|
|
145
|
+
Hikoutei は [MIT ライセンス](LICENSE) のもとで公開されています。
|
package/README.ko.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
+
<img src="assets/hikoutei-icon.png" alt="Hikoutei" width="220" />
|
|
6
|
+
|
|
5
7
|
# Hikoutei
|
|
6
8
|
|
|
7
9
|
**SQLite로 앱은 빠르게, Google Sheets로 업무 흐름은 눈에 보이게.**
|
|
@@ -11,7 +13,7 @@ Google Sheets 기반 MVP를 위한 타입 안전 리포지토리이자 안전한
|
|
|
11
13
|
사람이 검토하고 가볍게 협업할 수 있도록 Google Sheets에 비동기로 투영됩니다.
|
|
12
14
|
|
|
13
15
|
<a href="https://www.npmjs.com/package/hikoutei">npm</a> ·
|
|
14
|
-
<a href="
|
|
16
|
+
<a href="https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md">빠른 시작</a> ·
|
|
15
17
|
<a href="https://github.com/ManddarinShop/Hikoutei/issues">이슈</a>
|
|
16
18
|
|
|
17
19
|
[](https://www.npmjs.com/package/hikoutei)
|
|
@@ -32,43 +34,6 @@ Sheets는 검토, 운영, 가벼운 협업을 위한 화면으로 남습니다.
|
|
|
32
34
|
> Google Sheets를 권위 있는 애플리케이션 데이터베이스로 취급하지 않습니다.
|
|
33
35
|
> SQLite가 진실의 원천이고, Sheets는 사람을 위한 화면입니다.
|
|
34
36
|
|
|
35
|
-
## 빠른 시작
|
|
36
|
-
|
|
37
|
-
스칼라 엔티티를 정의하고 요청-로컬 매니저를 통해 로컬 SQLite authority를
|
|
38
|
-
사용합니다.
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import { createTypedSheets, defineTypedSheetsEntity } from "hikoutei";
|
|
42
|
-
|
|
43
|
-
const User = defineTypedSheetsEntity({
|
|
44
|
-
name: "User",
|
|
45
|
-
tableName: "users",
|
|
46
|
-
properties: {
|
|
47
|
-
id: { type: "string", primary: true },
|
|
48
|
-
name: { type: "string" },
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const hikoutei = await createTypedSheets({
|
|
53
|
-
dbName: "./hikoutei.sqlite",
|
|
54
|
-
entities: [User],
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const em = hikoutei.em.fork();
|
|
58
|
-
const user = em.create(User, { id: "u1", name: "Ada" });
|
|
59
|
-
em.persist(user);
|
|
60
|
-
await em.flush();
|
|
61
|
-
|
|
62
|
-
user.name = "Ada Lovelace";
|
|
63
|
-
await em.flush();
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**시트에는 무슨 일이 일어날까?** 쓰기는 즉시 로컬 SQLite에 커밋됩니다 —
|
|
67
|
-
애플리케이션 요청은 Google을 기다리지 않습니다. 동기화 서비스가 활성화되면
|
|
68
|
-
Hikoutei는 나중에 엔티티를 등록된 Google Sheet에 백그라운드로 투영합니다.
|
|
69
|
-
시트에서 이루어진 사람의 수정은 관찰되고 검증되어 SQLite로 수용되거나
|
|
70
|
-
충돌로 기록되며, 절대 조용히 덮어쓰이지 않습니다.
|
|
71
|
-
|
|
72
37
|
## Hikoutei를 쓰는 이유
|
|
73
38
|
|
|
74
39
|
- 시트 행을 수동으로 변환하는 대신 타입 지정 엔티티를 정의합니다.
|
|
@@ -77,29 +42,6 @@ Hikoutei는 나중에 엔티티를 등록된 Google Sheet에 백그라운드로
|
|
|
77
42
|
- 예상치 못한 컬럼 변경과 중복 헤더를 감지합니다.
|
|
78
43
|
- 충돌 중에 더 새로운 시트 수정을 덮어쓰지 않습니다.
|
|
79
44
|
|
|
80
|
-
## Hikoutei를 쓰기 좋은 때
|
|
81
|
-
|
|
82
|
-
Hikoutei는 다음 상황에 잘 맞습니다.
|
|
83
|
-
|
|
84
|
-
- 제품 워크플로의 일부로 스프레드시트가 있는 MVP와 프로토타입
|
|
85
|
-
- 내부 도구와 저트래픽 관리 애플리케이션
|
|
86
|
-
- 사람들이 Sheets를 쉽게 확인하면서 타입 지정된 애플리케이션 데이터를
|
|
87
|
-
유지하려는 팀
|
|
88
|
-
- SQLite를 로컬에서 사용하고 비동기 시트 업데이트를 수용할 수 있는 서비스
|
|
89
|
-
|
|
90
|
-
## 다른 도구를 선택해야 할 때
|
|
91
|
-
|
|
92
|
-
다음이 필요하다면 일반 데이터베이스와 Google API를 직접 사용하세요.
|
|
93
|
-
|
|
94
|
-
- 여러 행이나 서비스에 걸친 강력한 트랜잭션
|
|
95
|
-
- 높은 쓰기 처리량 또는 많은 동시 작성자
|
|
96
|
-
- 복잡한 쿼리, 조인, 리포팅 워크로드
|
|
97
|
-
- 멀티 서버·멀티 리전 조정
|
|
98
|
-
- Google Sheets에서의 즉시 읽기-쓰기 일관성
|
|
99
|
-
- Google Sheets를 애플리케이션의 주 데이터베이스로 사용
|
|
100
|
-
|
|
101
|
-
## Hikoutei가 당신에게 맞는 추상화인가요?
|
|
102
|
-
|
|
103
45
|
Hikoutei는 `google-spreadsheet`나 `@googleapis/sheets`를 대체하지 않습니다 —
|
|
104
46
|
한 단계 위에 위치합니다. 원시 스프레드시트 접근만 필요하다면 API 클라이언트를
|
|
105
47
|
직접 사용하세요.
|
|
@@ -114,164 +56,83 @@ Hikoutei는 `google-spreadsheet`나 `@googleapis/sheets`를 대체하지 않습
|
|
|
114
56
|
| 행·셀 직접 조작 | 제한적 | ✅ | ✅ |
|
|
115
57
|
| 전체 Google Sheets API 접근 | Provider 경유 | 부분적 | ✅ |
|
|
116
58
|
|
|
117
|
-
## Google Sheets 설정
|
|
118
|
-
|
|
119
|
-
Google Sheets 동기화는 서비스 측 관심사입니다. 애플리케이션은 provider
|
|
120
|
-
클라이언트를 import하거나, Sheet 라우트를 `createTypedSheets()`에 넘기거나,
|
|
121
|
-
쓰기마다 연산을 선택하지 않습니다 — 루트 API는 `dbName`과 `entities`만
|
|
122
|
-
받습니다. 동기화 런타임은 서비스 계정을 사용하는 하나의 내부 Google Sheets
|
|
123
|
-
API provider를 사용합니다 — Apps Script 배포가 없습니다. 동기화 자동 시작은
|
|
124
|
-
`HIKOUTEI_SYNC_SPREADSHEET_URL`과 `GOOGLE_APPLICATION_CREDENTIALS`로
|
|
125
|
-
선택되며, 설정할 공개 `googleSheetsApi` bootstrap 옵션은 없습니다.
|
|
126
|
-
|
|
127
|
-
### 환경 변수 기반 동기화 자동 시작
|
|
128
|
-
|
|
129
|
-
스프레드시트 URL을 환경 변수로 설정하면 `createTypedSheets()`가 내부적으로
|
|
130
|
-
Sheets 동기화를 시작합니다 — `flush()`는 설정 코드 없이 outbox worker를 통해
|
|
131
|
-
Google Sheets로 흘러갑니다:
|
|
132
|
-
|
|
133
|
-
```sh
|
|
134
|
-
HIKOUTEI_SYNC_SPREADSHEET_URL=https://docs.google.com/spreadsheets/d/<ID>/edit
|
|
135
|
-
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities: [User] });
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
`HIKOUTEI_SYNC_SPREADSHEET_URL`이 없으면 `createTypedSheets()`는 로컬 전용
|
|
143
|
-
(SQLite)으로 유지됩니다. 시작 실패는 명확한 메시지로 진단됩니다: 잘못된 URL,
|
|
144
|
-
없거나 잘못된 자격 증명 파일, 스프레드시트에 공유되지 않은 서비스 계정
|
|
145
|
-
(어떤 이메일을 공유해야 하는지 에러가 알려줍니다).
|
|
146
|
-
|
|
147
|
-
### 수동 서비스 계정 설정
|
|
148
|
-
|
|
149
|
-
1. **서비스 계정을 만듭니다.** Cloud 프로젝트에서 Google Sheets API를
|
|
150
|
-
활성화하고, `https://www.googleapis.com/auth/spreadsheets` 스코프의
|
|
151
|
-
서비스 계정을 만든 뒤 대상 스프레드시트를 해당 이메일에 **편집자(Editor)**로
|
|
152
|
-
공유합니다. provider가 탭을 만들고, 효과 행과 receipt 기록을 쓰고, 행
|
|
153
|
-
anchor를 관리하므로 뷰어 권한으로는 부족합니다.
|
|
154
|
-
2. **키를 서버 측에 둡니다.** 서비스 계정 키 경로를 서버의
|
|
155
|
-
`GOOGLE_APPLICATION_CREDENTIALS`에, 스프레드시트 ID는 추적되지 않는
|
|
156
|
-
비밀 저장소에 둡니다. 키를 브라우저 코드나 Git에 넣지 마세요.
|
|
157
|
-
3. **애플리케이션을 정상적으로 실행합니다.** `GOOGLE_APPLICATION_CREDENTIALS`와
|
|
158
|
-
`HIKOUTEI_SYNC_SPREADSHEET_URL`을 설정한 상태로 앱을 시작하면
|
|
159
|
-
`createTypedSheets()`가 이를 감지해 내부 sync bootstrap을 시작합니다 —
|
|
160
|
-
등록된 탭의 헤더를 만들고 검증한 뒤 outbox 전달과 User_Input 폴링을
|
|
161
|
-
시작합니다. 넘길 provider 옵션이나 직접 시작할 내부 bootstrap은 없습니다.
|
|
162
|
-
|
|
163
|
-
> **레거시 스프레드시트 참고.** 이전 Apps Script provider가 developer
|
|
164
|
-
> metadata 행 anchor로 프로비저닝한 스프레드시트는 마이그레이션되지
|
|
165
|
-
> 않습니다. `User_Input` 탭은 이제 `__hikoutei_row_id` 시스템 컬럼이
|
|
166
|
-
> 필요하므로 레거시 탭을 다시 프로비저닝해야 합니다.
|
|
167
|
-
|
|
168
|
-
Hikoutei는 내구성 있는 로컬 outbox, 멱등 전달, 충돌을 인지하는 업데이트를
|
|
169
|
-
사용하므로 일시적인 API 실패가 커밋된 애플리케이션 쓰기를 잃게 하지 않습니다.
|
|
170
|
-
provider는 자격 증명, 스프레드시트 ID, URL, 페이로드를 로그에 남기지 않으며,
|
|
171
|
-
Google 할당량 창 안에 머물도록 요청 시작 간격을 조절합니다. 상세 상태 머신과
|
|
172
|
-
복구 규칙은 [내부 정합성 모델](website/guide/internal-consistency.md)을
|
|
173
|
-
참고하세요.
|
|
174
|
-
|
|
175
|
-
라이브 Google 호출은 opt-in이며, 일반적인 검증 경로는 fake provider와 SQLite
|
|
176
|
-
fixture입니다. 자세한 설정과 문제 해결 단계는 [빠른 시작](website/guide/quick-start.md)을
|
|
177
|
-
참고하세요.
|
|
178
|
-
|
|
179
59
|
## 설치
|
|
180
60
|
|
|
181
|
-
프로젝트와 npm 패키지 이름은 모두 `hikoutei`입니다. 내장 SQLite provider는
|
|
182
|
-
현재 MikroORM을 필요로 합니다.
|
|
183
|
-
|
|
184
61
|
```sh
|
|
185
62
|
npm install hikoutei @mikro-orm/core @mikro-orm/sql
|
|
186
63
|
```
|
|
187
64
|
|
|
188
|
-
|
|
189
|
-
|
|
65
|
+
라이브러리 설치만 하면 Google Cloud에는 아무 것도 만들지 않습니다 — 기본은
|
|
66
|
+
로컬 전용(SQLite)으로 동작합니다. 시트 동기화를 원할 때만 아래 setup을
|
|
67
|
+
실행하세요.
|
|
190
68
|
|
|
191
|
-
##
|
|
69
|
+
## 설정 (Google Sheets 동기화)
|
|
192
70
|
|
|
193
|
-
|
|
194
|
-
- [아키텍처](website/guide/architecture.md) — 로컬 저장소와 Sheet 화면이 맞물리는 방식
|
|
195
|
-
- [쓰기 및 동기화 흐름](website/guide/sync-flow.md) — 비동기 전달과
|
|
196
|
-
복구 동작
|
|
197
|
-
- [내부 정합성 모델](website/guide/internal-consistency.md) — 내구성 outbox,
|
|
198
|
-
멱등 전달, 충돌을 인지하는 업데이트
|
|
199
|
-
- [개발](website/guide/contributing.md) — 로컬 개발 및 테스트 명령어
|
|
200
|
-
- [벤치마크 노트](website/guide/benchmarks.md) — 날짜가 기록된 측정과
|
|
201
|
-
그 한계
|
|
71
|
+
1회성 인터랙티브 작업입니다. gcloud CLI 설치 후:
|
|
202
72
|
|
|
203
|
-
|
|
73
|
+
```sh
|
|
74
|
+
npx hikoutei setup
|
|
75
|
+
```
|
|
204
76
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
필요합니다.
|
|
77
|
+
Cloud 프로젝트, 서비스 계정, 키, 스프레드시트를 만들고 `.env`까지 써 줍니다.
|
|
78
|
+
`HIKOUTEI_SYNC_SPREADSHEET_URL`이 없으면 `createTypedSheets()`는 로컬 전용
|
|
79
|
+
(SQLite)으로 유지됩니다. 상세 설정, 크리덴셜 풀, 쿼터 가이드, 수동 설정:
|
|
80
|
+
[Google Sheets 설정](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/setup.md).
|
|
210
81
|
|
|
211
|
-
##
|
|
82
|
+
## 사용법
|
|
212
83
|
|
|
213
|
-
|
|
84
|
+
스칼라 엔티티를 정의하고 요청-로컬 매니저를 통해 로컬 SQLite authority를
|
|
85
|
+
사용합니다.
|
|
214
86
|
|
|
215
87
|
```ts
|
|
216
|
-
|
|
217
|
-
User,
|
|
218
|
-
{
|
|
219
|
-
name: { like: "Ada%" },
|
|
220
|
-
age: { gte: 18, lt: 65 },
|
|
221
|
-
active: { in: [true] },
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
orderBy: { age: "desc", name: "asc" },
|
|
225
|
-
limit: 20,
|
|
226
|
-
offset: 0,
|
|
227
|
-
},
|
|
228
|
-
);
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
`eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`은 선언된 스칼라 타입에
|
|
232
|
-
허용되는 범위에서 사용할 수 있고, `like`는 문자열 전용입니다. `{ active: true }`
|
|
233
|
-
같은 동등 조건 축약도 계속 지원합니다. `count()`는 페이지네이션 전 필터 전체 개수를
|
|
234
|
-
반환하고, `findAndCount()`는 한 SQLite 스냅샷에서 페이지와 전체 개수를 읽습니다.
|
|
235
|
-
명시적 정렬에는 마지막 동률 해소 기준으로 PK가 추가되며, `orderBy` 없는
|
|
236
|
-
페이지네이션은 PK 오름차순을 사용합니다.
|
|
88
|
+
import { createTypedSheets, defineTypedSheetsEntity } from "hikoutei";
|
|
237
89
|
|
|
238
|
-
|
|
90
|
+
const User = defineTypedSheetsEntity({
|
|
91
|
+
name: "User",
|
|
92
|
+
tableName: "users",
|
|
93
|
+
properties: {
|
|
94
|
+
id: { type: "string", primary: true },
|
|
95
|
+
name: { type: "string" },
|
|
96
|
+
age: { type: "number" },
|
|
97
|
+
active: { type: "boolean" },
|
|
98
|
+
},
|
|
99
|
+
});
|
|
239
100
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
아직 발전 중입니다. 마이너 버전 업그레이드 전에 릴리스 노트를 확인하세요.
|
|
101
|
+
const hikoutei = await createTypedSheets({
|
|
102
|
+
dbName: "./hikoutei.sqlite",
|
|
103
|
+
entities: [User],
|
|
104
|
+
});
|
|
245
105
|
|
|
246
|
-
|
|
106
|
+
const em = hikoutei.em.fork();
|
|
107
|
+
const user = em.create(User, { id: "u1", name: "Ada", age: 36, active: true });
|
|
108
|
+
em.persist(user);
|
|
109
|
+
await em.flush();
|
|
247
110
|
|
|
248
|
-
|
|
249
|
-
|
|
111
|
+
user.name = "Ada Lovelace";
|
|
112
|
+
await em.flush();
|
|
250
113
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
2. **관계와 로딩**
|
|
258
|
-
- many-to-one, one-to-many, `populate()` 기능을 추가합니다.
|
|
259
|
-
- 공개 전에 관계의 SQLite 매핑, Sheets 프로젝션 표현, 스키마 동작, 충돌
|
|
260
|
-
의미론을 함께 설계합니다.
|
|
261
|
-
3. **스키마 운영**
|
|
262
|
-
- 마이그레이션과 스키마 드리프트 관리를 추가합니다.
|
|
263
|
-
- 검증 및 운영 흐름을 기존 설정 도구와 통합합니다.
|
|
114
|
+
const loaded = await em.findOne(User, { id: "u1" });
|
|
115
|
+
if (loaded !== null) {
|
|
116
|
+
em.remove(loaded);
|
|
117
|
+
await em.flush();
|
|
118
|
+
}
|
|
119
|
+
```
|
|
264
120
|
|
|
265
|
-
|
|
121
|
+
더 많은 읽기·트랜잭션·연산자: [빠른 시작](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md).
|
|
266
122
|
|
|
267
|
-
|
|
123
|
+
쓰기는 즉시 로컬 SQLite에 커밋됩니다 — 요청은 Google을 기다리지 않습니다.
|
|
124
|
+
시트에서 사람이 편집하면 폴링으로 되돌아와 SQLite에 수용되거나 충돌로
|
|
125
|
+
기록되며, 절대 조용히 덮어쓰이지 않습니다. 전체 파이프라인(outbox, 전달,
|
|
126
|
+
충돌 처리)은 [쓰기 및 동기화 흐름](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md)을 참고하세요.
|
|
268
127
|
|
|
269
|
-
|
|
270
|
-
- 업데이트·삭제 충돌 처리와 표시 개선
|
|
128
|
+
## 더 보기
|
|
271
129
|
|
|
272
|
-
|
|
273
|
-
|
|
130
|
+
- [빠른 시작](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md) — 설치, ORM 생명주기, 동기화 설정
|
|
131
|
+
- [아키텍처](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/architecture.md) — 로컬 저장소와 Sheet 화면이 맞물리는 방식
|
|
132
|
+
- [쓰기 및 동기화 흐름](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md) — 비동기 전달과 복구 동작
|
|
133
|
+
- [한계](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/limitations.md) — 다른 도구를 선택해야 할 때
|
|
134
|
+
- [프로젝트 상태와 로드맵](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/status.md) — 완료된 것과 다음 작업
|
|
274
135
|
|
|
275
136
|
## 라이선스
|
|
276
137
|
|
|
277
|
-
Hikoutei는 [MIT 라이선스](LICENSE)로 배포됩니다.
|
|
138
|
+
Hikoutei는 [MIT 라이선스](LICENSE)로 배포됩니다.
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
+
<img src="assets/hikoutei-icon.png" alt="Hikoutei" width="220" />
|
|
6
|
+
|
|
5
7
|
# Hikoutei
|
|
6
8
|
|
|
7
9
|
**Keep your app fast with SQLite. Keep your workflow visible in Google Sheets.**
|
|
@@ -12,7 +14,7 @@ changes are asynchronously projected to Google Sheets for human review and
|
|
|
12
14
|
lightweight collaboration.
|
|
13
15
|
|
|
14
16
|
<a href="https://www.npmjs.com/package/hikoutei">npm</a> ·
|
|
15
|
-
<a href="
|
|
17
|
+
<a href="https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md">Quick start</a> ·
|
|
16
18
|
<a href="https://github.com/ManddarinShop/Hikoutei/issues">Issues</a>
|
|
17
19
|
|
|
18
20
|
[](https://www.npmjs.com/package/hikoutei)
|
|
@@ -34,46 +36,6 @@ collaboration.
|
|
|
34
36
|
> and it does not treat Google Sheets as the authoritative application
|
|
35
37
|
> database. SQLite is the source of truth; Sheets is the human-facing view.
|
|
36
38
|
|
|
37
|
-
## Quick start
|
|
38
|
-
|
|
39
|
-
Define a scalar entity and use the local SQLite authority through a
|
|
40
|
-
request-local manager.
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
import { createTypedSheets, defineTypedSheetsEntity } from "hikoutei";
|
|
44
|
-
|
|
45
|
-
const User = defineTypedSheetsEntity({
|
|
46
|
-
name: "User",
|
|
47
|
-
tableName: "users",
|
|
48
|
-
properties: {
|
|
49
|
-
id: { type: "string", primary: true },
|
|
50
|
-
name: { type: "string" },
|
|
51
|
-
age: { type: "number" },
|
|
52
|
-
active: { type: "boolean" },
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const hikoutei = await createTypedSheets({
|
|
57
|
-
dbName: "./hikoutei.sqlite",
|
|
58
|
-
entities: [User],
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const em = hikoutei.em.fork();
|
|
62
|
-
const user = em.create(User, { id: "u1", name: "Ada", age: 36, active: true });
|
|
63
|
-
em.persist(user);
|
|
64
|
-
await em.flush();
|
|
65
|
-
|
|
66
|
-
user.name = "Ada Lovelace";
|
|
67
|
-
await em.flush();
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**What happens to the Sheet?** The write commits to local SQLite immediately —
|
|
71
|
-
the application request never waits on Google. When the sync service is
|
|
72
|
-
enabled, Hikoutei later projects the entity to the registered Google Sheet in
|
|
73
|
-
the background. Human edits made in the sheet are observed, validated, and
|
|
74
|
-
either accepted back into SQLite or recorded as conflicts, never silently
|
|
75
|
-
overwritten.
|
|
76
|
-
|
|
77
39
|
## Why Hikoutei?
|
|
78
40
|
|
|
79
41
|
- Define typed entities instead of manually converting Sheet rows.
|
|
@@ -82,29 +44,6 @@ overwritten.
|
|
|
82
44
|
- Detect unexpected column changes and duplicate headers.
|
|
83
45
|
- Avoid overwriting newer Sheet edits during conflicting updates.
|
|
84
46
|
|
|
85
|
-
## When to use Hikoutei
|
|
86
|
-
|
|
87
|
-
Hikoutei is a good fit for:
|
|
88
|
-
|
|
89
|
-
- MVPs and prototypes where a spreadsheet is part of the product workflow.
|
|
90
|
-
- Internal tools and low-traffic administrative applications.
|
|
91
|
-
- Teams that want typed application data while keeping Sheets easy for people
|
|
92
|
-
to inspect.
|
|
93
|
-
- Services that can use SQLite locally and accept asynchronous Sheet updates.
|
|
94
|
-
|
|
95
|
-
## When to choose something else
|
|
96
|
-
|
|
97
|
-
Use a conventional database and direct Google APIs when you need:
|
|
98
|
-
|
|
99
|
-
- Strong transactions across many rows or services.
|
|
100
|
-
- High write throughput or many concurrent writers.
|
|
101
|
-
- Complex queries, joins, or reporting workloads.
|
|
102
|
-
- Multi-server or multi-region coordination.
|
|
103
|
-
- Immediate read-after-write consistency in Google Sheets.
|
|
104
|
-
- Google Sheets to be the primary database for the application.
|
|
105
|
-
|
|
106
|
-
## Is Hikoutei the right abstraction for you?
|
|
107
|
-
|
|
108
47
|
Hikoutei does not replace `google-spreadsheet` or `@googleapis/sheets` — it
|
|
109
48
|
sits one level above them. If you only need raw spreadsheet access, use the API
|
|
110
49
|
client directly.
|
|
@@ -119,289 +58,83 @@ client directly.
|
|
|
119
58
|
| Direct row and cell manipulation | Limited | ✅ | ✅ |
|
|
120
59
|
| Full Google Sheets API access | Provider only | Partial | ✅ |
|
|
121
60
|
|
|
122
|
-
##
|
|
123
|
-
|
|
124
|
-
Google Sheets synchronization is a service-side concern. Applications do not
|
|
125
|
-
import a provider client, pass Sheet routes to `createTypedSheets()`, or choose
|
|
126
|
-
an operation for each write — `CreateTypedSheetsOptions` fields (`dbName`,
|
|
127
|
-
`entities`, `providerOptions`) are all optional. The sync runtime uses one
|
|
128
|
-
internal Google Sheets API provider with a service account — no Apps Script
|
|
129
|
-
deployment. Sync auto-start is selected by `HIKOUTEI_SYNC_SPREADSHEET_URL`
|
|
130
|
-
plus `GOOGLE_APPLICATION_CREDENTIALS`. Optional `providerOptions` tunes the
|
|
131
|
-
sync-path provider (telemetry/timeouts; inert when local-only), and
|
|
132
|
-
`createTypedSheetsWithSync()` exposes the same options with a richer result
|
|
133
|
-
for existing-sheet adoption.
|
|
134
|
-
|
|
135
|
-
**Fastest path:** install the gcloud CLI, then run `npx hikoutei setup` from your
|
|
136
|
-
project directory. On an interactive terminal it offers (press Enter) to
|
|
137
|
-
start `gcloud auth login --enable-gdrive-access --force` for you when the
|
|
138
|
-
active account is missing or lacks Drive access — you only complete the
|
|
139
|
-
browser approval yourself. (In `--yes`, CI, or non-TTY sessions, run that
|
|
140
|
-
login command yourself first.) Setup then creates the project, service
|
|
141
|
-
account, and key, creates a spreadsheet owned by your account, shares it
|
|
142
|
-
with the service account as an Editor, verifies service-account access, and
|
|
143
|
-
writes `GOOGLE_APPLICATION_CREDENTIALS` plus `HIKOUTEI_SYNC_SPREADSHEET_URL`
|
|
144
|
-
into your `.env`. The human access token is used in memory only and never
|
|
145
|
-
stored. Automatic setup runs on macOS and Linux; on Windows a non-dry-run
|
|
146
|
-
is refused before any mutation and manual setup is available. Interrupted runs resume from a local checkpoint
|
|
147
|
-
(`.hikoutei-setup-state.json`); a spreadsheet create whose outcome is
|
|
148
|
-
unknown is reconciled by its creation marker on the next run and setup
|
|
149
|
-
never creates a second spreadsheet (inspect Drive and rerun if setup
|
|
150
|
-
reports `sheet_create_uncertain`, and a create rejected up front with
|
|
151
|
-
HTTP 400/403 plus a confirmed-zero marker lookup rolls back to `key_ready`
|
|
152
|
-
so a corrected rerun starts a fresh marker). Sharing is write-ahead too:
|
|
153
|
-
`spreadsheet_share_started` is persisted before the idempotent SA writer
|
|
154
|
-
permission ensure and `spreadsheet_shared` after it, so a crash between
|
|
155
|
-
the remote permission mutation and the checkpoint write resumes the
|
|
156
|
-
ensure on the next run and never creates a second spreadsheet. The
|
|
157
|
-
service-account key is
|
|
158
|
-
created under a write-ahead contract too: the user-managed key list is
|
|
159
|
-
recorded as a baseline before the single gcloud key create, and
|
|
160
|
-
`key_create_started`/`key_ready` checkpoints let a crashed run recover a
|
|
161
|
-
staged or installed key instead of creating a second one. Only the
|
|
162
|
-
invocation that just persisted `key_create_started` may issue the one key
|
|
163
|
-
create; resumed runs are reconcile-only and, when no credential and no
|
|
164
|
-
post-baseline key are visible, poll the key list plus staged/final
|
|
165
|
-
evidence for up to two minutes (2, 4, 8, 16, 30, 30, 30 s) before failing
|
|
166
|
-
with `key_create_uncertain` — the create is never retried automatically.
|
|
167
|
-
An unmatched user-managed key with no local credential is never deleted
|
|
168
|
-
automatically — setup fails with `key_create_uncertain` and you inspect
|
|
169
|
-
the key list in the Google Cloud console before rerunning (a
|
|
170
|
-
verified-absent state requires removing the setup state file to reset the
|
|
171
|
-
key checkpoint); reused keys are enforced to owner-only mode 600. An exclusive lock directory
|
|
172
|
-
(`.hikoutei-setup-state.json.lock`) prevents concurrent runs and is never
|
|
173
|
-
removed automatically: a crash leaves an empty lock directory behind, and
|
|
174
|
-
removing it manually is required only when you are certain no setup is
|
|
175
|
-
running. Starting fresh requires removing or moving both the checkpoint and
|
|
176
|
-
the key file, or passing `--project` to recover an existing key —
|
|
177
|
-
checkpointed or identity-matched cloud resources are reused, and setup
|
|
178
|
-
never deletes cloud resources. The manual steps below remain available for
|
|
179
|
-
advanced setups.
|
|
180
|
-
|
|
181
|
-
**Setup progress.** `hikoutei setup` reports step-by-step progress to
|
|
182
|
-
**stderr** across the ten setup phases (cloud auth, Drive access, project,
|
|
183
|
-
APIs, service account, service-account key, spreadsheet, share,
|
|
184
|
-
service-account access, output): an **overall bar** advances only when a
|
|
185
|
-
phase actually completes — it is never an ETA and never guesses a
|
|
186
|
-
percentage — and a **detail line** shows the bounded propagation checks
|
|
187
|
-
(how many of the eight key/access checks have run) and the known 2, 4, 8,
|
|
188
|
-
16, 30, 30, 30 s waits, with a fixed `working…` label for unknown-duration
|
|
189
|
-
steps. On an interactive terminal the four-line block redraws in place;
|
|
190
|
-
in CI, non-TTY, or `NO_COLOR` sessions one static line is printed per
|
|
191
|
-
phase/retry event with no control sequences. Progress pauses and the
|
|
192
|
-
block is cleared during the interactive `gcloud auth login` handoff and
|
|
193
|
-
resumes with the retry. Progress never prints credentials, tokens, keys,
|
|
194
|
-
project ids, emails, paths, or raw command output, and it can never
|
|
195
|
-
change the setup result or exit code; `--dry-run` prints the command
|
|
196
|
-
plan only.
|
|
197
|
-
|
|
198
|
-
**Keep setup artifacts out of Git.** `hikoutei setup` writes its defaults
|
|
199
|
-
into the current directory: the service-account key
|
|
200
|
-
(`hikoutei-service-account.json`, owner-only mode 600 — a secret), the
|
|
201
|
-
resume checkpoint (`.hikoutei-setup-state.json` and its `.tmp`/unique-temp
|
|
202
|
-
and `.lock` siblings), private key staging/cleanup directories
|
|
203
|
-
(`.hikoutei-key-stage-*`, `.hikoutei-key-cleanup-*`), and
|
|
204
|
-
`.hikoutei-env-*` temporary env writes. The repository's `.gitignore`
|
|
205
|
-
already ignores these defaults, so a plain `git add .` does not pick them
|
|
206
|
-
up. A `.gitignore` is **not a security boundary**, though: it only keeps
|
|
207
|
-
untracked files out of `git add`, and it does not protect files that are
|
|
208
|
-
already tracked (remove a mistakenly tracked key from history and rotate
|
|
209
|
-
it — delete the user-managed key in the Cloud console and rerun setup).
|
|
210
|
-
When you use a custom `--output` or keep the key or checkpoint at custom
|
|
211
|
-
paths, add those exact paths to your application's ignore rules and never
|
|
212
|
-
commit them.
|
|
213
|
-
|
|
214
|
-
### Env-driven sync auto-start
|
|
215
|
-
|
|
216
|
-
Set the spreadsheet URL in the environment and `createTypedSheets()` starts
|
|
217
|
-
the Sheets sync internally — `flush()` then flows to Google Sheets through
|
|
218
|
-
the outbox worker with no per-call setup:
|
|
61
|
+
## Installation
|
|
219
62
|
|
|
220
63
|
```sh
|
|
221
|
-
|
|
222
|
-
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities: [User] });
|
|
64
|
+
npm install hikoutei @mikro-orm/core @mikro-orm/sql
|
|
227
65
|
```
|
|
228
66
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
shared on the spreadsheet (the error tells you which email to share).
|
|
233
|
-
|
|
234
|
-
### Manual service-account setup
|
|
235
|
-
|
|
236
|
-
1. **Create a service account.** Enable the Google Sheets API in a Cloud
|
|
237
|
-
project, create a service account with the
|
|
238
|
-
`https://www.googleapis.com/auth/spreadsheets` scope, and share the target
|
|
239
|
-
spreadsheet with its email as an **Editor**. The provider creates tabs,
|
|
240
|
-
writes effect rows and receipt records, and manages row anchors, so Viewer
|
|
241
|
-
access is not enough.
|
|
242
|
-
2. **Keep the key server-side.** Put the service-account key path in
|
|
243
|
-
`GOOGLE_APPLICATION_CREDENTIALS` on the server and the spreadsheet ID in an
|
|
244
|
-
untracked secret store. Never put the key in browser code or Git: add the
|
|
245
|
-
key path (and any custom `.env`/checkpoint paths) to the application's
|
|
246
|
-
`.gitignore` — the defaults created by `hikoutei setup` are already
|
|
247
|
-
ignored, but a `.gitignore` is not a security boundary and does not
|
|
248
|
-
protect already-tracked files.
|
|
249
|
-
3. **Run the application normally.** Start the app with
|
|
250
|
-
`GOOGLE_APPLICATION_CREDENTIALS` and `HIKOUTEI_SYNC_SPREADSHEET_URL` set;
|
|
251
|
-
`createTypedSheets()` detects them and starts the internal sync bootstrap —
|
|
252
|
-
it creates and verifies headers on the registered tabs, then starts outbox
|
|
253
|
-
delivery and User_Input polling. Pass `providerOptions` only when sync-path
|
|
254
|
-
tuning is needed; there is no internal bootstrap to start by hand.
|
|
255
|
-
|
|
256
|
-
> **Legacy spreadsheet note.** Spreadsheets provisioned by the old Apps Script
|
|
257
|
-
> provider with developer-metadata row anchors are not migrated: `User_Input`
|
|
258
|
-
> tabs now require the `__hikoutei_row_id` system column, so legacy tabs must
|
|
259
|
-
> be re-provisioned. Current provisioning also adds one internal formula
|
|
260
|
-
> column, `__hikoutei_row_check`, directly after each `User_Input` tab's
|
|
261
|
-
> registered range: Hikoutei writes a change-detection formula per row, and
|
|
262
|
-
> polling reads only that column to spot human edits (rows without it fall
|
|
263
|
-
> back to full reads). Do not edit, sort, or delete the column or its
|
|
264
|
-
> formulas.
|
|
265
|
-
|
|
266
|
-
Hikoutei uses a durable local outbox, idempotent delivery, and conflict-aware
|
|
267
|
-
updates so temporary API failures do not lose committed application writes. The
|
|
268
|
-
provider never logs credentials, spreadsheet IDs, URLs, or payloads, and it
|
|
269
|
-
spaces request starts to stay inside Google's quota windows. See the
|
|
270
|
-
[internal consistency model](website/guide/internal-consistency.md) for the
|
|
271
|
-
detailed state machine and recovery rules.
|
|
272
|
-
|
|
273
|
-
Live Google calls are opt-in; fake providers and SQLite fixtures are the normal
|
|
274
|
-
verification path. The detailed setup and troubleshooting steps are in the
|
|
275
|
-
[Quick start](website/guide/quick-start.md) and [Google Sheets setup](website/guide/setup.md) —
|
|
276
|
-
including the credential pool (`--sa-count`) and API quota guidance when
|
|
277
|
-
polling-heavy workloads hit HTTP 429.
|
|
67
|
+
Installing the library does not touch Google Cloud — it runs local-only
|
|
68
|
+
(SQLite) by default. Run the setup command below only when you want Sheet
|
|
69
|
+
sync.
|
|
278
70
|
|
|
279
|
-
##
|
|
71
|
+
## Setup (Google Sheets sync)
|
|
280
72
|
|
|
281
|
-
|
|
282
|
-
(packageManager `pnpm@11.1.2`):
|
|
73
|
+
One-time, interactive. Install the gcloud CLI, then:
|
|
283
74
|
|
|
284
75
|
```sh
|
|
285
|
-
|
|
76
|
+
npx hikoutei setup
|
|
286
77
|
```
|
|
287
78
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
- [Write and synchronization flow](website/guide/sync-flow.md) —
|
|
298
|
-
asynchronous delivery and recovery behavior.
|
|
299
|
-
- [Internal consistency model](website/guide/internal-consistency.md) —
|
|
300
|
-
durable outbox, idempotent delivery, and conflict-aware updates.
|
|
301
|
-
- [Contributing](website/guide/contributing.md) — local development and test
|
|
302
|
-
commands.
|
|
303
|
-
- [Soak testing](website/guide/soak-testing.md) — the long-duration soak
|
|
304
|
-
runner: source build, six scalar tables, 6h preflight and 24h direct-live
|
|
305
|
-
runs, log envs, redaction contract, resume/cleanup, and acceptance
|
|
306
|
-
criteria.
|
|
307
|
-
- [Benchmark notes](website/guide/benchmarks.md) — dated measurements and
|
|
308
|
-
their limitations.
|
|
309
|
-
|
|
310
|
-
## Limitations
|
|
311
|
-
|
|
312
|
-
- Google Sheets has quota, latency, and API rate limits. The outbound sync
|
|
313
|
-
worker batches effects to the spreadsheet scope, so remote request volume
|
|
314
|
-
does not grow with the number of tabs a dispatch touches.
|
|
315
|
-
- Sheet updates are asynchronous; the application should read its local state.
|
|
316
|
-
- SQLite is local to the service and is not a distributed coordination layer.
|
|
317
|
-
- Schema changes, manual edits, and conflicting updates still need an
|
|
318
|
-
operational policy from the application.
|
|
319
|
-
- The EntityManager is an ORM-style facade over scalar entities, not a full
|
|
320
|
-
ORM: entity definitions are scalar-only (`string`, `number`, `boolean`,
|
|
321
|
-
`date`), and v1 permits uniqueness only on the primary/business key.
|
|
322
|
-
- Relations, joins, `populate()`, migrations, cascades, bulk/ORM query
|
|
323
|
-
builders, and raw SQL are unsupported in this milestone. Sheets is an async
|
|
324
|
-
projection and human input surface, never a live query database.
|
|
325
|
-
|
|
326
|
-
## Local queries
|
|
327
|
-
|
|
328
|
-
Reads use Hikoutei-owned typed operators and always execute against SQLite:
|
|
79
|
+
This creates the Cloud project, service account, key, and spreadsheet, and
|
|
80
|
+
writes `.env` for you. Without `HIKOUTEI_SYNC_SPREADSHEET_URL`,
|
|
81
|
+
`createTypedSheets()` stays local-only (SQLite). Details, credential pools,
|
|
82
|
+
quota guidance, and manual setup: [Google Sheets setup](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/setup.md).
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
Define a scalar entity and use the local SQLite authority through a
|
|
87
|
+
request-local manager.
|
|
329
88
|
|
|
330
89
|
```ts
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
offset: 0,
|
|
90
|
+
import { createTypedSheets, defineTypedSheetsEntity } from "hikoutei";
|
|
91
|
+
|
|
92
|
+
const User = defineTypedSheetsEntity({
|
|
93
|
+
name: "User",
|
|
94
|
+
tableName: "users",
|
|
95
|
+
properties: {
|
|
96
|
+
id: { type: "string", primary: true },
|
|
97
|
+
name: { type: "string" },
|
|
98
|
+
age: { type: "number" },
|
|
99
|
+
active: { type: "boolean" },
|
|
342
100
|
},
|
|
343
|
-
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const hikoutei = await createTypedSheets({
|
|
104
|
+
dbName: "./hikoutei.sqlite",
|
|
105
|
+
entities: [User],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const em = hikoutei.em.fork();
|
|
109
|
+
const user = em.create(User, { id: "u1", name: "Ada", age: 36, active: true });
|
|
110
|
+
em.persist(user);
|
|
111
|
+
await em.flush();
|
|
112
|
+
|
|
113
|
+
user.name = "Ada Lovelace";
|
|
114
|
+
await em.flush();
|
|
115
|
+
|
|
116
|
+
const loaded = await em.findOne(User, { id: "u1" });
|
|
117
|
+
if (loaded !== null) {
|
|
118
|
+
em.remove(loaded);
|
|
119
|
+
await em.flush();
|
|
120
|
+
}
|
|
344
121
|
```
|
|
345
122
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
`HIKOUTEI_ERROR_CODES` constants (`HIKOUTEI_ERROR_CODES.INVALID_QUERY` and
|
|
362
|
-
`HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE`, which resolve to the lowercase
|
|
363
|
-
runtime codes `invalid_query` and `invalid_scalar_value`) instead of guessing
|
|
364
|
-
strings or parsing messages.
|
|
365
|
-
|
|
366
|
-
## Project status
|
|
367
|
-
|
|
368
|
-
Hikoutei is in active development. The current EntityManager supports scalar
|
|
369
|
-
entity lifecycle operations, typed local filters and ordering, `limit` /
|
|
370
|
-
`offset` pagination, `count()`, snapshot-consistent `findAndCount()`, and
|
|
371
|
-
callback-style `transactional()` work. Normal reads always come from SQLite,
|
|
372
|
-
never Google Sheets. Sheet edit ingestion and conflict presentation are still
|
|
373
|
-
evolving. Review release notes before upgrading minor versions.
|
|
374
|
-
|
|
375
|
-
## Roadmap
|
|
376
|
-
|
|
377
|
-
The first EntityManager milestone, rich local reads, is complete. Remaining
|
|
378
|
-
milestones follow the implementation order below; no milestone is tied to a
|
|
379
|
-
date or release number.
|
|
380
|
-
|
|
381
|
-
1. **Lifecycle-safe writes**
|
|
382
|
-
- Add `upsert` and direct/bulk mutation capabilities only through a
|
|
383
|
-
Hikoutei-owned contract that preserves one SQLite transaction across the
|
|
384
|
-
entity table, canonical state, and durable Sheet effect outbox.
|
|
385
|
-
- Do not promise raw `nativeInsert`, `nativeUpdate`, `nativeDelete`, or SQL
|
|
386
|
-
pass-through APIs that could bypass that atomic lifecycle.
|
|
387
|
-
2. **Relationships and loading**
|
|
388
|
-
- Add many-to-one, one-to-many, and `populate()` capabilities.
|
|
389
|
-
- Design SQLite relationship mapping, Sheets projection representation,
|
|
390
|
-
schema behavior, and conflict semantics together before public release.
|
|
391
|
-
3. **Schema operations**
|
|
392
|
-
- Add migration and schema drift management.
|
|
393
|
-
- Integrate validation and operational workflows with the existing setup
|
|
394
|
-
tooling.
|
|
395
|
-
|
|
396
|
-
### Synchronization and operations
|
|
397
|
-
|
|
398
|
-
The following work continues in parallel with the EntityManager milestones:
|
|
399
|
-
|
|
400
|
-
- Complete ingestion of intentional user edits from Google Sheets.
|
|
401
|
-
- Improve update/delete conflict handling and presentation.
|
|
402
|
-
|
|
403
|
-
See the [open issues](https://github.com/ManddarinShop/Hikoutei/issues)
|
|
404
|
-
for current work.
|
|
123
|
+
More reads, transactions, and operators: [Quick start](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md).
|
|
124
|
+
|
|
125
|
+
Writes commit to local SQLite immediately — the request never waits on Google.
|
|
126
|
+
Human edits in the Sheet flow back through polling — accepted into SQLite or
|
|
127
|
+
recorded as conflicts, never silently overwritten. The full pipeline (outbox,
|
|
128
|
+
delivery, conflict handling) is covered in
|
|
129
|
+
[Write and synchronization flow](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md).
|
|
130
|
+
|
|
131
|
+
## Learn more
|
|
132
|
+
|
|
133
|
+
- [Quick start](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/quick-start.md) — installation, ORM lifecycle, sync setup.
|
|
134
|
+
- [Architecture](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/architecture.md) — local store and Sheet views.
|
|
135
|
+
- [Write and synchronization flow](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/sync-flow.md) — delivery and recovery.
|
|
136
|
+
- [Limitations](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/limitations.md) — when to choose something else.
|
|
137
|
+
- [Project status and roadmap](https://github.com/ManddarinShop/Hikoutei-Website-/blob/main/guide/status.md) — what is done and next.
|
|
405
138
|
|
|
406
139
|
## License
|
|
407
140
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hikoutei",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.20-dev",
|
|
4
4
|
"description": "Typed repository and safe write layer for Google Sheets-backed MVPs. SQLite-authoritative entity lifecycle with asynchronous Google Sheets projection.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|