hikoutei 0.4.2 → 0.4.3

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 (140) hide show
  1. package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts +64 -0
  2. package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts.map +1 -0
  3. package/dist/application/sync/outbound/SheetsEffectDispatcher.js +440 -0
  4. package/dist/application/sync/outbound/SheetsEffectDispatcher.js.map +1 -0
  5. package/dist/application/sync/service/SyncServiceBootstrap.d.ts +3 -4
  6. package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
  7. package/dist/application/sync/service/SyncServiceBootstrap.js +7 -4
  8. package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
  9. package/dist/application/sync/telemetry/syncTiming.d.ts +20 -33
  10. package/dist/application/sync/telemetry/syncTiming.d.ts.map +1 -1
  11. package/dist/application/sync/telemetry/syncTiming.js +14 -12
  12. package/dist/application/sync/telemetry/syncTiming.js.map +1 -1
  13. package/dist/infrastructure/storage/errors.d.ts +10 -5
  14. package/dist/infrastructure/storage/errors.d.ts.map +1 -1
  15. package/dist/infrastructure/storage/errors.js +10 -7
  16. package/dist/infrastructure/storage/errors.js.map +1 -1
  17. package/dist/infrastructure/storage/index.d.ts +6 -4
  18. package/dist/infrastructure/storage/index.d.ts.map +1 -1
  19. package/dist/infrastructure/storage/index.js +3 -2
  20. package/dist/infrastructure/storage/index.js.map +1 -1
  21. package/dist/infrastructure/storage/sqlite/schema.d.ts +12 -11
  22. package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -1
  23. package/dist/infrastructure/storage/sqlite/schema.js +17 -109
  24. package/dist/infrastructure/storage/sqlite/schema.js.map +1 -1
  25. package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts +2 -2
  26. package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts.map +1 -1
  27. package/dist/infrastructure/storage/state/canonical/canonicalCommit.js +2 -2
  28. package/dist/infrastructure/storage/state/canonical/canonicalCommit.js.map +1 -1
  29. package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts +1 -2
  30. package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts.map +1 -1
  31. package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js +1 -2
  32. package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js.map +1 -1
  33. package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts +1 -1
  34. package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts.map +1 -1
  35. package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts +1 -1
  36. package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts.map +1 -1
  37. package/dist/infrastructure/storage/state/observation/observationQuarantine.js +1 -1
  38. package/dist/infrastructure/storage/state/observation/observationQuarantine.js.map +1 -1
  39. package/dist/infrastructure/storage/state/observation/observationTypes.d.ts +1 -1
  40. package/dist/infrastructure/storage/state/observation/observationTypes.d.ts.map +1 -1
  41. package/dist/infrastructure/storage/state/observation/observationValidation.d.ts +1 -1
  42. package/dist/infrastructure/storage/state/observation/observationValidation.d.ts.map +1 -1
  43. package/dist/infrastructure/storage/state/observation/observationWriter.d.ts +1 -1
  44. package/dist/infrastructure/storage/state/observation/observationWriter.d.ts.map +1 -1
  45. package/dist/infrastructure/storage/state/observation/observationWriter.js +1 -2
  46. package/dist/infrastructure/storage/state/observation/observationWriter.js.map +1 -1
  47. package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts +1 -2
  48. package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts.map +1 -1
  49. package/dist/infrastructure/storage/state/resolution/resolutionWriter.js +1 -2
  50. package/dist/infrastructure/storage/state/resolution/resolutionWriter.js.map +1 -1
  51. package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts +1 -1
  52. package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts.map +1 -1
  53. package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts +1 -1
  54. package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts.map +1 -1
  55. package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js +1 -1
  56. package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js.map +1 -1
  57. package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts +1 -1
  58. package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts.map +1 -1
  59. package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js +2 -2
  60. package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js.map +1 -1
  61. package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts +1 -1
  62. package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts.map +1 -1
  63. package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js +1 -1
  64. package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js.map +1 -1
  65. package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts +1 -1
  66. package/dist/infrastructure/storage/sync/shared/syncRegistry.js +1 -1
  67. package/package.json +13 -7
  68. package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts +0 -66
  69. package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts.map +0 -1
  70. package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js +0 -123
  71. package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js.map +0 -1
  72. package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts +0 -111
  73. package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts.map +0 -1
  74. package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js +0 -369
  75. package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js.map +0 -1
  76. package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts +0 -127
  77. package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts.map +0 -1
  78. package/dist/application/sync/outbound/effects/SyncEffectWorker.js +0 -552
  79. package/dist/application/sync/outbound/effects/SyncEffectWorker.js.map +0 -1
  80. package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts +0 -85
  81. package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts.map +0 -1
  82. package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js +0 -74
  83. package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js.map +0 -1
  84. package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts +0 -31
  85. package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts.map +0 -1
  86. package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js +0 -222
  87. package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js.map +0 -1
  88. package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts +0 -14
  89. package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts.map +0 -1
  90. package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js +0 -25
  91. package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js.map +0 -1
  92. package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts +0 -61
  93. package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts.map +0 -1
  94. package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js +0 -296
  95. package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js.map +0 -1
  96. package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts +0 -17
  97. package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts.map +0 -1
  98. package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js +0 -80
  99. package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js.map +0 -1
  100. package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts +0 -13
  101. package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts.map +0 -1
  102. package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js +0 -248
  103. package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js.map +0 -1
  104. package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts +0 -141
  105. package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts.map +0 -1
  106. package/dist/infrastructure/storage/sync/outbound/effectOutbox.js +0 -318
  107. package/dist/infrastructure/storage/sync/outbound/effectOutbox.js.map +0 -1
  108. package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts +0 -143
  109. package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts.map +0 -1
  110. package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js +0 -16
  111. package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js.map +0 -1
  112. package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts +0 -33
  113. package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts.map +0 -1
  114. package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js +0 -259
  115. package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js.map +0 -1
  116. package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts +0 -27
  117. package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts.map +0 -1
  118. package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js +0 -316
  119. package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js.map +0 -1
  120. package/dist/infrastructure/storage/sync/shared/writerLease.d.ts +0 -76
  121. package/dist/infrastructure/storage/sync/shared/writerLease.d.ts.map +0 -1
  122. package/dist/infrastructure/storage/sync/shared/writerLease.js +0 -189
  123. package/dist/infrastructure/storage/sync/shared/writerLease.js.map +0 -1
  124. package/docs/advanced-sheets-gateway-concurrency-problem.md +0 -434
  125. package/docs/architecture.md +0 -224
  126. package/docs/ci.md +0 -293
  127. package/docs/code-guidelines.md +0 -248
  128. package/docs/development.md +0 -74
  129. package/docs/gateway-removal-inventory.md +0 -147
  130. package/docs/git-workflow.md +0 -224
  131. package/docs/google-sheets-sync-scaling-strategy.md +0 -459
  132. package/docs/mikro-orm-adapter-spike.md +0 -89
  133. package/docs/quick-start.md +0 -137
  134. package/docs/sql-layer-plan.md +0 -59
  135. package/docs/sync-bulk-write-benchmark.md +0 -2117
  136. package/docs/sync-observability.md +0 -100
  137. package/docs/task-queue-write-model.md +0 -640
  138. package/docs/typed-sheets-mvp-scope-2026-06-29.md +0 -490
  139. package/docs/typed-sheets-plan.md +0 -417
  140. package/docs/write-and-synchronization-flow.md +0 -179
package/docs/ci.md DELETED
@@ -1,293 +0,0 @@
1
- # CI scenarios
2
-
3
- Hikoutei's CI runs two installed-consumer scenarios against the packed
4
- package. Each is built as an installed consumer: after `npm pack` it imports
5
- from the packed tarball rather than from `src/**` or the source-only test
6
- fake, and neither is allowed to import those.
7
-
8
- 1. **Internal sync E2E** (`scripts/ci/run-api-scenario.mjs`) drives the
9
- internal typed-sheets sync pipeline — projection registration, sheet
10
- provisioning, the bounded effect worker, polling, and the MikroORM-backed
11
- storage/CAS/hash machinery — and loads implementation modules directly from
12
- the packed `dist/` tree.
13
- 2. **Installed root API smoke** (`scripts/ci/run-root-api-scenario.mjs`)
14
- imports ONLY the installed package root entrypoint `hikoutei` (no internal
15
- package subpaths and no source imports) and exercises the public
16
- entity-lifecycle contract an application is meant to use.
17
-
18
- The public contract is the high-level `hikoutei` root API: a SQLite-backed
19
- entity lifecycle. Sheet configuration, registration, and synchronization remain
20
- internal service concerns. The
21
- `hikoutei/orm` and `hikoutei/mikro-orm` package subpaths are intentionally not
22
- published; the root smoke performs those dynamic imports only as negative
23
- boundary assertions (they must be rejected with `ERR_PACKAGE_PATH_NOT_EXPORTED`)
24
- while the lifecycle itself imports only the root `hikoutei` entrypoint. This
25
- guards the boundary directly in an installed consumer in addition to the
26
- source-level tests.
27
-
28
- ## Internal sync E2E
29
-
30
- The scenario is built as an installed consumer: after `npm pack` it imports the
31
- package entrypoints from the packed tarball rather than from `src/**` or the
32
- source-only test fake. It is not allowed to import those. Both backends execute
33
- the same lifecycle:
34
-
35
- 1. create and register a mapped entity and its `System_State`/`User_Input`
36
- projections;
37
- 2. provision the Sheets tabs;
38
- 3. insert, read, update, and delete one entity through the entity manager;
39
- 4. run the bounded effect worker after each write;
40
- 5. edit a User_Input value and verify that polling reports the changed row;
41
- 6. restore the value, delete the entity, and verify the final projection state.
42
-
43
- ## Installed root API smoke
44
-
45
- The root smoke imports only the installed `hikoutei` root entrypoint and drives
46
- the stable public lifecycle end to end against an in-memory SQLite authority
47
- (`dbName: ":memory:"`):
48
-
49
- 1. define a scalar entity with `defineTypedSheetsEntity()`;
50
- 2. open the runtime with `createTypedSheets()` and only `dbName` plus entity tokens;
51
- 3. obtain a request-local manager with `em.fork()`;
52
- 4. `create()` / `persist()` / `flush()` one entity and `findOne()`-verify it;
53
- 5. mutate the loaded entity, `flush()`, and re-read it through a fresh fork to
54
- confirm the mutation committed locally;
55
- 6. `remove()` / `flush()` and verify the entity is gone through a fresh fork;
56
- 7. close the runtime.
57
-
58
- It uses assertions and writes a JSON report plus a clear pass summary. It never
59
- contacts Google Sheets, never needs credentials, and never provisions remote
60
- tabs, so it runs as a packed consumer only in the normal CI, develop, and stable
61
- verify jobs and has no live counterpart. It is local-only — an in-memory SQLite
62
- authority with no backend concept — not a fake-backend scenario like the
63
- internal sync E2E. Its entity lifecycle imports only the
64
- root `hikoutei` package; the `hikoutei/orm` and `hikoutei/mikro-orm` dynamic
65
- imports it performs are negative boundary assertions that must be rejected,
66
- not part of the lifecycle.
67
-
68
- ## Backends and triggers
69
-
70
- The normal CI, develop, and stable verification jobs run **both** installed-consumer
71
- scenarios — the internal sync E2E and the installed root API smoke. The
72
- internal E2E uses the **fake** backend (no Google Sheets contact, no
73
- credentials); the root API smoke is local-only, an in-memory SQLite authority
74
- (`:memory:`) with no backend concept. The workflows are:
75
-
76
- - `.github/workflows/ci.yml` runs on pull requests and pushes to `main` and
77
- `develop`.
78
- - `.github/workflows/develop-version.yml` creates the next patch version and
79
- `develop-vX.Y.Z` tag after a `develop` push.
80
- - `.github/workflows/develop-publish.yml` verifies and publishes that tag with
81
- the `latest` dist-tag.
82
- - `.github/workflows/main-version.yml` creates the next minor version and
83
- `vX.Y.Z` tag after a `main` push.
84
- - `.github/workflows/stable-publish.yml` verifies and publishes that tag with
85
- the `latest` dist-tag too.
86
-
87
- Only the internal sync E2E has a live variant, in the separate
88
- `.github/workflows/live-integration.yml` workflow. It is opt-in via
89
- `workflow_dispatch` — a maintainer with write access dispatches it explicitly;
90
- it never runs automatically on pull requests or pushes — and exercises
91
- that single scenario against real Google Sheets with `--backend=live
92
- --outbound=direct`. The installed root API smoke has no live
93
- counterpart: it is a local-only lifecycle smoke against an in-memory SQLite
94
- authority, so it never runs there.
95
-
96
- Because live is opt-in via `workflow_dispatch`, it is triggered manually by a
97
- maintainer with write access rather than automatically on pull requests, so
98
- there is no forked-PR secret-exposure path. The live path is
99
- service-account-only: the workflow materializes the service-account key in
100
- `$RUNNER_TEMP` and scopes the credential/spreadsheet environment to the live
101
- execution and cleanup steps only, not the whole job, so
102
- checkout/setup/install/build never see them. The live run uses a dedicated test
103
- spreadsheet shared with the service account and the following GitHub Actions
104
- secrets:
105
-
106
- - `GOOGLE_SERVICE_ACCOUNT_CREDENTIALS_JSON` (the service-account key JSON)
107
- - `GOOGLE_SHEETS_TEST_SPREADSHEET_ID`
108
-
109
- No gateway is deployed or invoked — the live path is service-account-only, so
110
- the workflow needs only those two secrets.
111
-
112
- ## Cleanup
113
-
114
- Each live run generates unique tab names from the workflow run ID. The runner
115
- writes a manifest before provisioning, cleans those tabs in a `finally` path,
116
- and the workflow repeats cleanup with `if: always()` so a failed assertion does
117
- not leave the fixture behind. Cleanup uses the same service account
118
- (`deleteSheet` requests through the direct provider's transport) and removes
119
- the shared receipt tab too. Do not point the live secrets at a production
120
- spreadsheet; cleanup is intentionally scoped to the dedicated CI spreadsheet.
121
-
122
- ## Kohkai dependency
123
-
124
- Hikoutei consumes the standalone `@hikoutei/kohkai` package from the
125
- `ManddarinShop/Kohkai` repository. The codec is not a Hikoutei workspace and
126
- its source is not included in the Hikoutei tarball:
127
-
128
- ```json
129
- {
130
- "dependencies": {
131
- "@hikoutei/kohkai": "0.1.0"
132
- }
133
- }
134
- ```
135
-
136
- The root CI, develop publish, and stable publish workflows verify that the
137
- exact declared Kohkai version already exists on npm before installing or
138
- publishing Hikoutei. The first integration release therefore requires:
139
-
140
- 1. publish `@hikoutei/kohkai@0.1.0` from the Kohkai repository using tag `v0.1.0`;
141
- 2. merge the Hikoutei dependency integration;
142
- 3. let the next develop release produce `0.3.2` from the current `0.3.1` baseline.
143
-
144
- Existing Hikoutei releases remain unchanged. The root
145
- `test/kohkai-compatibility.test.ts` pins the Hikoutei shared stable encoding to
146
- the Kohkai compatibility vectors and proves the local codec is byte-compatible
147
- with the generic `@hikoutei/kohkai` codec; the Apps Script codec mirror was
148
- removed with the gateway.
149
-
150
- ## Develop publication
151
-
152
- A push to `develop` runs `.github/workflows/develop-version.yml`. It verifies
153
- the merged package, increments only the patch component, updates
154
- `package.json` and `package-lock.json`, commits the release version, and creates
155
- an annotated tag such as `develop-v0.3.1`. The generated commit is guarded from
156
- being processed as another release.
157
-
158
- The tag triggers `.github/workflows/develop-publish.yml`. It repeats the
159
- unit/type/build/package checks, the installed-package internal sync
160
- E2E, and the installed root API smoke before publishing the numeric package
161
- version with the npm `latest` dist-tag. The version has no `-beta` suffix, and
162
- a plain `npm install hikoutei` resolves this channel:
163
-
164
- ```sh
165
- npm install hikoutei
166
- ```
167
-
168
- The old `develop` dist-tag is no longer updated by this workflow; existing
169
- pinned `hikoutei@develop` installs keep the last develop-tagged version.
170
-
171
- The version calculation is isolated in `scripts/ci/release-version.mjs` and is
172
- covered by `test/release-version.test.ts`. For example:
173
-
174
- ```text
175
- 0.3.0 + patch → 0.3.1
176
- 0.3.1 + patch → 0.3.2
177
- 0.3.2 + patch → 0.3.3
178
- ```The workflow requires a repository `RELEASE_TOKEN` secret with contents write
179
- permission. A token other than the default `GITHUB_TOKEN` is used because a
180
- push made by `GITHUB_TOKEN` does not trigger another workflow; the release tag
181
- must trigger `develop-publish.yml`. The publish job separately requires
182
- `NPM_TOKEN` plus npm provenance. The branch SHA is checked immediately before
183
- pushing, and the commit plus tag use one atomic Git push, so a concurrent
184
- develop merge fails without leaving a stale release tag. The version commit and
185
- npm publication are not atomic; a failed publish can be retried from the
186
- existing `develop-vX.Y.Z` tag after verifying that the version has not already
187
- been published.
188
-
189
- ## Stable publication
190
-
191
- A push to `main` runs `.github/workflows/main-version.yml`. It verifies the
192
- merged package, increments the minor component, updates the package manifests,
193
- commits the release version, and creates an annotated tag such as `v0.4.0`:
194
-
195
- ```text
196
- 0.3.1 + minor → 0.4.0
197
- ```
198
-
199
- The tag triggers `.github/workflows/stable-publish.yml`. That workflow only
200
- validates the numeric tag and matching package manifest versions, reruns the
201
- full checks and installed-consumer scenarios, and publishes to npm with the
202
- `latest` dist-tag. A `develop-v0.3.1` tag cannot trigger this numeric `vX.Y.Z`
203
- workflow.
204
-
205
- The main version workflow uses the same `RELEASE_TOKEN` requirement so its
206
- `vX.Y.Z` tag triggers `stable-publish.yml`. The stable publish job also
207
- requires the repository `NPM_TOKEN` secret and npm provenance before publishing
208
- `latest`. Stable package versions are
209
- immutable on npm, so reusing a published version fails instead of replacing
210
- it.
211
-
212
- Every release is also mirrored to GitHub Packages as the scoped package
213
- `@ManddarinShop/hikoutei` (`npm.pkg.github.com`), repacked from the
214
- verified tarball because the GitHub Packages npm registry only hosts
215
- scoped packages; the mirror uses the workflow `GITHUB_TOKEN` with
216
- `packages: write`.
217
-
218
- Both channels publish `latest`, so their versions must increase globally:
219
-
220
- ```text
221
- develop merges → patch: 0.3.3 → 0.3.4 → 0.3.5 (latest)
222
- main merge (after merging develop) → minor: 0.3.5 → 0.4.0 (latest)
223
- merge main back into develop → 0.4.0
224
- next develop merges → patch: 0.4.0 → 0.4.1 → 0.4.2 (latest)
225
- next main merge → minor: 0.4.2 → 0.5.0 (latest)
226
- ```
227
-
228
- Keep the channels aligned: after a stable main release, merge `main` back
229
- into `develop` so the next develop patch release starts from the new stable
230
- baseline. If a develop release is prepared before that back-merge, its
231
- publish fails loudly on the latest-dist-tag monotonicity check (the develop
232
- version trails the main minor); merge main back and re-run the publish from
233
- the existing `develop-vX.Y.Z` tag. Normal users install the rolling line
234
- with:
235
-
236
- ```sh
237
- npm install hikoutei
238
- ```
239
-
240
- ## Artifacts
241
-
242
- The CI, develop, and stable jobs each emit a JSON report for **both**
243
- scenarios (internal sync E2E and installed root API smoke). The live
244
- job emits a JSON report only for the internal sync E2E, the single
245
- scenario it runs. Both runner scripts (`run-api-scenario.mjs` and
246
- `run-root-api-scenario.mjs`) default their `--summary` option to
247
- `GITHUB_STEP_SUMMARY`, so in GitHub Actions every CI/develop/stable invocation
248
- writes a step summary even though none passes `--summary` explicitly. The live
249
- internal E2E run is the only invocation that passes `--summary` explicitly
250
- (`--summary="$GITHUB_STEP_SUMMARY"`), which resolves to the same default but
251
- documents the intent. The develop and stable verify jobs upload their two
252
- installed-consumer scenario reports as a dedicated reports artifact
253
- (`if: always()`), separate from the package artifact (`if: success()`) that the
254
- publish job consumes. Setup time and steady-state time are reported separately
255
- for the internal E2E so spreadsheet creation and header provisioning do not
256
- distort the internal sync measurements.
257
-
258
- The develop and stable package artifacts are each a single directory that
259
- holds the npm tarball and its `sha256` checksum together under
260
- `$RUNNER_TEMP/hikoutei-{develop,stable}-package`. Their GitHub artifact names
261
- are `hikoutei-develop-package` and `hikoutei-stable-package`; the installed
262
- consumer report artifacts are `hikoutei-develop-fake-installed-consumer` and
263
- `hikoutei-stable-fake-installed-consumer`. The normal CI report artifact is
264
- `hikoutei-fake-installed-consumer`. The publish job downloads the corresponding
265
- package directory, asserts it contains exactly one `hikoutei-*.tgz` and the
266
- checksum file, verifies the checksum line names that exact tarball (not merely
267
- any file present in the directory), and only then runs `sha256sum --check`
268
- before publishing. Name/version validation, npm provenance, the
269
- `latest` dist-tag, and the stale branch SHA checks are enforced by
270
- the corresponding workflows.
271
-
272
- ## Action versions
273
-
274
- Every workflow pins `actions/checkout`, `actions/setup-node`,
275
- `actions/upload-artifact`, and `actions/download-artifact` to the current
276
- Node 24-compatible major (`v5`) so the JavaScript actions run on the runner's
277
- Node 24 runtime:
278
-
279
- | Action | Major | Runtime |
280
- | --- | --- | --- |
281
- | `actions/checkout` | `v5` | Node 24 |
282
- | `actions/setup-node` | `v5` | Node 24 |
283
- | `actions/upload-artifact` | `v5` | Node 24 |
284
- | `actions/download-artifact` | `v5` | Node 24 |
285
-
286
- All four are on the same major, so there is no exception to record. The pin
287
- preserves behavior: artifacts are downloaded by name (not by artifact ID);
288
- `upload-artifact` still zips by default; `setup-node` `cache: npm` is set
289
- explicitly (the package has no `packageManager` field, so the auto-cache
290
- shortcut never applies); and the publish steps set `NODE_AUTH_TOKEN` in their
291
- own `env`, so npm provenance and the `id-token: write` permission are
292
- unchanged. This major is the action's own runtime version; the build and
293
- test job still uses `node-version: 22`.
@@ -1,248 +0,0 @@
1
- # Code Guidelines
2
-
3
- ## Project Identity
4
-
5
- This project is a TypeScript library for using Google Sheets as a lightweight, typed repository layer for MVP apps, internal tools, and low-traffic admin workflows.
6
-
7
- The project is not a full database replacement, not a Prisma/JPA clone, and not a general-purpose Google Sheets API wrapper. Existing libraries such as `google-spreadsheet` and `@googleapis/sheets` already cover low-level Sheets access.
8
-
9
- The core value is safety around common Google Sheets-as-data-store failure modes:
10
-
11
- - schema drift caused by manual sheet edits
12
- - stale writes and lost updates
13
- - invalid row parsing
14
- - duplicate or missing key columns
15
- - API quota pressure
16
- - paced write serialization through the direct provider
17
-
18
- ## Positioning
19
-
20
- Use this wording when describing the project:
21
-
22
- > Typed repository and safe write layer for Google Sheets-backed MVPs.
23
-
24
- Avoid these claims:
25
-
26
- - Google Sheets replacement for MySQL/Postgres
27
- - JPA for Google Sheets
28
- - Prisma for Google Sheets
29
- - transaction-safe database on top of Sheets
30
-
31
- The honest boundary matters. Google Sheets has no native database transaction model, limited quota, weak query capabilities, and manual edit risk. The library should make those constraints explicit instead of hiding them.
32
-
33
- ## MVP Scope
34
-
35
- The first implementation should stay small and prove the core safety model.
36
-
37
- Required MVP capabilities:
38
-
39
- - schema definition API
40
- - adapter boundary for Sheets access
41
- - header/schema validation
42
- - required column detection
43
- - duplicate header detection
44
- - key column detection
45
- - row parsing
46
- - `text`, `number`, `boolean` basic column parsers
47
- - `findAll`
48
- - `findById`
49
- - `insert`
50
- - `update`
51
- - `_version` based optimistic locking
52
- - `SchemaDriftError`
53
- - `ConflictError`
54
- - `ParseError`
55
-
56
- MVP exclusions:
57
-
58
- - relations and joins
59
- - SQL-like query language
60
- - migration engine
61
- - lazy loading
62
- - multi-row atomic transactions
63
- - caching
64
- - request collapse
65
- - retry/backoff
66
- - dashboard UI
67
- - browser support matrix
68
-
69
- ## Architecture
70
-
71
- Keep domain rules separate from application orchestration, infrastructure
72
- storage, and Google API details.
73
-
74
- Recommended package boundaries:
75
-
76
- ```txt
77
- src/
78
- domain/ # 정규화 값, 평가, 충돌, 상태 규칙
79
- shared/ # 도메인 간 공유 상수·인코딩·상태 계약
80
- application/
81
- orm/ # 공개 ORM facade, mapping, flush 계획
82
- sync/ # worker, reconciliation, Sheets 조정, telemetry
83
- adapter/
84
- persistence/
85
- contracts/ # 저장소 공통 계약
86
- providers/mikro-orm/ # 현재 MikroORM + SQLite 구현
87
- sheets/
88
- providers/google-sheets-api/
89
- transport/ # paced REST transport client
90
- model/ # preflight·planner·observation·batch 모델
91
- infrastructure/
92
- storage/ # SQLite canonical state와 outbox
93
- index.ts
94
- ```
95
-
96
- `domain/`과 `shared/`는 외부 SDK를 몰라야 한다. `application/`은 use case와
97
- 동기화 흐름을 조율하고, `infrastructure/`는 SQLite 같은 외부 저장 기술을
98
- 담당한다. `adapter/`는 persistence와 Sheets provider를 각각 계약 뒤에
99
- 격리한다.
100
-
101
- The domain should depend on adapter interfaces, not directly on Google Sheets
102
- SDKs.
103
-
104
- The first tests should use an in-memory fake adapter. Real Google integration tests can come later and should be opt-in because they require credentials and quota.
105
-
106
- ## Code Modification Rules
107
-
108
- Do not modify production source files under `src/**` unless the user explicitly asks for production implementation work.
109
-
110
- When the user asks for planning, review, explanation, test scaffolding, or configuration only:
111
-
112
- - do not edit `src/**`
113
- - do not create new production files under `src/**`
114
- - do not "fix" user-written production code opportunistically
115
- - explain suspected production code issues in the response instead
116
- - wait for explicit approval before changing production code
117
-
118
- Allowed without extra confirmation when requested:
119
-
120
- - documentation files
121
- - planning notes
122
- - test file scaffolding under `test/**`
123
- - TypeScript/package/test configuration files
124
- - `.gitignore`
125
-
126
- If a production source issue blocks the requested work, describe the blocker and ask before editing `src/**`.
127
-
128
- ## Adapter Boundary
129
-
130
- Adapters are split by capability and provider. A provider is a concrete
131
- implementation such as MikroORM or the Google Sheets REST provider; a contract
132
- is the stable boundary that core, ORM, storage, or runtime code consumes.
133
-
134
- - `adapter/persistence/contracts/`: SQL and persistence contracts independent of
135
- MikroORM, Prisma, or another future implementation
136
- - `adapter/persistence/providers/mikro-orm/`: the current MikroORM-backed
137
- entity engine and SQLite storage bridge
138
- - `adapter/sheets/providers/google-sheets-api/`: the current direct Google
139
- Sheets REST provider, separated into transport and model; the single sync
140
- provider
141
-
142
- The adapter should expose sheet-level operations in terms the core needs, not Google-specific concepts.
143
-
144
- Suggested responsibilities:
145
-
146
- - read headers
147
- - read rows
148
- - append row
149
- - replace row by index or key
150
- - optionally re-read row before update
151
-
152
- Do not leak Google SDK response objects into core repository logic.
153
-
154
- ## Schema Drift Policy
155
-
156
- Schema drift must not be silently ignored.
157
-
158
- The library should fail clearly when:
159
-
160
- - a required column is missing
161
- - the key column is missing
162
- - headers are duplicated
163
- - `_version` is required but missing
164
- - a row cannot be parsed into the declared type
165
-
166
- Unexpected extra columns may be allowed by default, but the behavior should be explicit and configurable later.
167
-
168
- ## Stale Write Policy
169
-
170
- MVP optimistic locking should be based on a version column.
171
-
172
- Expected behavior:
173
-
174
- - read current row
175
- - keep its `_version`
176
- - before update, re-check the current `_version`
177
- - if the version changed, throw `ConflictError`
178
- - if unchanged, write the updated row with incremented `_version`
179
-
180
- This is not a true database transaction. Document it as stale-write protection, not full transactional safety.
181
-
182
- ## Google Sheets API Constraints
183
-
184
- Design with quota and latency in mind.
185
-
186
- Current official Sheets API limits to consider:
187
-
188
- - 300 read requests per minute per project
189
- - 60 read requests per minute per user per project
190
- - 300 write requests per minute per project
191
- - 60 write requests per minute per user per project
192
- - 2MB recommended max request payload
193
- - 180 seconds max processing time per request
194
- - quota exceeded responses return 429
195
-
196
- This means the library should favor batch reads and avoid one API call per row where possible.
197
-
198
- ## Future Extensions
199
-
200
- After the MVP is stable, possible extensions are:
201
-
202
- - read cache
203
- - in-flight read collapse
204
- - retry/backoff for 429 and transient 5xx
205
- - Google Sheet template generator
206
- - schema drift report
207
- - audit log sheet
208
- - `_createdAt` and `_updatedAt` system columns
209
- - GitHub Action for schema verification
210
-
211
- Add these only after the base repository model is tested and documented.
212
-
213
- ## Testing Standard
214
-
215
- Tests should prove behavior through realistic sheet states, not through shallow mocks.
216
-
217
- Required test categories:
218
-
219
- - valid schema passes
220
- - missing required column fails
221
- - duplicate header fails
222
- - missing key column fails
223
- - invalid number/boolean parse fails
224
- - `findAll` returns typed rows
225
- - `findById` returns matching row
226
- - `findById` returns null/undefined for missing key, whichever API chooses
227
- - insert rejects duplicate key
228
- - update increments `_version`
229
- - update rejects stale version with `ConflictError`
230
- - extra column behavior is documented by test
231
-
232
- The fake adapter should be simple but should preserve enough behavior to expose row/header bugs.
233
-
234
- ## Documentation Standard
235
-
236
- README should explain:
237
-
238
- - when this library is appropriate
239
- - when it is not appropriate
240
- - Google Sheets quota constraints
241
- - schema drift problem
242
- - stale write problem
243
- - quick start
244
- - API reference
245
- - limitations
246
- - roadmap
247
-
248
- Do not market the project as a general database replacement.
@@ -1,74 +0,0 @@
1
- # Hikoutei Development
2
-
3
- ## Local verification
4
-
5
- Install dependencies and run the normal checks:
6
-
7
- ```sh
8
- npm ci
9
- npm test
10
- npm run typecheck
11
- npm run typecheck:test
12
- npm run build
13
- npm pack --dry-run
14
- ```
15
-
16
- The default test suite uses a fake Sheets provider and SQLite/MikroORM fixtures.
17
- It does not require live Google credentials.
18
-
19
- ## Google integration tests
20
-
21
- Live Google Sheets tests are opt-in. The tracked live path is
22
- service-account-only: it requires a service-account key, a shared
23
- spreadsheet, and consumes external quota. Keep secrets in an untracked
24
- environment file and never commit them.
25
-
26
- The internal end-to-end scenario runs against the fake backend by default
27
- and against the live backend in full-direct mode:
28
-
29
- ```sh
30
- node scripts/ci/run-api-scenario.mjs --backend fake
31
- node scripts/ci/run-api-scenario.mjs --backend live --outbound direct
32
- ```
33
-
34
- The live backend is the service-account-only direct provider, used for
35
- provisioning, outbound effects, observation, `mutateRow`, and cleanup. It
36
- requires `GOOGLE_APPLICATION_CREDENTIALS` and
37
- `GOOGLE_SHEETS_TEST_SPREADSHEET_ID` — and nothing else. The report
38
- records only `sheetMatched: true` for the direct mode and never prints
39
- spreadsheet IDs.
40
-
41
- `--backend live` always runs the direct provider; the removed gateway mode no
42
- longer exists, so any `--outbound` value other than `direct` is rejected.
43
-
44
- ## Benchmarks
45
-
46
- Performance measurements should be recorded in
47
- [`sync-bulk-write-benchmark.md`](sync-bulk-write-benchmark.md). Separate:
48
-
49
- - one-time setup from steady-state work
50
- - raw transport writes from full worker drain
51
- - reconciliation from the normal append path
52
- - local SQLite/ORM time from HTTP and provider time
53
-
54
- The benchmark history is not a universal Sheets performance guarantee. Network
55
- latency, Sheets API behavior, quotas, and spreadsheet state can
56
- change the result. The full direct provider (`googleSheetsApi`) is not a
57
- performance claim either: the raw-transport benchmarks under `scripts/bench/`
58
- measure the unguarded API path (no receipts, no compare-and-set), while the
59
- provider adds fail-closed guarantees (receipts and compare-and-set), so its
60
- throughput is unverified until measured through the full worker.
61
-
62
- ## Package preview
63
-
64
- Before publishing, inspect the tarball contents:
65
-
66
- ```sh
67
- npm pack --dry-run
68
- ```
69
-
70
- The package should include the built `dist/` output and the public
71
- documentation only — no Apps Script deployment. The sync provider calls the
72
- Google Sheets REST API (`spreadsheets.get` / `spreadsheets.batchUpdate`)
73
- through a service account, so the tarball contains no gateway sources or
74
- manifest; the service account needs only the Spreadsheets scope.