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.
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts +64 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts.map +1 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js +440 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js.map +1 -0
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts +3 -4
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
- package/dist/application/sync/service/SyncServiceBootstrap.js +7 -4
- package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.d.ts +20 -33
- package/dist/application/sync/telemetry/syncTiming.d.ts.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.js +14 -12
- package/dist/application/sync/telemetry/syncTiming.js.map +1 -1
- package/dist/infrastructure/storage/errors.d.ts +10 -5
- package/dist/infrastructure/storage/errors.d.ts.map +1 -1
- package/dist/infrastructure/storage/errors.js +10 -7
- package/dist/infrastructure/storage/errors.js.map +1 -1
- package/dist/infrastructure/storage/index.d.ts +6 -4
- package/dist/infrastructure/storage/index.d.ts.map +1 -1
- package/dist/infrastructure/storage/index.js +3 -2
- package/dist/infrastructure/storage/index.js.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.d.ts +12 -11
- package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.js +17 -109
- package/dist/infrastructure/storage/sqlite/schema.js.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts +1 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js +1 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.js +1 -2
- package/dist/infrastructure/storage/state/observation/observationWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js +2 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.js +1 -1
- package/package.json +13 -7
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts +0 -66
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js +0 -123
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts +0 -111
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js +0 -369
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts +0 -127
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js +0 -552
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts +0 -85
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js +0 -74
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts +0 -31
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js +0 -222
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts +0 -14
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js +0 -25
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts +0 -61
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js +0 -296
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts +0 -17
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js +0 -80
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts +0 -13
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js +0 -248
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts +0 -141
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js +0 -318
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts +0 -143
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js +0 -16
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts +0 -33
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js +0 -259
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts +0 -27
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js +0 -316
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts +0 -76
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.js +0 -189
- package/dist/infrastructure/storage/sync/shared/writerLease.js.map +0 -1
- package/docs/advanced-sheets-gateway-concurrency-problem.md +0 -434
- package/docs/architecture.md +0 -224
- package/docs/ci.md +0 -293
- package/docs/code-guidelines.md +0 -248
- package/docs/development.md +0 -74
- package/docs/gateway-removal-inventory.md +0 -147
- package/docs/git-workflow.md +0 -224
- package/docs/google-sheets-sync-scaling-strategy.md +0 -459
- package/docs/mikro-orm-adapter-spike.md +0 -89
- package/docs/quick-start.md +0 -137
- package/docs/sql-layer-plan.md +0 -59
- package/docs/sync-bulk-write-benchmark.md +0 -2117
- package/docs/sync-observability.md +0 -100
- package/docs/task-queue-write-model.md +0 -640
- package/docs/typed-sheets-mvp-scope-2026-06-29.md +0 -490
- package/docs/typed-sheets-plan.md +0 -417
- 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`.
|
package/docs/code-guidelines.md
DELETED
|
@@ -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.
|
package/docs/development.md
DELETED
|
@@ -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.
|