hikoutei 0.5.20 → 0.5.22
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 +23 -10
- package/README.ko.md +19 -8
- package/README.md +22 -11
- package/dist/adapter/persistence/contracts/scalar.d.ts +34 -0
- package/dist/adapter/persistence/contracts/scalar.d.ts.map +1 -1
- package/dist/adapter/persistence/contracts/scalar.js +6 -1
- package/dist/adapter/persistence/contracts/scalar.js.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts +19 -20
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js +117 -67
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.d.ts +31 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/{MikroOrmMappedTypedSheets.js → MikroOrmMappedRuntime.js} +10 -31
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarEntityRuntime.js.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarRuntime.js.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.d.ts +12 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.js +20 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts +2 -7
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts.map +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js +5 -11
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js.map +1 -1
- package/dist/api/Hikoutei.d.ts.map +1 -1
- package/dist/api/Hikoutei.js +3 -4
- package/dist/api/Hikoutei.js.map +1 -1
- package/dist/api/errors.d.ts +2 -0
- package/dist/api/errors.d.ts.map +1 -1
- package/dist/api/errors.js +2 -0
- package/dist/api/errors.js.map +1 -1
- package/dist/api/internalEntityManager.d.ts.map +1 -1
- package/dist/api/internalEntityManager.js +31 -9
- package/dist/api/internalEntityManager.js.map +1 -1
- package/dist/application/orm/mapping/contracts.d.ts +8 -3
- package/dist/application/orm/mapping/contracts.d.ts.map +1 -1
- package/dist/application/orm/mapping/values.d.ts +2 -0
- package/dist/application/orm/mapping/values.d.ts.map +1 -1
- package/dist/application/orm/mapping/values.js +10 -3
- package/dist/application/orm/mapping/values.js.map +1 -1
- package/dist/application/orm/persistence/flush/flushCoordinator.d.ts +2 -2
- package/dist/application/orm/persistence/flush/flushCoordinator.d.ts.map +1 -1
- package/dist/application/orm/persistence/flush/flushCoordinator.js +9 -16
- package/dist/application/orm/persistence/flush/flushCoordinator.js.map +1 -1
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.d.ts.map +1 -1
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.js +26 -13
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.js.map +1 -1
- package/dist/application/orm/persistence/projection/projectionEffects.d.ts +2 -2
- package/dist/application/orm/persistence/projection/projectionEffects.d.ts.map +1 -1
- package/dist/application/orm/persistence/projection/projectionEffects.js +6 -6
- package/dist/application/orm/persistence/projection/projectionEffects.js.map +1 -1
- package/dist/application/orm/persistence/scalar/unitOfWork.d.ts +21 -2
- package/dist/application/orm/persistence/scalar/unitOfWork.d.ts.map +1 -1
- package/dist/application/orm/persistence/scalar/unitOfWork.js +107 -8
- package/dist/application/orm/persistence/scalar/unitOfWork.js.map +1 -1
- package/dist/application/orm/persistence/support/contracts.d.ts +6 -6
- package/dist/application/orm/persistence/support/contracts.d.ts.map +1 -1
- package/dist/application/orm/persistence/support/contracts.js +0 -2
- package/dist/application/orm/persistence/support/contracts.js.map +1 -1
- package/dist/application/orm/persistence/support/helpers.d.ts +3 -3
- package/dist/application/orm/persistence/support/helpers.d.ts.map +1 -1
- package/dist/application/orm/persistence/support/helpers.js +12 -9
- package/dist/application/orm/persistence/support/helpers.js.map +1 -1
- package/dist/application/orm/persistence/support/timing.d.ts +2 -2
- package/dist/application/orm/persistence/support/timing.d.ts.map +1 -1
- package/dist/application/orm/persistence/support/timing.js +4 -4
- package/dist/application/orm/persistence/support/timing.js.map +1 -1
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
- package/dist/application/sync/service/SyncServiceBootstrap.js +3 -3
- package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
- package/package.json +1 -1
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts +0 -55
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js +0 -120
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts +0 -56
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.js.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts +0 -38
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js +0 -57
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts +0 -19
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts.map +0 -1
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js +0 -67
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js.map +0 -1
- package/dist/application/orm/api/TypedSheetsOrm.d.ts +0 -71
- package/dist/application/orm/api/TypedSheetsOrm.d.ts.map +0 -1
- package/dist/application/orm/api/TypedSheetsOrm.js +0 -116
- package/dist/application/orm/api/TypedSheetsOrm.js.map +0 -1
- package/dist/application/orm/api/contracts.d.ts +0 -118
- package/dist/application/orm/api/contracts.d.ts.map +0 -1
- package/dist/application/orm/api/contracts.js +0 -21
- package/dist/application/orm/api/contracts.js.map +0 -1
package/README.ja.md
CHANGED
|
@@ -121,9 +121,12 @@ API クライアントを直接使ってください。
|
|
|
121
121
|
|
|
122
122
|
Google Sheets の同期はサービス側の関心事です。アプリケーションは provider
|
|
123
123
|
クライアントを import したり、`createTypedSheets()` にシートルートを渡したり、
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
書き込みごとに操作を選んだりしません — ルート API が受け取るのは `dbName` と
|
|
125
|
+
`entities` だけです。同期ランタイムはサービスアカウントを使う単一の内部
|
|
126
|
+
Google Sheets API provider を使用します — Apps Script のデプロイは不要です。
|
|
127
|
+
同期の自動開始は `HIKOUTEI_SYNC_SPREADSHEET_URL` と
|
|
128
|
+
`GOOGLE_APPLICATION_CREDENTIALS` で選択され、設定する公開
|
|
129
|
+
`googleSheetsApi` bootstrap オプションはありません。
|
|
127
130
|
|
|
128
131
|
### 環境変数による同期の自動開始
|
|
129
132
|
|
|
@@ -145,7 +148,7 @@ const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities
|
|
|
145
148
|
不正、資格情報ファイルの欠落・不正、スプレッドシートに共有されていない
|
|
146
149
|
サービスアカウント (共有すべきメールアドレスをエラーが教えてくれます)。
|
|
147
150
|
|
|
148
|
-
###
|
|
151
|
+
### 手動でのサービスアカウント設定
|
|
149
152
|
|
|
150
153
|
1. **サービスアカウントを作成する。** Cloud プロジェクトで Google Sheets API
|
|
151
154
|
を有効化し、`https://www.googleapis.com/auth/spreadsheets` スコープの
|
|
@@ -157,9 +160,17 @@ const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities
|
|
|
157
160
|
`GOOGLE_APPLICATION_CREDENTIALS` に、スプレッドシート ID は追跡されない
|
|
158
161
|
シークレットストアに置きます。キーをブラウザコードや Git に入れないで
|
|
159
162
|
ください。
|
|
160
|
-
3.
|
|
161
|
-
|
|
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
|
+
> レガシータブは再プロビジョニングが必要です。
|
|
163
174
|
|
|
164
175
|
Hikoutei は、永続的なローカル outbox・冪等な配信・競合を考慮した更新を使う
|
|
165
176
|
ため、一時的な API 障害でコミット済みのアプリケーション書き込みが失われる
|
|
@@ -211,8 +222,11 @@ Hikoutei は活発に開発中です。現在の EntityManager は、スカラ
|
|
|
211
222
|
`limit` / `offset` ページネーション、コールバック形式の `transactional()` を
|
|
212
223
|
サポートします。通常の読み取り元は常に SQLite であり、Google Sheets では
|
|
213
224
|
ありません。
|
|
214
|
-
|
|
215
|
-
|
|
225
|
+
`hikoutei setup` CLI がスプレッドシートとサービスアカウントをプロビジョニ
|
|
226
|
+
ングし、direct Google Sheets API provider(環境変数による自動開始)が唯一の
|
|
227
|
+
同期経路です — Apps Script ゲートウェイはありません。シート編集の取り込みと
|
|
228
|
+
競合表示はまだ発展途上です。マイナーバージョンのアップグレード前にリリース
|
|
229
|
+
ノートを確認してください。
|
|
216
230
|
|
|
217
231
|
## ロードマップ
|
|
218
232
|
|
|
@@ -245,7 +259,6 @@ EntityManager のロードマップは、以下の実装順序に従います。
|
|
|
245
259
|
|
|
246
260
|
- Google Sheets からの意図的なユーザー編集の取り込みを完了する
|
|
247
261
|
- 更新・削除の競合処理と表示を改善する
|
|
248
|
-
- レジストリと direct-provider デプロイのセットアップツールを改善する
|
|
249
262
|
|
|
250
263
|
現在の作業は[オープンな Issues](https://github.com/ManddarinShop/Hikoutei/issues)を
|
|
251
264
|
参照してください。
|
package/README.ko.md
CHANGED
|
@@ -118,9 +118,11 @@ Hikoutei는 `google-spreadsheet`나 `@googleapis/sheets`를 대체하지 않습
|
|
|
118
118
|
|
|
119
119
|
Google Sheets 동기화는 서비스 측 관심사입니다. 애플리케이션은 provider
|
|
120
120
|
클라이언트를 import하거나, Sheet 라우트를 `createTypedSheets()`에 넘기거나,
|
|
121
|
-
쓰기마다 연산을 선택하지
|
|
122
|
-
|
|
123
|
-
사용합니다 — Apps Script 배포가 없습니다.
|
|
121
|
+
쓰기마다 연산을 선택하지 않습니다 — 루트 API는 `dbName`과 `entities`만
|
|
122
|
+
받습니다. 동기화 런타임은 서비스 계정을 사용하는 하나의 내부 Google Sheets
|
|
123
|
+
API provider를 사용합니다 — Apps Script 배포가 없습니다. 동기화 자동 시작은
|
|
124
|
+
`HIKOUTEI_SYNC_SPREADSHEET_URL`과 `GOOGLE_APPLICATION_CREDENTIALS`로
|
|
125
|
+
선택되며, 설정할 공개 `googleSheetsApi` bootstrap 옵션은 없습니다.
|
|
124
126
|
|
|
125
127
|
### 환경 변수 기반 동기화 자동 시작
|
|
126
128
|
|
|
@@ -142,7 +144,7 @@ const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities
|
|
|
142
144
|
없거나 잘못된 자격 증명 파일, 스프레드시트에 공유되지 않은 서비스 계정
|
|
143
145
|
(어떤 이메일을 공유해야 하는지 에러가 알려줍니다).
|
|
144
146
|
|
|
145
|
-
###
|
|
147
|
+
### 수동 서비스 계정 설정
|
|
146
148
|
|
|
147
149
|
1. **서비스 계정을 만듭니다.** Cloud 프로젝트에서 Google Sheets API를
|
|
148
150
|
활성화하고, `https://www.googleapis.com/auth/spreadsheets` 스코프의
|
|
@@ -152,9 +154,16 @@ const hikoutei = await createTypedSheets({ dbName: "./hikoutei.sqlite", entities
|
|
|
152
154
|
2. **키를 서버 측에 둡니다.** 서비스 계정 키 경로를 서버의
|
|
153
155
|
`GOOGLE_APPLICATION_CREDENTIALS`에, 스프레드시트 ID는 추적되지 않는
|
|
154
156
|
비밀 저장소에 둡니다. 키를 브라우저 코드나 Git에 넣지 마세요.
|
|
155
|
-
3.
|
|
157
|
+
3. **애플리케이션을 정상적으로 실행합니다.** `GOOGLE_APPLICATION_CREDENTIALS`와
|
|
158
|
+
`HIKOUTEI_SYNC_SPREADSHEET_URL`을 설정한 상태로 앱을 시작하면
|
|
159
|
+
`createTypedSheets()`가 이를 감지해 내부 sync bootstrap을 시작합니다 —
|
|
156
160
|
등록된 탭의 헤더를 만들고 검증한 뒤 outbox 전달과 User_Input 폴링을
|
|
157
|
-
시작합니다.
|
|
161
|
+
시작합니다. 넘길 provider 옵션이나 직접 시작할 내부 bootstrap은 없습니다.
|
|
162
|
+
|
|
163
|
+
> **레거시 스프레드시트 참고.** 이전 Apps Script provider가 developer
|
|
164
|
+
> metadata 행 anchor로 프로비저닝한 스프레드시트는 마이그레이션되지
|
|
165
|
+
> 않습니다. `User_Input` 탭은 이제 `__hikoutei_row_id` 시스템 컬럼이
|
|
166
|
+
> 필요하므로 레거시 탭을 다시 프로비저닝해야 합니다.
|
|
158
167
|
|
|
159
168
|
Hikoutei는 내구성 있는 로컬 outbox, 멱등 전달, 충돌을 인지하는 업데이트를
|
|
160
169
|
사용하므로 일시적인 API 실패가 커밋된 애플리케이션 쓰기를 잃게 하지 않습니다.
|
|
@@ -204,7 +213,10 @@ MikroORM은 구현 세부 사항이며 Hikoutei의 공개 엔티티 API에는
|
|
|
204
213
|
Hikoutei는 활발히 개발 중입니다. 현재 EntityManager는 스칼라 엔티티의 생명주기
|
|
205
214
|
작업, 동등 조건을 사용하는 `find()` / `findOne()`, `find()`의 `limit` /
|
|
206
215
|
`offset` 페이지네이션, 콜백형 `transactional()`을 지원합니다. 일반 읽기는
|
|
207
|
-
Google Sheets가 아니라 항상 SQLite에서 수행됩니다.
|
|
216
|
+
Google Sheets가 아니라 항상 SQLite에서 수행됩니다. `hikoutei setup` CLI가
|
|
217
|
+
스프레드시트와 서비스 계정을 프로비저닝하며, direct Google Sheets API
|
|
218
|
+
provider(환경 변수 자동 시작)가 유일한 sync 경로입니다 — Apps Script
|
|
219
|
+
gateway가 없습니다. 시트 편집 수집과 충돌 표시는
|
|
208
220
|
아직 발전 중입니다. 마이너 버전 업그레이드 전에 릴리스 노트를 확인하세요.
|
|
209
221
|
|
|
210
222
|
## 로드맵
|
|
@@ -238,7 +250,6 @@ EntityManager 로드맵은 아래 구현 순서를 따릅니다. 단계 순서
|
|
|
238
250
|
|
|
239
251
|
- Google Sheets에서 의도적인 사용자 편집 수집 완성
|
|
240
252
|
- 업데이트·삭제 충돌 처리와 표시 개선
|
|
241
|
-
- 레지스트리 및 direct-provider 배포를 위한 설정 도구 개선
|
|
242
253
|
|
|
243
254
|
현재 작업은 [오픈 이슈](https://github.com/ManddarinShop/Hikoutei/issues)를
|
|
244
255
|
참고하세요.
|
package/README.md
CHANGED
|
@@ -121,9 +121,11 @@ client directly.
|
|
|
121
121
|
|
|
122
122
|
Google Sheets synchronization is a service-side concern. Applications do not
|
|
123
123
|
import a provider client, pass Sheet routes to `createTypedSheets()`, or choose
|
|
124
|
-
an operation for each write
|
|
125
|
-
|
|
126
|
-
account — no Apps Script deployment.
|
|
124
|
+
an operation for each write — the root API accepts only `dbName` and
|
|
125
|
+
`entities`. The sync runtime uses one internal Google Sheets API provider with
|
|
126
|
+
a service account — no Apps Script deployment. Sync auto-start is selected by
|
|
127
|
+
`HIKOUTEI_SYNC_SPREADSHEET_URL` plus `GOOGLE_APPLICATION_CREDENTIALS`; there is
|
|
128
|
+
no public `googleSheetsApi` bootstrap option to configure.
|
|
127
129
|
|
|
128
130
|
**Fastest path:** install the gcloud CLI, then run `npx hikoutei setup` from your
|
|
129
131
|
project directory. On an interactive terminal it offers (press Enter) to
|
|
@@ -224,7 +226,7 @@ local-only (SQLite). Startup failures are diagnosed with clear messages:
|
|
|
224
226
|
invalid URL, missing/invalid credentials file, or a service account not
|
|
225
227
|
shared on the spreadsheet (the error tells you which email to share).
|
|
226
228
|
|
|
227
|
-
###
|
|
229
|
+
### Manual service-account setup
|
|
228
230
|
|
|
229
231
|
1. **Create a service account.** Enable the Google Sheets API in a Cloud
|
|
230
232
|
project, create a service account with the
|
|
@@ -239,9 +241,17 @@ shared on the spreadsheet (the error tells you which email to share).
|
|
|
239
241
|
`.gitignore` — the defaults created by `hikoutei setup` are already
|
|
240
242
|
ignored, but a `.gitignore` is not a security boundary and does not
|
|
241
243
|
protect already-tracked files.
|
|
242
|
-
3. **
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
3. **Run the application normally.** Start the app with
|
|
245
|
+
`GOOGLE_APPLICATION_CREDENTIALS` and `HIKOUTEI_SYNC_SPREADSHEET_URL` set;
|
|
246
|
+
`createTypedSheets()` detects them and starts the internal sync bootstrap —
|
|
247
|
+
it creates and verifies headers on the registered tabs, then starts outbox
|
|
248
|
+
delivery and User_Input polling. There is no provider option to pass and no
|
|
249
|
+
internal bootstrap to start by hand.
|
|
250
|
+
|
|
251
|
+
> **Legacy spreadsheet note.** Spreadsheets provisioned by the old Apps Script
|
|
252
|
+
> provider with developer-metadata row anchors are not migrated: `User_Input`
|
|
253
|
+
> tabs now require the `__hikoutei_row_id` system column, so legacy tabs must
|
|
254
|
+
> be re-provisioned.
|
|
245
255
|
|
|
246
256
|
Hikoutei uses a durable local outbox, idempotent delivery, and conflict-aware
|
|
247
257
|
updates so temporary API failures do not lose committed application writes. The
|
|
@@ -293,9 +303,11 @@ Hikoutei is in active development. The current EntityManager supports scalar
|
|
|
293
303
|
entity lifecycle operations, equality-filtered `find()` / `findOne()`,
|
|
294
304
|
`limit` / `offset` pagination on `find()`, and callback-style
|
|
295
305
|
`transactional()` work.
|
|
296
|
-
Normal reads always come from SQLite, never Google Sheets.
|
|
297
|
-
|
|
298
|
-
|
|
306
|
+
Normal reads always come from SQLite, never Google Sheets. The `hikoutei
|
|
307
|
+
setup` CLI provisions the spreadsheet and service account, and the direct
|
|
308
|
+
Google Sheets API provider (env-driven auto-start) is the only sync path — no
|
|
309
|
+
Apps Script gateway. Sheet edit ingestion and conflict presentation are still
|
|
310
|
+
evolving. Review release notes before upgrading minor versions.
|
|
299
311
|
|
|
300
312
|
## Roadmap
|
|
301
313
|
|
|
@@ -329,7 +341,6 @@ The following work continues in parallel with the EntityManager milestones:
|
|
|
329
341
|
|
|
330
342
|
- Complete ingestion of intentional user edits from Google Sheets.
|
|
331
343
|
- Improve update/delete conflict handling and presentation.
|
|
332
|
-
- Improve setup tooling for registry and direct-provider deployment.
|
|
333
344
|
|
|
334
345
|
See the [open issues](https://github.com/ManddarinShop/Hikoutei/issues)
|
|
335
346
|
for current work.
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* include any canonical-state and durable-outbox work associated with a row
|
|
13
13
|
* change. Those sync details remain provider-internal.
|
|
14
14
|
*/
|
|
15
|
+
import type { SqlExecutor } from "./sql.js";
|
|
15
16
|
/** Scalar value bound for one entity column. */
|
|
16
17
|
export type ScalarEntityValue = string | number | boolean | Date | null;
|
|
17
18
|
/** Storage affinity for one scalar entity column. */
|
|
@@ -31,6 +32,7 @@ export interface ScalarEntityTableDefinition {
|
|
|
31
32
|
}
|
|
32
33
|
/** A row to insert into one scalar entity table. */
|
|
33
34
|
export interface ScalarEntityInsert {
|
|
35
|
+
readonly entityName: string;
|
|
34
36
|
readonly tableName: string;
|
|
35
37
|
readonly primaryKeyColumn: string;
|
|
36
38
|
/** Column-to-value map for every declared property of the entity. */
|
|
@@ -38,17 +40,49 @@ export interface ScalarEntityInsert {
|
|
|
38
40
|
}
|
|
39
41
|
/** A changed row to update inside one scalar entity table. */
|
|
40
42
|
export interface ScalarEntityUpdate {
|
|
43
|
+
readonly entityName: string;
|
|
41
44
|
readonly tableName: string;
|
|
42
45
|
readonly primaryKeyColumn: string;
|
|
43
46
|
readonly primaryKeyValue: ScalarEntityValue;
|
|
47
|
+
/** Complete post-update row used by canonical and projection planning. */
|
|
48
|
+
readonly values: Readonly<Record<string, ScalarEntityValue>>;
|
|
44
49
|
/** Only the columns whose values changed since the loaded snapshot. */
|
|
45
50
|
readonly changedValues: Readonly<Record<string, ScalarEntityValue>>;
|
|
46
51
|
}
|
|
47
52
|
/** A row to delete from one scalar entity table by primary key. */
|
|
48
53
|
export interface ScalarEntityDelete {
|
|
54
|
+
readonly entityName: string;
|
|
49
55
|
readonly tableName: string;
|
|
50
56
|
readonly primaryKeyColumn: string;
|
|
51
57
|
readonly primaryKeyValue: ScalarEntityValue;
|
|
58
|
+
/** Complete persisted row snapshot used for delete canonical evidence. */
|
|
59
|
+
readonly values: Readonly<Record<string, ScalarEntityValue>>;
|
|
60
|
+
}
|
|
61
|
+
/** Runtime operation tags used by the provider-neutral flush plan. */
|
|
62
|
+
export declare const SCALAR_ENTITY_CHANGE_KINDS: {
|
|
63
|
+
readonly INSERT: "insert";
|
|
64
|
+
readonly UPDATE: "update";
|
|
65
|
+
readonly DELETE: "delete";
|
|
66
|
+
};
|
|
67
|
+
/** One lifecycle row operation promoted from Hikoutei's Unit of Work. */
|
|
68
|
+
export type ScalarEntityFlushChange = {
|
|
69
|
+
readonly kind: typeof SCALAR_ENTITY_CHANGE_KINDS.INSERT;
|
|
70
|
+
readonly row: ScalarEntityInsert;
|
|
71
|
+
} | {
|
|
72
|
+
readonly kind: typeof SCALAR_ENTITY_CHANGE_KINDS.UPDATE;
|
|
73
|
+
readonly row: ScalarEntityUpdate;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: typeof SCALAR_ENTITY_CHANGE_KINDS.DELETE;
|
|
76
|
+
readonly row: ScalarEntityDelete;
|
|
77
|
+
};
|
|
78
|
+
/** SQL context used by a provider to plan canonical and outbox work. */
|
|
79
|
+
export interface ScalarEntityFlushContext {
|
|
80
|
+
readonly changes: readonly ScalarEntityFlushChange[];
|
|
81
|
+
readonly sql: SqlExecutor;
|
|
82
|
+
}
|
|
83
|
+
/** Callback for sync-aware providers to plan work before entity SQL is flushed. */
|
|
84
|
+
export interface ScalarEntityFlushCoordinator {
|
|
85
|
+
onFlush(context: ScalarEntityFlushContext): Promise<void>;
|
|
52
86
|
}
|
|
53
87
|
/** Equality filter and paging for a scalar entity read. */
|
|
54
88
|
export interface ScalarEntityQuery {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scalar.d.ts","sourceRoot":"","sources":["../../../../src/adapter/persistence/contracts/scalar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,gDAAgD;AAChD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;AAExE,qDAAqD;AACrD,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAElE,kFAAkF;AAClF,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,iFAAiF;AACjF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,4BAA4B,EAAE,CAAC;CAC3D;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAC9D;AAED,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,uEAAuE;IACvE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;CACrE;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"scalar.d.ts","sourceRoot":"","sources":["../../../../src/adapter/persistence/contracts/scalar.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,5 +12,10 @@
|
|
|
12
12
|
* include any canonical-state and durable-outbox work associated with a row
|
|
13
13
|
* change. Those sync details remain provider-internal.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
/** Runtime operation tags used by the provider-neutral flush plan. */
|
|
16
|
+
export const SCALAR_ENTITY_CHANGE_KINDS = {
|
|
17
|
+
INSERT: "insert",
|
|
18
|
+
UPDATE: "update",
|
|
19
|
+
DELETE: "delete",
|
|
20
|
+
};
|
|
16
21
|
//# sourceMappingURL=scalar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scalar.js","sourceRoot":"","sources":["../../../../src/adapter/persistence/contracts/scalar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
1
|
+
{"version":3,"file":"scalar.js","sourceRoot":"","sources":["../../../../src/adapter/persistence/contracts/scalar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwDH,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACR,CAAC"}
|
package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts
CHANGED
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MikroORM implementation of Hikoutei's scalar persistence SPI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* public entrypoint.
|
|
4
|
+
* Hikoutei's Unit of Work is the only lifecycle authority. This adapter merely
|
|
5
|
+
* schedules its provider-neutral row plan on a transaction-bound MikroORM
|
|
6
|
+
* manager, runs the optional mapped canonical/outbox planner, and flushes the
|
|
7
|
+
* entity statements in the same SQLite transaction.
|
|
9
8
|
*/
|
|
10
9
|
import type { ResolvedHikouteiEntityDescriptor } from "../../../../../api/entity.js";
|
|
11
|
-
import type { ScalarEntityPersistenceProvider, ScalarEntityQuery, ScalarEntityRow, ScalarEntityTransaction } from "../../../contracts/scalar.js";
|
|
12
|
-
import type {
|
|
13
|
-
import type {
|
|
10
|
+
import type { ScalarEntityFlushCoordinator, ScalarEntityPersistenceProvider, ScalarEntityQuery, ScalarEntityRow, ScalarEntityTransaction } from "../../../contracts/scalar.js";
|
|
11
|
+
import type { MikroOrmSqliteAdapter } from "../storage/MikroOrmSqliteAdapter.js";
|
|
12
|
+
import type { MappedEntityReference } from "../../../../../application/orm/mapping/contracts.js";
|
|
14
13
|
/** Internal mapping from a public descriptor to its generated MikroORM entity. */
|
|
15
14
|
export interface MikroOrmScalarEntityBinding {
|
|
16
15
|
readonly descriptor: ResolvedHikouteiEntityDescriptor;
|
|
17
|
-
|
|
16
|
+
/** Kept opaque so MikroORM types remain inside this adapter module. */
|
|
17
|
+
readonly entity: MappedEntityReference<Record<string, unknown>>;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Adapts
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* constructed. Every transaction delegates through `TypedSheetsOrm.em`, so
|
|
24
|
-
* its existing canonical/outbox flush coordinator remains in the same SQLite
|
|
25
|
-
* transaction as the generated entity rows.
|
|
20
|
+
* Adapts generated MikroORM entity bindings to the provider-neutral scalar
|
|
21
|
+
* contract. A mapped coordinator, when supplied, runs before entity SQL and
|
|
22
|
+
* shares the active manager's transaction-bound SQL executor.
|
|
26
23
|
*/
|
|
27
24
|
export declare class MikroOrmScalarPersistenceProvider implements ScalarEntityPersistenceProvider {
|
|
28
|
-
private readonly
|
|
25
|
+
private readonly storage;
|
|
26
|
+
private readonly flushCoordinator?;
|
|
29
27
|
private readonly bindings;
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
private readonly bindingsByTable;
|
|
29
|
+
constructor(storage: MikroOrmSqliteAdapter, bindings: readonly MikroOrmScalarEntityBinding[], flushCoordinator?: ScalarEntityFlushCoordinator | undefined);
|
|
30
|
+
/** Reads through a clean manager without flushing another Unit of Work. */
|
|
32
31
|
read(query: ScalarEntityQuery): Promise<readonly ScalarEntityRow[]>;
|
|
33
|
-
/** Runs
|
|
32
|
+
/** Runs the common UoW plan and mapped work in one SQLite transaction. */
|
|
34
33
|
beginTransaction<Result>(work: (transaction: ScalarEntityTransaction) => Promise<Result>): Promise<Result>;
|
|
35
|
-
/** Closes the
|
|
34
|
+
/** Closes the one SQLite connection owned by this provider. */
|
|
36
35
|
close(): Promise<void>;
|
|
37
36
|
private requireBindingByTable;
|
|
38
37
|
}
|
package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MikroOrmScalarPersistenceProvider.d.ts","sourceRoot":"","sources":["../../../../../../src/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MikroOrmScalarPersistenceProvider.d.ts","sourceRoot":"","sources":["../../../../../../src/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAErF,OAAO,KAAK,EAGV,4BAA4B,EAE5B,+BAA+B,EAC/B,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EAGxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAEjG,kFAAkF;AAClF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,UAAU,EAAE,gCAAgC,CAAC;IACtD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjE;AAED;;;;GAIG;AACH,qBAAa,iCAAkC,YAAW,+BAA+B;IAKrF,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IANpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmD;IAC5E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmD;gBAGhE,OAAO,EAAE,qBAAqB,EAC/C,QAAQ,EAAE,SAAS,2BAA2B,EAAE,EAC/B,gBAAgB,CAAC,EAAE,4BAA4B,YAAA;IAMlE,2EAA2E;IACrE,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAKzE,0EAA0E;IACpE,gBAAgB,CAAC,MAAM,EAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,uBAAuB,KAAK,OAAO,CAAC,MAAM,CAAC,GAC9D,OAAO,CAAC,MAAM,CAAC;IAalB,+DAA+D;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,qBAAqB;CAK9B"}
|
package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js
CHANGED
|
@@ -1,116 +1,169 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MikroORM implementation of Hikoutei's scalar persistence SPI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* public entrypoint.
|
|
4
|
+
* Hikoutei's Unit of Work is the only lifecycle authority. This adapter merely
|
|
5
|
+
* schedules its provider-neutral row plan on a transaction-bound MikroORM
|
|
6
|
+
* manager, runs the optional mapped canonical/outbox planner, and flushes the
|
|
7
|
+
* entity statements in the same SQLite transaction.
|
|
9
8
|
*/
|
|
10
9
|
import { HIKOUTEI_ERROR_CODES, HikouteiError } from "../../../../../api/errors.js";
|
|
11
10
|
import { isCanonicalUtcIsoDate } from "../../../../../shared/validation.js";
|
|
12
11
|
import { isRecord } from "../../../../../shared/encoding/typeGuards.js";
|
|
13
12
|
/**
|
|
14
|
-
* Adapts
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* constructed. Every transaction delegates through `TypedSheetsOrm.em`, so
|
|
18
|
-
* its existing canonical/outbox flush coordinator remains in the same SQLite
|
|
19
|
-
* transaction as the generated entity rows.
|
|
13
|
+
* Adapts generated MikroORM entity bindings to the provider-neutral scalar
|
|
14
|
+
* contract. A mapped coordinator, when supplied, runs before entity SQL and
|
|
15
|
+
* shares the active manager's transaction-bound SQL executor.
|
|
20
16
|
*/
|
|
21
17
|
export class MikroOrmScalarPersistenceProvider {
|
|
22
|
-
|
|
18
|
+
storage;
|
|
19
|
+
flushCoordinator;
|
|
23
20
|
bindings;
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
bindingsByTable;
|
|
22
|
+
constructor(storage, bindings, flushCoordinator) {
|
|
23
|
+
this.storage = storage;
|
|
24
|
+
this.flushCoordinator = flushCoordinator;
|
|
26
25
|
this.bindings = new Map(bindings.map((binding) => [binding.descriptor.name, binding]));
|
|
26
|
+
this.bindingsByTable = new Map(bindings.map((binding) => [binding.descriptor.tableName, binding]));
|
|
27
27
|
}
|
|
28
|
-
/** Reads through a
|
|
28
|
+
/** Reads through a clean manager without flushing another Unit of Work. */
|
|
29
29
|
async read(query) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
return readRows(manager, binding, query);
|
|
30
|
+
const manager = this.storage.forkEntityManager();
|
|
31
|
+
return readRows(manager, this.requireBindingByTable(query.tableName), query);
|
|
33
32
|
}
|
|
34
|
-
/** Runs
|
|
33
|
+
/** Runs the common UoW plan and mapped work in one SQLite transaction. */
|
|
35
34
|
async beginTransaction(work) {
|
|
36
|
-
return this.
|
|
37
|
-
const transaction = new MikroOrmScalarTransaction(
|
|
35
|
+
return this.storage.transactional(async ({ entityManager }) => {
|
|
36
|
+
const transaction = new MikroOrmScalarTransaction(this.storage, entityManager, this.bindings, this.bindingsByTable, this.flushCoordinator);
|
|
38
37
|
return work(transaction);
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
|
-
/** Closes the
|
|
40
|
+
/** Closes the one SQLite connection owned by this provider. */
|
|
42
41
|
async close() {
|
|
43
|
-
await this.
|
|
42
|
+
await this.storage.close(true);
|
|
44
43
|
}
|
|
45
44
|
requireBindingByTable(tableName) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
throw new HikouteiError(HIKOUTEI_ERROR_CODES.UNREGISTERED_ENTITY, `entity table "${tableName}" is not registered with this runtime.`);
|
|
45
|
+
const binding = this.bindingsByTable.get(tableName);
|
|
46
|
+
if (binding !== undefined)
|
|
47
|
+
return binding;
|
|
48
|
+
throw unregisteredTable(tableName);
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
|
-
/** Transaction-bound
|
|
54
|
-
class
|
|
55
|
-
|
|
51
|
+
/** Transaction-bound reader shared by provider reads and flush-time lookups. */
|
|
52
|
+
class MikroOrmScalarReader {
|
|
53
|
+
entityManager;
|
|
56
54
|
bindings;
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
bindingsByTable;
|
|
56
|
+
constructor(entityManager, bindings, bindingsByTable) {
|
|
57
|
+
this.entityManager = entityManager;
|
|
59
58
|
this.bindings = bindings;
|
|
59
|
+
this.bindingsByTable = bindingsByTable;
|
|
60
|
+
}
|
|
61
|
+
async read(query) {
|
|
62
|
+
return readRows(this.entityManager, this.requireBindingByTable(query.tableName), query);
|
|
63
|
+
}
|
|
64
|
+
requireBindingByTable(tableName) {
|
|
65
|
+
const binding = this.bindingsByTable.get(tableName);
|
|
66
|
+
if (binding !== undefined)
|
|
67
|
+
return binding;
|
|
68
|
+
throw unregisteredTable(tableName);
|
|
69
|
+
}
|
|
70
|
+
requireBindingForRow(entityName, tableName) {
|
|
71
|
+
const binding = this.bindings.get(entityName);
|
|
72
|
+
if (binding !== undefined && binding.descriptor.tableName === tableName)
|
|
73
|
+
return binding;
|
|
74
|
+
throw new HikouteiError(HIKOUTEI_ERROR_CODES.UNREGISTERED_ENTITY, `entity "${entityName}" is not registered for table "${tableName}".`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Schedules a Hikoutei flush plan on the active MikroORM manager.
|
|
79
|
+
*
|
|
80
|
+
* The coordinator runs before MikroORM emits entity SQL. Both its SQL and the
|
|
81
|
+
* later entity statements are owned by the surrounding `transactional()` call,
|
|
82
|
+
* so either side can reject without leaving a partial canonical/outbox write.
|
|
83
|
+
*/
|
|
84
|
+
class MikroOrmScalarTransaction extends MikroOrmScalarReader {
|
|
85
|
+
storage;
|
|
86
|
+
flushCoordinator;
|
|
87
|
+
changes = [];
|
|
88
|
+
constructor(storage, entityManager, bindings, bindingsByTable, flushCoordinator) {
|
|
89
|
+
super(entityManager, bindings, bindingsByTable);
|
|
90
|
+
this.storage = storage;
|
|
91
|
+
this.flushCoordinator = flushCoordinator;
|
|
60
92
|
}
|
|
61
93
|
async insert(row) {
|
|
62
|
-
const binding = this.
|
|
63
|
-
const entity = this.
|
|
64
|
-
this.
|
|
94
|
+
const binding = this.requireBindingForRow(row.entityName, row.tableName);
|
|
95
|
+
const entity = createManagedEntity(this.entityManager, binding, row.values);
|
|
96
|
+
this.entityManager.persist(entity);
|
|
97
|
+
this.changes.push({ kind: "insert", row });
|
|
65
98
|
}
|
|
66
99
|
async update(row) {
|
|
67
|
-
const binding = this.
|
|
100
|
+
const binding = this.requireBindingForRow(row.entityName, row.tableName);
|
|
68
101
|
const existing = await this.findOne(binding, row.primaryKeyColumn, row.primaryKeyValue);
|
|
69
102
|
if (existing === null) {
|
|
70
103
|
throw new HikouteiError(HIKOUTEI_ERROR_CODES.ENTITY_NOT_FOUND, `cannot update missing entity in table "${row.tableName}".`);
|
|
71
104
|
}
|
|
72
105
|
Object.assign(existing, toInternalData(binding.descriptor, row.changedValues));
|
|
73
|
-
this.
|
|
106
|
+
this.entityManager.persist(existing);
|
|
107
|
+
this.changes.push({ kind: "update", row });
|
|
74
108
|
}
|
|
75
109
|
async delete(row) {
|
|
76
|
-
const binding = this.
|
|
110
|
+
const binding = this.requireBindingForRow(row.entityName, row.tableName);
|
|
77
111
|
const existing = await this.findOne(binding, row.primaryKeyColumn, row.primaryKeyValue);
|
|
78
112
|
if (existing === null) {
|
|
79
113
|
throw new HikouteiError(HIKOUTEI_ERROR_CODES.ENTITY_NOT_FOUND, `cannot delete missing entity in table "${row.tableName}".`);
|
|
80
114
|
}
|
|
81
|
-
this.
|
|
115
|
+
this.entityManager.remove(existing);
|
|
116
|
+
this.changes.push({ kind: "delete", row });
|
|
82
117
|
}
|
|
83
|
-
|
|
84
|
-
const binding = this.requireBindingByTable(query.tableName);
|
|
85
|
-
return readRows(this.manager, binding, query);
|
|
86
|
-
}
|
|
87
|
-
/** Forces MikroORM's mapped flush before the common UoW advances snapshots. */
|
|
118
|
+
/** Plans mapped state first, then flushes the scheduled entity statements. */
|
|
88
119
|
async flush() {
|
|
89
|
-
|
|
120
|
+
if (this.changes.length > 0 && this.flushCoordinator !== undefined) {
|
|
121
|
+
await this.flushCoordinator.onFlush({
|
|
122
|
+
changes: [...this.changes],
|
|
123
|
+
sql: this.storage.createSqlExecutor(this.entityManager),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
await this.entityManager.flush();
|
|
127
|
+
this.changes.length = 0;
|
|
90
128
|
}
|
|
91
129
|
async findOne(binding, primaryKeyColumn, primaryKeyValue) {
|
|
92
|
-
const result = await this.
|
|
130
|
+
const result = await Reflect.apply(this.entityManager.findOne, this.entityManager, [
|
|
131
|
+
binding.entity,
|
|
132
|
+
{ [primaryKeyColumn]: toInternalValue(binding.descriptor, primaryKeyColumn, primaryKeyValue) },
|
|
133
|
+
]);
|
|
134
|
+
if (result === null)
|
|
135
|
+
return null;
|
|
136
|
+
if (!isRecord(result)) {
|
|
137
|
+
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, "MikroORM findOne result must be an entity object or null.");
|
|
138
|
+
}
|
|
93
139
|
return result;
|
|
94
140
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw new HikouteiError(HIKOUTEI_ERROR_CODES.UNREGISTERED_ENTITY, `entity table "${tableName}" is not registered with this runtime.`);
|
|
141
|
+
}
|
|
142
|
+
function createManagedEntity(entityManager, binding, values) {
|
|
143
|
+
const result = Reflect.apply(entityManager.create, entityManager, [binding.entity, toInternalData(binding.descriptor, values), { partial: true, persist: false }]);
|
|
144
|
+
if (!isRecord(result)) {
|
|
145
|
+
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, "MikroORM create result must be an entity object.");
|
|
101
146
|
}
|
|
147
|
+
return result;
|
|
102
148
|
}
|
|
103
|
-
async function readRows(
|
|
104
|
-
const
|
|
105
|
-
|
|
149
|
+
async function readRows(entityManager, binding, query) {
|
|
150
|
+
const result = await Reflect.apply(entityManager.find, entityManager, [
|
|
151
|
+
binding.entity,
|
|
152
|
+
toMikroOrmFilter(binding.descriptor, query.where),
|
|
153
|
+
toMikroOrmQueryOptions(query),
|
|
154
|
+
]);
|
|
155
|
+
if (!Array.isArray(result) || !result.every(isRecord)) {
|
|
156
|
+
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, "MikroORM find result must be an array of entity objects.");
|
|
157
|
+
}
|
|
158
|
+
return result.map((entity) => fromInternalEntity(binding.descriptor, entity));
|
|
106
159
|
}
|
|
107
|
-
function
|
|
160
|
+
function toMikroOrmQueryOptions(query) {
|
|
108
161
|
return {
|
|
109
162
|
...(query.limit === undefined ? {} : { limit: query.limit }),
|
|
110
163
|
...(query.offset === undefined ? {} : { offset: query.offset }),
|
|
111
164
|
};
|
|
112
165
|
}
|
|
113
|
-
function
|
|
166
|
+
function toMikroOrmFilter(descriptor, where) {
|
|
114
167
|
return Object.fromEntries(Object.entries(where).map(([property, value]) => [
|
|
115
168
|
property,
|
|
116
169
|
toInternalValue(descriptor, property, value),
|
|
@@ -129,18 +182,12 @@ function toInternalValue(descriptor, propertyName, value) {
|
|
|
129
182
|
return value;
|
|
130
183
|
}
|
|
131
184
|
function fromInternalEntity(descriptor, entity) {
|
|
132
|
-
if (!isRecord(entity)) {
|
|
133
|
-
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, "stored entity must be an object");
|
|
134
|
-
}
|
|
135
185
|
const values = {};
|
|
136
186
|
for (const property of descriptor.properties) {
|
|
137
|
-
const value = entity
|
|
187
|
+
const value = Reflect.get(entity, property.name);
|
|
138
188
|
if (value === null || value === undefined) {
|
|
139
|
-
if (!property.nullable
|
|
140
|
-
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, `${property.name} is missing
|
|
141
|
-
}
|
|
142
|
-
if (!property.nullable && value === null) {
|
|
143
|
-
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, `${property.name} is unexpectedly null in the stored entity.`);
|
|
189
|
+
if (!property.nullable) {
|
|
190
|
+
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, `${property.name} is unexpectedly ${value === null ? "null" : "missing"} in the stored entity.`);
|
|
144
191
|
}
|
|
145
192
|
values[property.name] = null;
|
|
146
193
|
continue;
|
|
@@ -176,4 +223,7 @@ function fromInternalEntity(descriptor, entity) {
|
|
|
176
223
|
function throwInvalidStoredScalar(propertyName, expected) {
|
|
177
224
|
throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, `${propertyName} must be a ${expected} in the stored entity.`);
|
|
178
225
|
}
|
|
226
|
+
function unregisteredTable(tableName) {
|
|
227
|
+
return new HikouteiError(HIKOUTEI_ERROR_CODES.UNREGISTERED_ENTITY, `entity table "${tableName}" is not registered with this runtime.`);
|
|
228
|
+
}
|
|
179
229
|
//# sourceMappingURL=MikroOrmScalarPersistenceProvider.js.map
|