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.
Files changed (95) hide show
  1. package/README.ja.md +23 -10
  2. package/README.ko.md +19 -8
  3. package/README.md +22 -11
  4. package/dist/adapter/persistence/contracts/scalar.d.ts +34 -0
  5. package/dist/adapter/persistence/contracts/scalar.d.ts.map +1 -1
  6. package/dist/adapter/persistence/contracts/scalar.js +6 -1
  7. package/dist/adapter/persistence/contracts/scalar.js.map +1 -1
  8. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts +19 -20
  9. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.d.ts.map +1 -1
  10. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js +117 -67
  11. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js.map +1 -1
  12. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.d.ts +31 -0
  13. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.d.ts.map +1 -0
  14. package/dist/adapter/persistence/providers/mikro-orm/engine/{MikroOrmMappedTypedSheets.js → MikroOrmMappedRuntime.js} +10 -31
  15. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedRuntime.js.map +1 -0
  16. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarEntityRuntime.js.map +1 -1
  17. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarRuntime.js.map +1 -1
  18. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.d.ts +12 -0
  19. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.d.ts.map +1 -0
  20. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.js +20 -0
  21. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarStorage.js.map +1 -0
  22. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts +2 -7
  23. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts.map +1 -1
  24. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js +5 -11
  25. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js.map +1 -1
  26. package/dist/api/Hikoutei.d.ts.map +1 -1
  27. package/dist/api/Hikoutei.js +3 -4
  28. package/dist/api/Hikoutei.js.map +1 -1
  29. package/dist/api/errors.d.ts +2 -0
  30. package/dist/api/errors.d.ts.map +1 -1
  31. package/dist/api/errors.js +2 -0
  32. package/dist/api/errors.js.map +1 -1
  33. package/dist/api/internalEntityManager.d.ts.map +1 -1
  34. package/dist/api/internalEntityManager.js +31 -9
  35. package/dist/api/internalEntityManager.js.map +1 -1
  36. package/dist/application/orm/mapping/contracts.d.ts +8 -3
  37. package/dist/application/orm/mapping/contracts.d.ts.map +1 -1
  38. package/dist/application/orm/mapping/values.d.ts +2 -0
  39. package/dist/application/orm/mapping/values.d.ts.map +1 -1
  40. package/dist/application/orm/mapping/values.js +10 -3
  41. package/dist/application/orm/mapping/values.js.map +1 -1
  42. package/dist/application/orm/persistence/flush/flushCoordinator.d.ts +2 -2
  43. package/dist/application/orm/persistence/flush/flushCoordinator.d.ts.map +1 -1
  44. package/dist/application/orm/persistence/flush/flushCoordinator.js +9 -16
  45. package/dist/application/orm/persistence/flush/flushCoordinator.js.map +1 -1
  46. package/dist/application/orm/persistence/lifecycle/entityLifecycle.d.ts.map +1 -1
  47. package/dist/application/orm/persistence/lifecycle/entityLifecycle.js +26 -13
  48. package/dist/application/orm/persistence/lifecycle/entityLifecycle.js.map +1 -1
  49. package/dist/application/orm/persistence/projection/projectionEffects.d.ts +2 -2
  50. package/dist/application/orm/persistence/projection/projectionEffects.d.ts.map +1 -1
  51. package/dist/application/orm/persistence/projection/projectionEffects.js +6 -6
  52. package/dist/application/orm/persistence/projection/projectionEffects.js.map +1 -1
  53. package/dist/application/orm/persistence/scalar/unitOfWork.d.ts +21 -2
  54. package/dist/application/orm/persistence/scalar/unitOfWork.d.ts.map +1 -1
  55. package/dist/application/orm/persistence/scalar/unitOfWork.js +107 -8
  56. package/dist/application/orm/persistence/scalar/unitOfWork.js.map +1 -1
  57. package/dist/application/orm/persistence/support/contracts.d.ts +6 -6
  58. package/dist/application/orm/persistence/support/contracts.d.ts.map +1 -1
  59. package/dist/application/orm/persistence/support/contracts.js +0 -2
  60. package/dist/application/orm/persistence/support/contracts.js.map +1 -1
  61. package/dist/application/orm/persistence/support/helpers.d.ts +3 -3
  62. package/dist/application/orm/persistence/support/helpers.d.ts.map +1 -1
  63. package/dist/application/orm/persistence/support/helpers.js +12 -9
  64. package/dist/application/orm/persistence/support/helpers.js.map +1 -1
  65. package/dist/application/orm/persistence/support/timing.d.ts +2 -2
  66. package/dist/application/orm/persistence/support/timing.d.ts.map +1 -1
  67. package/dist/application/orm/persistence/support/timing.js +4 -4
  68. package/dist/application/orm/persistence/support/timing.js.map +1 -1
  69. package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
  70. package/dist/application/sync/service/SyncServiceBootstrap.js +3 -3
  71. package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
  72. package/package.json +1 -1
  73. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts +0 -55
  74. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts.map +0 -1
  75. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js +0 -120
  76. package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js.map +0 -1
  77. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts +0 -56
  78. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts.map +0 -1
  79. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.js.map +0 -1
  80. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts +0 -38
  81. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts.map +0 -1
  82. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js +0 -57
  83. package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js.map +0 -1
  84. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts +0 -19
  85. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts.map +0 -1
  86. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js +0 -67
  87. package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js.map +0 -1
  88. package/dist/application/orm/api/TypedSheetsOrm.d.ts +0 -71
  89. package/dist/application/orm/api/TypedSheetsOrm.d.ts.map +0 -1
  90. package/dist/application/orm/api/TypedSheetsOrm.js +0 -116
  91. package/dist/application/orm/api/TypedSheetsOrm.js.map +0 -1
  92. package/dist/application/orm/api/contracts.d.ts +0 -118
  93. package/dist/application/orm/api/contracts.d.ts.map +0 -1
  94. package/dist/application/orm/api/contracts.js +0 -21
  95. 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
- 使う単一の Google Sheets API provider(内部 `googleSheetsApi` bootstrap オプ
126
- ション)を使用します — Apps Script のデプロイは不要です。
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
- ### Service-account provider (googleSheetsApi)
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. **内部 sync bootstrap を起動する。** `googleSheetsApi` を設定すると、
161
- 登録済みタブのヘッダーを作成・検証した後、outbox 配信と User_Input ポー
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
- 하나의 Google Sheets API provider(내부 `googleSheetsApi` bootstrap 옵션)를
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
- ### Service-account provider (googleSheetsApi)
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. **내부 sync bootstrap을 기동합니다.** `googleSheetsApi`를 설정하면
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. The sync runtime uses one Google Sheets API
125
- provider (the internal `googleSheetsApi` bootstrap option) with a service
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
- ### Service-account provider (googleSheetsApi)
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. **Start the internal sync bootstrap** with `googleSheetsApi` configured. It
243
- creates and verifies headers on the registered tabs, then starts outbox
244
- delivery and User_Input polling.
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. Sheet edit ingestion
297
- and conflict presentation are still evolving. Review release notes before
298
- upgrading minor versions.
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":"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"}
1
+ {"version":3,"file":"scalar.d.ts","sourceRoot":"","sources":["../../../../src/adapter/persistence/contracts/scalar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,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,UAAU,EAAE,MAAM,CAAC;IAC5B,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,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,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,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAC9D;AAED,sEAAsE;AACtE,eAAO,MAAM,0BAA0B;;;;CAI7B,CAAC;AAEX,yEAAyE;AACzE,MAAM,MAAM,uBAAuB,GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,0BAA0B,CAAC,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAA;CAAE,GAC7F;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,0BAA0B,CAAC,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAA;CAAE,GAC7F;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,0BAA0B,CAAC,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAElG,wEAAwE;AACxE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACrD,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;CAC3B;AAED,mFAAmF;AACnF,MAAM,WAAW,4BAA4B;IAC3C,OAAO,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,qEAAqE;IACrE,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC;IACpE;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,gDAAgD;AAChD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAE1E;;;;;;;GAOG;AACH,MAAM,WAAW,+BAA+B;IAC9C,2EAA2E;IAC3E,gBAAgB,CAAC,MAAM,EACrB,IAAI,EAAE,CAAC,WAAW,EAAE,uBAAuB,KAAK,OAAO,CAAC,MAAM,CAAC,GAC9D,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,4EAA4E;IAC5E,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC;IACpE,oEAAoE;IACpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
@@ -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
- export {};
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"}
@@ -1,38 +1,37 @@
1
1
  /**
2
2
  * MikroORM implementation of Hikoutei's scalar persistence SPI.
3
3
  *
4
- * The public Unit of Work prepares provider-neutral row changes. This adapter
5
- * translates those changes into the existing mapped MikroORM facade, whose
6
- * flush coordinator still commits canonical state and the durable Sheet
7
- * outbox in the same SQLite transaction. No MikroORM type crosses the root
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 { TypedSheetsEntityReference } from "../../../../../application/orm/api/contracts.js";
13
- import type { TypedSheetsOrm } from "../../../../../application/orm/api/TypedSheetsOrm.js";
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
- readonly entity: TypedSheetsEntityReference<Record<string, unknown>>;
16
+ /** Kept opaque so MikroORM types remain inside this adapter module. */
17
+ readonly entity: MappedEntityReference<Record<string, unknown>>;
18
18
  }
19
19
  /**
20
- * Adapts one mapped MikroORM runtime to the provider-neutral scalar contract.
21
- *
22
- * Entity tables are created by the generated schemas before this adapter is
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 orm;
25
+ private readonly storage;
26
+ private readonly flushCoordinator?;
29
27
  private readonly bindings;
30
- constructor(orm: TypedSheetsOrm, bindings: readonly MikroOrmScalarEntityBinding[]);
31
- /** Reads through a fresh internal manager so public identity maps stay local. */
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 one common-UoW transaction through the mapped MikroORM facade. */
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 internal mapped runtime and its SQLite connection. */
34
+ /** Closes the one SQLite connection owned by this provider. */
36
35
  close(): Promise<void>;
37
36
  private requireBindingByTable;
38
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MikroOrmScalarPersistenceProvider.d.ts","sourceRoot":"","sources":["../../../../../../src/adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,gCAAgC,EACjC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAGV,+BAA+B,EAC/B,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EAGxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACV,0BAA0B,EAE3B,MAAM,iDAAiD,CAAC;AACzD,OAAO,KAAK,EAEV,cAAc,EACf,MAAM,sDAAsD,CAAC;AAI9D,kFAAkF;AAClF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,UAAU,EAAE,gCAAgC,CAAC;IACtD,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtE;AAED;;;;;;;GAOG;AACH,qBAAa,iCAAkC,YAAW,+BAA+B;IAIrF,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmD;gBAGzD,GAAG,EAAE,cAAc,EACpC,QAAQ,EAAE,SAAS,2BAA2B,EAAE;IAKlD,iFAAiF;IAC3E,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAMzE,0EAA0E;IACpE,gBAAgB,CAAC,MAAM,EAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,uBAAuB,KAAK,OAAO,CAAC,MAAM,CAAC,GAC9D,OAAO,CAAC,MAAM,CAAC;IAOlB,oEAAoE;IAC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,qBAAqB;CAS9B"}
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"}
@@ -1,116 +1,169 @@
1
1
  /**
2
2
  * MikroORM implementation of Hikoutei's scalar persistence SPI.
3
3
  *
4
- * The public Unit of Work prepares provider-neutral row changes. This adapter
5
- * translates those changes into the existing mapped MikroORM facade, whose
6
- * flush coordinator still commits canonical state and the durable Sheet
7
- * outbox in the same SQLite transaction. No MikroORM type crosses the root
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 one mapped MikroORM runtime to the provider-neutral scalar contract.
15
- *
16
- * Entity tables are created by the generated schemas before this adapter is
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
- orm;
18
+ storage;
19
+ flushCoordinator;
23
20
  bindings;
24
- constructor(orm, bindings) {
25
- this.orm = orm;
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 fresh internal manager so public identity maps stay local. */
28
+ /** Reads through a clean manager without flushing another Unit of Work. */
29
29
  async read(query) {
30
- const binding = this.requireBindingByTable(query.tableName);
31
- const manager = this.orm.em.fork();
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 one common-UoW transaction through the mapped MikroORM facade. */
33
+ /** Runs the common UoW plan and mapped work in one SQLite transaction. */
35
34
  async beginTransaction(work) {
36
- return this.orm.em.transactional(async (manager) => {
37
- const transaction = new MikroOrmScalarTransaction(manager, this.bindings);
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 internal mapped runtime and its SQLite connection. */
40
+ /** Closes the one SQLite connection owned by this provider. */
42
41
  async close() {
43
- await this.orm.close(true);
42
+ await this.storage.close(true);
44
43
  }
45
44
  requireBindingByTable(tableName) {
46
- for (const binding of this.bindings.values()) {
47
- if (binding.descriptor.tableName === tableName)
48
- return binding;
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 translation from scalar plans to mapped entity writes. */
54
- class MikroOrmScalarTransaction {
55
- manager;
51
+ /** Transaction-bound reader shared by provider reads and flush-time lookups. */
52
+ class MikroOrmScalarReader {
53
+ entityManager;
56
54
  bindings;
57
- constructor(manager, bindings) {
58
- this.manager = manager;
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.requireBindingByTable(row.tableName);
63
- const entity = this.manager.create(binding.entity, toInternalData(binding.descriptor, row.values));
64
- this.manager.persist(entity);
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.requireBindingByTable(row.tableName);
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.manager.persist(existing);
106
+ this.entityManager.persist(existing);
107
+ this.changes.push({ kind: "update", row });
74
108
  }
75
109
  async delete(row) {
76
- const binding = this.requireBindingByTable(row.tableName);
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.manager.remove(existing);
115
+ this.entityManager.remove(existing);
116
+ this.changes.push({ kind: "delete", row });
82
117
  }
83
- async read(query) {
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
- await this.manager.flush();
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.manager.findOne(binding.entity, { [primaryKeyColumn]: toInternalValue(binding.descriptor, primaryKeyColumn, primaryKeyValue) });
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
- requireBindingByTable(tableName) {
96
- for (const binding of this.bindings.values()) {
97
- if (binding.descriptor.tableName === tableName)
98
- return binding;
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(manager, binding, query) {
104
- const entities = await manager.find(binding.entity, toInternalFilter(binding.descriptor, query.where), toFindOptions(query));
105
- return entities.map((entity) => fromInternalEntity(binding.descriptor, entity));
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 toFindOptions(query) {
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 toInternalFilter(descriptor, where) {
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[property.name];
187
+ const value = Reflect.get(entity, property.name);
138
188
  if (value === null || value === undefined) {
139
- if (!property.nullable && value === undefined) {
140
- throw new HikouteiError(HIKOUTEI_ERROR_CODES.INVALID_SCALAR_VALUE, `${property.name} is missing from the stored entity.`);
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