Repository navigation
Commit 4d99158
fix(driver-sql): a read-only boot refuses a SQLite datasource whose file can be neither opened nor created, instead of reporting it connected (#25007)
Fixes #24961
Clause-②: no (narrowing)
The narrowing face: a read-only boot (`sqliteAbsentFile:
'empty-in-memory'`, which `os migrate plan` and the read-only dry runs
use) that answered `datasource 'NAME': connected` for a SQLite path that
can be neither opened nor created now reports that datasource failed,
naming the file. No exported declaration changes. The review of record
is owed at `CONTRACT_REVIEW_TIER` on that face.
Dev session `session_01QK6pdbxKZiHdFApL8RYVh9` (a `mode:subagent` run),
dispatched by the `domain:engine` seat 2 under claim 6303642231.
## What was wrong
`resolveSqliteAbsentFileTarget`
(`packages/drivers/driver-sql/src/sql-driver.ts`) is the one place the
"does the SQLite target exist?" judgement is made. Under
`'empty-in-memory'` it answered `:memory:` whenever
`existsSync(filename)` was `false`. `existsSync` is `false` for every
path it cannot stat, not only for an absent one. So a path under a
regular file read as absent, the read-only boot opened an empty
in-memory database over it, and the datasource read `connected`. The
boot that writes refuses the same path in `SqlDriver.connect()`.
## What changed
- **The judgement asks the operating system, and writes nothing**
(`probeAbsentSqliteDatabaseFile`, new and module-private in
`packages/drivers/driver-sql/src/sqlite-database-file.ts`, next to
`makeDatabaseDirectory`, whose walk it mirrors without the `mkdir`):
- `stat(filename)` succeeds: the file exists and is opened as before.
- `stat(filename)` answers anything but `ENOENT` (`ENOTDIR`, `EACCES`,
`ELOOP`, …): the file cannot exist. That answer is the refusal.
- `ENOENT`: walk up to the nearest level that exists. A level whose name
a dangling symbolic link holds is refused. Otherwise the level must
allow write and search (`access(W_OK | X_OK)`), the permission that
creating the next level or the file needs. Its refusal (`EACCES`, or
`EROFS` on a read-only mount) is the answer.
- **The refusal is the driver's `SqliteDatabaseFileError`**, which names
the file and carries the system's `code`. It is the error the writing
boot's `connect()` already raises (#23696), and
`isSqliteFileOpenFailure` (#24955) already classifies it.
`resolveSqliteAbsentFileTarget` throws it, so the `SqlDriver`
constructor throws it too, and no Knex instance is built over the file.
- **Both callers of the exported judgement get the same answer.** The
service layer's `resolveSqliteDriver` resolves the wasm rung's filename
through `resolveSqliteAbsentFileTarget` before it builds any rung
(`sqlite-driver-fallback.ts`, the line under the "filename the wasm rung
must open" note). So the native rung, the wasm rung and production
(`dev: false`, native unprobed) all reject with the same error. The
factory's `create()` rejects, and `DatasourceConnectionService` records
the datasource `failed` under its declared name, through the same
`handleFailure` path the writing boot takes. **No line of
`sqlite-driver-fallback.ts` changed.** `applyAutoVacuumIncremental` and
`isSqliteFileOpenFailure`, which #24960 is changing, are untouched.
- **`knexConfigFor` asks the judgement for a SQLite client only.** A
server dialect's `connection` string is a URL, not a filename. Measured
on base: `new SqlDriver({ client: 'pg', connection:
'postgres://app@db.example:5432/main', sqliteAbsentFile:
'empty-in-memory' })` had its connection string rewritten to `:memory:`
and answered `sqliteOpenedEmptyInMemory: true`. That contradicts
`SqlDriverConfig.sqliteAbsentFile`'s "ignored for non-SQLite dialects".
No producer passes the option to a server dialect, but the judgement now
does filesystem work and can throw, so it must not be handed a URL. It
sits inside `knexConfigFor`, on this claim's file surface.
- **Docs:** `SqliteAbsentFileMode` and `resolveSqliteAbsentFileTarget`
say that only a file `'create'` could bring into existence is absent.
The `SqliteDatabaseFileError` class doc is left as it stands. The
probe's own doc says where the error is thrown from. This avoids a
textual conflict with #24960, which edits that block.
- One changeset: `@objectstack/driver-sql` `patch`, with the `Clause-②`
line, the BREAKING banner, FROM and TO, and the ADR-0087 disposition
(`not-required (no-migration-prescription)`).
## H1: what the filesystem answers, measured before the change
Measured on Node v22.22.0 with a probe script (deleted), as root and as
uid 65534 (`setpriv`). Nothing was written.
| path | `existsSync` | `stat(file)` | nearest existing level |
`access(W_OK and X_OK)` there |
|---|---|---|---|---|
| under a regular file (`plain/archive.db`) | false | `ENOTDIR` | n/a |
n/a |
| deeper (`plain/sub/archive.db`) | false | `ENOTDIR` | n/a | n/a |
| file in a `0700` dir that is not ours (uid 65534) | false | `EACCES` |
n/a | n/a |
| absent file in a `0555` dir | false | `ENOENT` | the dir | root: ok;
uid 65534: `EACCES` |
| `/proc/os-24961/archive.db` | false | `ENOENT` | `/proc` | root:
**ok**, though its `mkdir` answers `ENOENT` (the #23696 reading); uid
65534: `EACCES` |
| `/opt/claude-code/os-24961/archive.db` (ro mount) | false | `ENOENT` |
`/opt/claude-code` | `EROFS` for both |
| absent file, existing or creatable dir | false | `ENOENT` | a writable
dir | ok |
| a dangling symlink as the directory (measured by its pin) | false |
`ENOENT` | `stat` passes over the link to its parent; `lstat` still
finds the link | ok on the parent |
`stat` already separates "absent" (`ENOENT`) from "cannot exist"
(anything else). `access` on the nearest existing level covers
permission and read-only mounts.
## Public door, before and after
`bin/run-dev.js migrate plan --database-url file:main.db` in a throwaway
project. `dist/objectstack.json` declares `archive` (`driver: 'sqlite'`)
and binds one object, `os24961_note`, to it. The driver-sql `dist/` was
built from base `4b8f597ba` for "before" and from `3370501f9` for
"after". The parent directory listing was compared before and after each
run.
| `archive` filename | before (base) | after |
|---|---|---|
| `…/plain/archive.db` (under a regular file) | `datasource 'archive':
connected`; `Planned 1 declared datasource(s) … archive
(…/plain/archive.db) — connected under the declared name`; `+
os24961_note [create_table, 8 column(s)]`; exit 0 | `datasource
'archive': connect failed — SQLite database file '…/plain/archive.db'
cannot be opened or created: ENOTDIR: not a directory, stat
'…/plain/archive.db'. (1 object(s) bind to it explicitly (os24961_note)
… fail-fast per ADR-0062 D5)`; nothing planned; exit 1 |
| `…/plain/sub/archive.db` | not run at the door on base | the same
refusal, `ENOTDIR`; exit 1 |
| `…/link/archive.db` (a dangling symlink as the directory) |
`connected`, planned `create_table`, exit 0 | `connect failed — … cannot
be created: its directory '…/link' cannot be made ('…/link' is a
symbolic link to a path that does not exist): ENOENT …`; exit 1 |
| `…/ok/archive.db` (absent, directory exists) | not run at the door on
base (the existing behaviour, pinned) | `connected`, `+ os24961_note
[create_table, 8 column(s)]`, exit 0, file not created |
| `…/ok/new/deeper/archive.db` (absent, directories missing) | not run
at the door on base (the existing behaviour, pinned) | `connected`,
planned, exit 0, no directory created |
Every run, before and after, left the listing unchanged. The writing
boot over the same path (`migrate apply --yes`, measured on base; this
PR does not touch that path) answers `connect failed — … cannot be
created: its directory '…/plain' could not be made: EEXIST …`, exit 1.
The two boots now agree. Their codes differ (`ENOTDIR` here, `EEXIST`
there) because the read-only judgement asks `stat` where the writing
boot asks `mkdir`. Each is the system's own answer.
Resolver answers on the built `dist/` after the fix, as uid 65534: a
`0555` directory gives `EACCES` ("cannot be opened or created"), one
level deeper gives `EACCES` ("its directory … cannot be made"),
`/proc/os-24961/…` gives `EACCES`, and the read-only mount gives
`EROFS`. As root, the `0555` directory and `/proc/os-24961/…` read
absent. Nothing was written in any case.
## Pins
`packages/drivers/driver-sql/src/sql-driver-sqlite-absent-file.test.ts`
(relative `src` import), a new describe block:
- A path under a regular file, at either depth:
`SqliteDatabaseFileError`, `code` `ENOTDIR`, `filename` the file. The
directory listing is unchanged.
- A dangling symlink as the directory: refused (`ENOENT`). The link is
still dangling, and its target was not created.
- A `0555` directory as a non-superuser: `EACCES`, at both depths, and
nothing created (`skipIf` superuser). As the superuser the same
directory reads absent, because the writing boot can create there
(`runIf` superuser). CI's runner is not root, so CI runs the first. This
container runs as root, so here it ran the second, and the first was
measured on `dist/` as uid 65534 (above).
- CONTROL: an absent file in an existing directory, and under missing
directories, still answers `:memory:` and writes nothing.
- `'create'` and the default hand the path on unchanged.
- `new SqlDriver({ … sqliteAbsentFile: 'empty-in-memory' })` over the
path throws the same error. Without the option, the same driver's
`connect()` refuses the same file. Nothing is written.
- A `pg` driver with a connection string keeps its string and answers
`sqliteOpenedEmptyInMemory: false`. This is an assertion added to the
existing "inert for non-sqlite dialects" case.
`packages/services/service-datasource/src/__tests__/sqlite-driver-fallback-readonly-unopenable.test.ts`
(new; it resolves `@objectstack/driver-sql` through `dist/`, a
`KNOWN_UNALIASED_TEST_IMPORTS` pair). Real `better-sqlite3`, real wasm
SQLite, real files. An addon that cannot load is faked where
`better-sqlite3` loads it, the same way as the #24955 file:
- `resolveSqliteDriver({ sqliteAbsentFile: 'empty-in-memory' })` under a
regular file, on the development native rung, the development wasm rung,
and production: each rejects with `SqliteDatabaseFileError` / `ENOTDIR`.
It never resolves an engine, never warns, and writes nothing.
- An absent file that can be created: the native rung opens empty in
memory (`sqliteOpenedEmptyInMemory: true`), and the wasm rung opens
`:memory:` (`engine: 'sqlite-wasm'`). Nothing is on disk for either.
- The read-only boot `os migrate plan` composes
(`createDefaultDatasourceDriverFactory({ sqliteAbsentFile:
'empty-in-memory' })`): `create()` rejects with the refusal (code
asserted). Through `DatasourceConnectionService.connectDeclared`, in
development and in production, the datasource reads `failed`, its reason
names the file, no driver is registered under `archive`, and nothing is
written.
This pin file is the only `service-datasource` line in the diff. It is a
test, outside the package's `files[]`, and the cross-lane declaration to
`domain:services` is the seat's.
## Ablation
Both legs went through `scripts/ablation-replace.mjs` (WRAP), each with
a `trap` restore on absolute paths. Each restore was proven by blob
equality with `HEAD`, an empty `git diff HEAD`, and an empty `git status
--porcelain`.
- **Leg 1, the judgement.** The probe's first answer was replaced by
plain `existsSync` semantics: stat succeeds means exists, anything else
means absent. The marker identifier was `ABLATION_24961`. Anchor 1 to 0.
Blob `fbde48ea8f70` became `fb3ea19400da` and then returned to
`fbde48ea8f70`. Then `pnpm --filter @objectstack/driver-sql build`, and
`ablation-dist-preflight` found the marker in 2 built files. Predicted:
driver-sql 3 failed, 12 passed, 1 skipped; service-datasource 6 failed,
22 passed. **Observed the same.** Red in driver-sql: under a regular
file, the dangling link, and the constructor. Red in service-datasource:
the native, wasm and production rungs, the factory, and the service in
development and in production. The absent-file controls, the #24955 file
(9) and `sqlite-driver-fallback.test.ts` (11) stayed green. After the
restore: rebuild, and `--absent` found the marker in none of the 6 built
files, with the tree clean. The first attempt at this leg was **refused
by the tool before anything ran**: its anchor sat inside its own
replacement, so the anchor count could not drop. Nothing was built or
tested on that attempt. The anchor was changed and the leg re-run.
- **Leg 2, the SQLite-client gate in `knexConfigFor`.** `||
!this.isSqlite` was removed. Blob `dea47a1a378e` became `38af381b3aac`
and then returned. The subject is a relative `src` import, so this leg
has no dist step. Predicted and observed: 1 failed ("is inert for
:memory: and for non-sqlite dialects"), 14 passed, 1 skipped.
## Local verification
Builds:
- Base closure for the before-door: `turbo run build
--filter='@objectstack/cli^...' --concurrency=2`, 60 of 60.
- After merging `origin/main` (`4fe3af9d7`, merge head `28d5dd67e`):
`pnpm install --frozen-lockfile`, then `turbo run build
--filter='@objectstack/cli^...' --filter=@objectstack/cli
--concurrency=2`, 61 of 61.
Tests:
- Full driver-sql suite (`pnpm --filter @objectstack/driver-sql exec
vitest run --maxWorkers=2`) on `02302719e`: 225 files passed, 12
skipped; 3698 tests passed, 210 skipped. The skips include the live PG
and MySQL cells (no live server here) and this file's superuser split.
- Full service-datasource suite on `02302719e`: 51 files, 916 passed.
- On the merge head `28d5dd67e`: the driver-sql pin file gave 15 passed
and 1 skipped; the three service-datasource fallback files gave 28
passed.
Merge state: `origin/main` was merged at `4fe3af9d7`. Since then `main`
has gained `fbb42d900` (plugin-auth) and `86d236071` (rest), which touch
none of this diff's packages; the queue rebuilds on current `main`.
Against #24960's branch head `6d3dd6247` (fetched into a ref of my own),
`git merge-tree` reports no conflict: that branch edits
`SqliteDatabaseFileError`'s doc block, which this diff leaves alone.
Typecheck:
- `pnpm --filter @objectstack/driver-sql typecheck` passed, and its `tsc
--listFiles` includes the edited test file.
- `pnpm --filter @objectstack/service-datasource typecheck`: the first
run (`02302719e`) failed on the new pin file (a `.then` on `create()`'s
union return type). It was fixed in `7c66e98f6`, and it passes there and
on `28d5dd67e`.
Driver ledger (`pnpm check:driver-conformance`), before (`4b8f597ba`)
and after (`28d5dd67e`): `55 covered cell(s), 0 in the DEBT ledger, 0
exempt` both times. The dialect axis is unchanged: 9 suites, 11 of 11
dialect-scored cells matrix-routed. The ledger did not move.
Gates:
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived **72** commands at the
merge head `28d5dd67e`. The derivation's first line names
`objectstack-ai/objectstack` at `28d5dd67e`. The PM's list at
`e4d604efd` had 55, and the 17 extra come from this diff's changeset and
its new service-datasource test. All 72 ran one at a time, each exit
code captured before any pipe.
- First pass: 70 exited 0, and 2 exited 3 (PREREQUISITE NOT MET).
- `node scripts/check-plugin-teardown-shape.mjs --self-test`: its
positive-control commit `621a48760788` was outside this shallow clone. I
fetched that one commit into a ref of my own (`--depth=1`) and re-ran:
exit 0, 48 cases.
- `pnpm check:dual-build-cjs-loads`: ten packages outside the CLI
closure had no `dist/`. I ran `turbo run build
--filter='!@objectstack/docs' --concurrency=2` (76 of 76) and re-ran:
exit 0.
- `--ran` over the first pass: 72 derived, 70 run, 2 NOT-MEASURED, 0
UNRUN. Over the final record: 72 derived, 72 run, 0 NOT-MEASURED, 0
UNRUN, and the tool exited 0.
- After the merge (`packages/spec` moved on `main`'s side), `pnpm
--filter @objectstack/spec check:generated`: all 14 generated artifacts
up to date, exit 0.
- `test:repo` (the vitest `repo` project) was not run locally: the diff
touches none of its tests, and the derivation does not name it. It is
declared to CI (`Test Core`).
- Every family CI runs is read from this PR's check-runs, not re-run
here.
## Acceptance notes
- **Boundary, measured: one shape still reads absent without a write.**
`/proc/MISSING/…` as root: `access` answers ok, and only `mkdir`
(`ENOENT`) would say no. The governing text, "`'empty-in-memory'` leaves
the filesystem untouched", rules out finding out by trying. So the
read-only boot still opens it in memory, and the writing boot still
refuses it. As a non-root user it is refused (`EACCES` on `/proc`). The
probe's doc names this.
- **Telemetry sibling on a read-only boot.**
`provisionTelemetryDatasource` wraps `resolveSqliteDriver` in a
best-effort `try`. Over an unopenable sibling path, the read-only boot
now gets no sibling, as the writing boot already did, where before it
got an in-memory one. The sibling sits next to the primary, and an
unopenable primary already fails the boot first. This is by code reading
only, not measured at a door.
- **`NODE_ENV=production`:** pinned at the service layer (`dev: false`,
both the resolver and the connection service). The production entry
`bin/run.js` was not driven: NOT MEASURED at that door.
- **`os migrate audit-mail-bodies` without `--apply`:** its archive arm
exists only on PR #24934, which is unmerged. NOT MEASURED. Its read-only
boot builds its datasources through the same factory, so the factory
pins are its mechanism.
- **Not addressed here, in the reporting seat's hands:** the writing
boot over a dangling symlink as a declared datasource's directory.
Measured on base and unchanged by this PR: `migrate apply --yes` prints
`datasource 'archive': connected`, then a false "native better-sqlite3
unavailable (ABI mismatch or not built)" step-down warning, and exits 1
later. The driver's `makeDatabaseDirectory` accepts `EEXIST` on the
link. better-sqlite3's code-less "directory does not exist" `TypeError`
is not one of the codes `connect()` refuses on. The report carries it as
a finding.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QK6pdbxKZiHdFApL8RYVh9)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 02e6d73 commit 4d99158
5 files changed
Lines changed: 483 additions & 6 deletions
File tree
- .changeset
- packages
- drivers/driver-sql/src
- services/service-datasource/src/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
163 | 172 | | |
164 | 173 | | |
165 | 174 | | |
| |||
175 | 184 | | |
176 | 185 | | |
177 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
| 159 | + | |
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
165 | | - | |
166 | 165 | | |
167 | 166 | | |
168 | 167 | | |
| |||
5159 | 5158 | | |
5160 | 5159 | | |
5161 | 5160 | | |
5162 | | - | |
| 5161 | + | |
| 5162 | + | |
| 5163 | + | |
| 5164 | + | |
5163 | 5165 | | |
5164 | 5166 | | |
5165 | 5167 | | |
| |||
5182 | 5184 | | |
5183 | 5185 | | |
5184 | 5186 | | |
| 5187 | + | |
| 5188 | + | |
| 5189 | + | |
| 5190 | + | |
| 5191 | + | |
| 5192 | + | |
| 5193 | + | |
| 5194 | + | |
| 5195 | + | |
| 5196 | + | |
| 5197 | + | |
| 5198 | + | |
| 5199 | + | |
| 5200 | + | |
| 5201 | + | |
| 5202 | + | |
| 5203 | + | |
| 5204 | + | |
5185 | 5205 | | |
5186 | 5206 | | |
5187 | 5207 | | |
| |||
5191 | 5211 | | |
5192 | 5212 | | |
5193 | 5213 | | |
5194 | | - | |
| 5214 | + | |
5195 | 5215 | | |
5196 | 5216 | | |
5197 | 5217 | | |
| |||
6437 | 6457 | | |
6438 | 6458 | | |
6439 | 6459 | | |
| 6460 | + | |
| 6461 | + | |
| 6462 | + | |
| 6463 | + | |
| 6464 | + | |
| 6465 | + | |
| 6466 | + | |
| 6467 | + | |
| 6468 | + | |
| 6469 | + | |
6440 | 6470 | | |
6441 | 6471 | | |
6442 | 6472 | | |
6443 | 6473 | | |
6444 | 6474 | | |
6445 | | - | |
| 6475 | + | |
6446 | 6476 | | |
6447 | 6477 | | |
6448 | 6478 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
0 commit comments