Repository navigation
Commit 4008e62
feat(platform-objects): sys_personal_view, a reader's own named list views, with the owner's delete door (server step) (#29575)
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9RdqjLGpNWRr2BGJHkC5giq)_
Fixes #29443
Clause-②: yes (widening)
Metadata-protocol: none
Clause-② measured: on base (`origin/main` @ `81cd9eb0`),
`sys_personal_view` does not exist, so every
`/api/v1/data/sys_personal_view` call is refused as an unknown object.
No registrar mounts `DELETE /api/v1/personal-views/:id` there (read from
the source; not driven over HTTP on base). On head both are served, and
the dogfood file below is 16/16. No existing answer narrows: the only
grants added name the new object, no shipped set loses anything, and the
generic data `DELETE` keeps answering what it answered (pinned).
Metadata-protocol stays `none`. This is a platform object holding user
data, not a key, value or metadata type an author writes, and `/meta`
does not serve it (pinned).
This closes the server step of #29443: read, create, rename, and the
owner's own delete. Which view opens by default stays with the Console's
existing per-object pin (see *Round 2*). The Console half follows on the
card.
## 维护者速读
- **改了什么**:新增平台对象
`sys_personal_view`(个人视图)。一行就是某个用户为某个对象保存的一个命名列表视图,含所有者、对象名、名称和 ListView
配置。通用数据 API 可以读、建、改名。默认视图不在这里存,沿用 Console
已有的按对象置顶(`list.view.pinned`,#27594)。另外新增一个所有者删除门 `DELETE
/api/v1/personal-views/:id`,只能删除调用者自己的视图。
- **为什么改**:依据 #28971 裁决 C,「我的视图」是用户数据,不是视图元数据。这是第一版的服务端一半,以
`sys_user_preference` 为模板。删除门按席位裁决 6583289013 建,沿用 #26161 字母 A 的形状。
-
**风险与回滚**:这是纯新增对象和新增路由,已有对象和已有答复都不变。行级范围只到所有者本人;组织管理员的通配绕过对该对象已关闭,读不到、改不了、也删不了成员的视图(都答
404)。平台管理员(`admin_full_access`)保留通配:通过通用数据 API 能读、改、删任何成员的视图,和
`sys_user_preference` 的现状一致,已有测试钉住,并列为待决问题;只有本 PR
的删除门拒绝他删别人的视图(403)。外部成员本版没有授权,也列为待决问题。视图的所有者在保存时确定,任何非系统调用都不能改
`user_id`(403)。回滚就是 revert 本 PR,表留着无害。
-
**先看**:`packages/platform-objects/src/identity/sys-personal-view.object.ts`;`packages/plugins/plugin-auth/src/personal-view-hooks.ts`;`packages/plugins/plugin-auth/src/personal-view-routes.ts`;`packages/plugins/plugin-security/src/objects/default-permission-sets.ts`
里的两处新条目。
- **两分钟验证**:`pnpm --filter @objectstack/plugin-auth exec vitest run
src/personal-view-hooks.test.ts src/personal-view-routes.test.ts`,预期 35
个全绿;`pnpm --filter @objectstack/dogfood exec vitest run
test/personal-view-row-scope.dogfood.test.ts`,预期 16 个全绿。
- **需要你做的**:无。路由名 `/api/v1/personal-views/:id`
已由席位接受;两个待决问题(平台管理员的通配、外部成员)见文末。
## What changes
- **New platform object `sys_personal_view`**
(`packages/platform-objects/src/identity/sys-personal-view.object.ts`).
plugin-auth's identity manifest registers it right after
`sys_user_preference`, on the same template.
- Fields: `user_id` (owner, lookup `sys_user`), `object_name`, `label`
(the name the owner gave it), `config` (json, spec's `ListView` shape),
and `created_at` / `updated_at`. There is no default column: which view
opens by default is the Console's existing pin (see *Round 2*).
- It is `managedBy: 'system-data'` and `access: { default: 'private' }`,
so a plain (non-super-user) `'*'` wildcard does not reach it. A wildcard
carrying `viewAllRecords` / `modifyAllRecords` still does (ADR-0066 D2):
`organization_admin`'s is closed below, and `admin_full_access`'s is
not.
- It declares the owner-only `defaultRowScope` `user_id ==
current_user.id` for every operation.
- The table carries the organization column, so the Layer 0 wall bounds
it too (ADR-0131). The regenerated tenancy census counts it in reach.
- **Two write rules**
(`packages/plugins/plugin-auth/src/personal-view-hooks.ts`). Both
registrars register them: `AuthPlugin` on `kernel:ready`, and
`IdentityObjectsPlugin.start()` on a kernel without auth.
- **The body must be a `ListView` for the row's own object.** A create,
and an update that sends `config` or `object_name`, parse the resulting
body with spec's `ListViewSchema`; an update fills whichever of the two
it did not send from the stored row. A body that does not parse is
refused `400 VALIDATION_FAILED` (an existing code, through
`validationFailure`), with one `fields[]` entry per issue, each path
under `config`. Text that is not JSON is refused the same way, with the
single field `config`. A body whose `data.object` names another object
than `object_name` is refused the same way, on `config.data.object`
(code `object_mismatch`). A rename that sends neither is not re-judged.
- **The owner is fixed when the view is saved.** An update that changes
`user_id` is refused `403 PERMISSION_DENIED` for every caller but the
system, platform administrators included; an echo of the stored owner is
no change. The row scope alone does not hold this for an organization
member: `member_default`'s `'*'` update floor (`created_by ==
current_user.id`, positions `org_member`) is a held update policy, so
the object's declared default does not apply to an update, and a
re-parent's post-image still carries the member's own `created_by`. The
round-2 battery found it (REST `PATCH user_id` answered 200).
- **The owner's delete door, `DELETE /api/v1/personal-views/:id`**
(`packages/plugins/plugin-auth/src/personal-view-routes.ts`). Seat
ruling 6583289013; it takes the shape of #26161's unreact door, ruling
6422861164 letter A. `AuthPlugin` mounts it on the kernel's HTTP server,
next to its auth routes. It removes one row, and only the caller's own:
1. The caller is resolved through the one composition,
`resolveRequestExecutionContext`.
2. Anonymous is refused `401 UNAUTHENTICATED`.
3. A held sign-in gets its policy's `403`.
4. A query parameter is refused `400 VALIDATION_ERROR`.
5. The row is read **as the caller**. Another member, an organization
administrator, and an unknown id all get `404 RECORD_NOT_FOUND`.
6. A row the caller can read but does not own (`user_id !== caller`;
today only a platform administrator reads one) is refused loudly with
`403 PERMISSION_DENIED`, and the row stays. This is the door's own
answer; the platform administrator's generic data `DELETE` is not
refused (see Grants).
7. Only then is the row removed, as the system, with `attributedUserId`
set to the caller and inside the caller's organization. The answer is
`200 { id, deleted: true }`.
No grant moves: `member_default` is unchanged, and the generic data
`DELETE` of the owner's own row is still `403` (pinned). **Route name:**
`/api/v1/personal-views/:id`, the unreact door's convention (a plural
resource noun under `/api/v1`, the record id in the path, no query). The
seat accepted it in review.
- Its ledger row:
`packages/plugins/plugin-auth/src/personal-view-route-ledger.ts` (`gap`:
the Console's My views list is the consumer, and the SDK has no method
yet), with `personal-view-route-ledger.conformance.test.ts`.
- Its `scripts/check-route-envelope.mjs` declaration (`responses: 0, ok:
0, err: 0`; every body goes through the shared pair).
- Its `scripts/check-single-authz-resolver.mjs` delegator row.
- Its removal is one new elevated write call site in the tenant audit
census. `node scripts/tenant-audit-census.mjs --write` regenerated the
artefacts (256 sites), and the hand-written figures on
`content/docs/permissions/tenant-audit-census.mdx` follow it (255 → 256,
78 → 79, 145 → 146).
- A server that registers no `DELETE` verb (a partial embedding, or a
test double) skips the door with a warning that names it, instead of
failing the boot.
- **Grants**
(`packages/plugins/plugin-security/src/objects/default-permission-sets.ts`):
- `member_default`: read, create and edit on `sys_personal_view`, and
**no delete**. The set is bound to the `everyone` anchor, which may not
carry a delete bit (ADR-0090 D5).
- `organization_admin` (and the derived `organization_admin_no_bypass`):
an explicit `sys_personal_view` entry with no `viewAllRecords` /
`modifyAllRecords`. For this object it overrides the wildcard's
super-user bits, the `sys_audit_log` pattern. An organization
administrator reads, writes and deletes only their own views.
- `admin_full_access` is untouched (seat ruling 6587007982). Its
wildcard carries `viewAllRecords` and `modifyAllRecords`, so through the
generic data API it **reads, edits and deletes** every member's view,
exactly as it does `sys_user_preference` today. Its pins forbid a
per-object entry there, and the rescue path depends on it. Pinned as a
recorded boundary at engine and REST level; listed as an open question
below. Only the owner's door refuses it (`403`).
- `external_default` gets **no** grant in this version: an external
member cannot save a personal view. Listed as an open question for the
Console half.
- **The id** is the engine's record id (nanoid). It never contains `.`,
so it can never be read as `object.key` view syntax (pinned).
- **Not view metadata:** `/meta/view` does not list a personal view, and
`/meta/view/<id>` answers 404 (pinned). There is no metadata type, no
package export and no metadata write door.
- **The only spec touch** (allowed by seat ruling, recorded in round 2):
one line, `'sys_personal_view'` in
`PLATFORM_OBJECTS_BY_PACKAGE['platform-objects']`
(`packages/spec/src/system/constants/platform-object-names.ts`). Nothing
else under `packages/spec` changes.
- **Rosters:**
- the CLI organization-ownership inventory row (`fate: 'attribution'`);
- the i18n extract config;
- the en / zh-CN / ja-JP / es-ES object bundles, regenerated and then
translated by hand;
- `scripts/platform-object-tenancy-census.json`, regenerated (92
registered / 47 in reach).
- `content/docs/permissions/system-context.mdx`: row 26c owns the owner
rule's system skip (`ctx.session.isSystem`), and the generated site
register is rewritten with `--write` (166 read sites).
- **Changeset** `.changeset/29443-personal-views-server.md`: patch for
platform-objects, plugin-auth, plugin-security, cli **and spec** (the
spec package publishes a changed constant, and AGENTS.md asks every
publishing change to carry a changeset).
Not in scope, and not touched: sharing, team views, admin management, a
field picker, packaging, any `ui/**` change, #29037's
`ViewScopeSchema.personal` / `sharing.type`, and a shared own-row door
helper (review S3; the seat files it as a follow-up card).
## Round 2 (review issuecomment-6586971553, seat ruling
issuecomment-6587007982)
- **M1, no stored default.** The `is_default` column and the one-default
hook are gone. The default is the Console's existing pin
`LIST_PINNED_VIEW_PREFERENCE` (`list.view.pinned`, #27594,
`ui/packages/app-shell/src/utils/workspacePreference.ts`). **Finding:**
the pin can hold a personal view's id as it is. It stores `{"view":
<id>, "v": 1}` for any non-empty string, and `ObjectView` resolves it
with `resolveViewId(pinned, views.map(v => v.id), objectName)`, whose
first step is an exact id match. So it works once the Console half lists
personal views in `views`. A nanoid has no `.`, so the bare-key
qualification step (`<object>.<key>`) never turns it into a metadata
view's id. No second store.
- **M2, platform administrator.** `admin_full_access` keeps its
wildcard. Its generic GET, PATCH and DELETE of a member's view are
pinned as a recorded boundary (engine and REST). The body, changeset and
open question no longer say its delete is refused; only the door refuses
it.
- **S1.** The object's comment now says a wildcard with `viewAllRecords`
/ `modifyAllRecords` still reaches a private object (ADR-0066 D2).
- **S2.** External members are out of scope for this version:
`external_default` gets no grant. Open question for the Console half.
- **Nits.** An update that changes `user_id` is refused `403
PERMISSION_DENIED` and the owner is kept (pinned, engine and REST;
ablation 9). Pinning it over REST found that the row scope did not hold
it for an organization member (see *Two write rules*), so a save rule
holds it now. The same `created_by` floor applies to
`sys_user_preference`: by reading the source, its `user_id` is not
readonly and no hook guards it, so an organization member can likely
re-parent a preference row too. That was not driven over REST, and it is
reported as an out-of-scope finding, not changed here. A
`config.data.object` that differs from `object_name` is refused `400
VALIDATION_FAILED` on `config.data.object`, on create and on update
(pinned, engine and REST; ablation 8).
- Round 1's forced edit to `durability-swallow-repair.test.ts` is
reverted: with no personal-view `afterInsert` left, the SCIM-stamp fake
passes unmodified (22/22).
- Merging main brought #29007, which retires `titleFormat` on
code-shipped objects; `sys_personal_view` now names its title by
`nameField: 'label'` alone (the sweep in
`managed-api-method-affordance-sweep.test.ts` caught it).
- The route name was accepted; the spec roster line is recorded as
allowed; S3 (a shared own-row door) is the seat's follow-up card.
## Open questions
1. **The platform administrator's reach.** Through the generic data API,
`admin_full_access` reads, edits and deletes every member's personal
view, exactly as it does `sys_user_preference` (pinned as a recorded
boundary). Options: **A** keep it, and decide it with
`sys_user_preference` as one question; **B** close it for both
user-owned objects with a narrow engine-level exception, which needs the
set's pins amended. Recommendation: A for this card, because the rescue
path depends on the wildcard and the question is the same for both
objects.
2. **External members.** `external_default` grants nothing on
`sys_personal_view`, so an external member cannot save a view in this
version. Whether they should is a question for the Console half.
## Why
#28971 ruling C (comment 6570568131): a reader's named list views are
user data, a user-owned platform object modelled on
`sys_user_preference`, not a view metadata scope. Seat ruling 6583289013
put the owner's delete in this step. This PR is that server step; the
Console half follows on the card.
## Tests
- `packages/plugins/plugin-auth/src/personal-view-hooks.test.ts`: 27
tests on real ObjectQL + SQLite `SqlDriver` + `SecurityPlugin`.
- **Owner:** saves, lists and renames their own views. The id has no
`.`. An update handing a view to another user is refused 403, and the
owner is kept; a platform administrator, whom no row scope binds, is
refused by the save rule alone; an echo of the stored owner is accepted
(control).
- **Another member:** `findOne` returns null, `find` returns [], `count`
is 0, and an update answers `404 RECORD_NOT_FOUND`. A create naming
another owner is refused `403 PERMISSION_DENIED`.
- **Admins:** `organization_admin` and `organization_admin_no_bypass`
both see none of a member's views (404). An organization admin keeps
their own views (control). The recorded boundary: `admin_full_access`
reads, edits and deletes a member's view through the engine.
- **Body:** an invalid `config` is refused `VALIDATION_FAILED`, with
paths under `config`, on create and on update, and the stored body is
unchanged. Also covered: non-JSON text, and a rename without `config`.
- **The listed object:** a create whose `config.data.object` is another
object, an update moving only `object_name`, and an update sending only
a body for another object are each refused on `config.data.object`, with
the row unchanged. Control: changing both together is accepted.
- **The door's core** (`removeOwnPersonalView`): the owner's row is
removed and the owner's other views stay. Another member gets 404 and
the row stays. Both organization-admin sets get 404 and the row stays.
The platform admin gets 403 and the row stays. An unknown id gets 404.
The generic engine delete of the owner's own row is still 403.
- `packages/plugins/plugin-auth/src/personal-view-routes.test.ts`: 8
tests on the door's shell.
- It mounts exactly `DELETE /api/v1/personal-views/:id`.
- An unreadable permission store answers its own 503. Anonymous gets
401. A held sign-in gets its policy's 403. A query parameter gets 400.
No engine gets 503. An engine fault gets 500. In none of these is the
engine asked.
- On success, the removal runs as `{ isSystem, attributedUserId: caller,
tenantId: caller's org }`.
-
`packages/plugins/plugin-auth/src/personal-view-route-ledger.conformance.test.ts`:
3 tests. Every mounted route has a ledger row, and every row is mounted.
- `packages/qa/dogfood/test/personal-view-row-scope.dogfood.test.ts`: 16
tests through REST, on the showcase app booted with its default
security.
- **Owner:** saves, lists, reads and renames views, and cannot hand one
to another member (403).
- **Another member:** 404 / 404 / `RECORD_NOT_FOUND`, and the generic
delete answers `403 PERMISSION_DENIED`. A planted row naming another
owner gets 403.
- **Organization admin** (granted through `sys_user_permission_set`):
404.
- **Invalid body:** `400 VALIDATION_FAILED`; a body listing another
object: `400` on `config.data.object`.
- **The predicate guard:** a member whose field-level security hides
`showcase_project.budget` can save a view filtering on `budget`, because
a save does not re-judge field security. The list query `GET
/data/showcase_project?$filter={"budget":{"$gt":0}}` is then refused
403, naming `budget`.
- **`/meta/view`** does not serve it.
- **The door, `[door]`:** the owner gets 200 and the row is gone.
Another member gets 404 and the row stays. An organization admin gets
404 and the row stays. The platform admin's door delete gets 403, and
the row stays. Recorded boundary: the platform admin's generic GET,
PATCH and DELETE of a member's view succeed. Anonymous gets 401. The
generic `DELETE /data/sys_personal_view/<own id>` is still 403.
- **Forced test edits** (three; each is a list or a capture that the
change itself moves):
-
`packages/plugins/plugin-security/src/objects/default-row-scope-declarations.test.ts`:
the census of objects declaring `defaultRowScope` gains
`DECLARED_AT_BIRTH = { sys_personal_view }`. It is kept separate from
the ruling's 23 `DECLARERS`, which are unchanged. A new `it.each`
asserts the literal and that no shipped set carries a copy.
-
`packages/platform-objects/src/apps/translations/bundle-ownership.test.ts`:
`OWNED_OBJECTS` now names `sys_personal_view`. That is the guard's own
instruction when the extract config gains an object.
-
`packages/qa/dogfood/test/route-ledger-live-mount-parity.dogfood.test.ts`:
the gate's union of ledgers reads `PERSONAL_VIEW_ROUTE_LEDGER`, because
the door mounts on the `IHttpServer` port. Without it, "every mounted
route is ledgered" was red on the full battery: `DELETE
/api/v1/personal-views/:id`. The dogfood test task declares the imported
source as an input in `scripts/cross-package-test-inputs.mjs` and
`turbo.json`, the same as the storage, i18n and settings ledgers.
<details><summary>Ablations (each red, then restored, <code>git diff
HEAD</code> empty)</summary>
| Mutation | Red |
|---|---|
| Remove the `organization_admin` entry for `sys_personal_view` | The
`organization_admin` test (404 → row read).
`organization_admin_no_bypass` stays green, as expected: that variant
strips the bypass bits itself. |
| Remove `defaultRowScope` (platform-objects `dist/` rebuilt, preflight
confirmed absent) | 4 tests: member read, plant, both organization-admin
cases |
| Remove the `ListView` check (both hooks) | 3 validation tests |
| Round 2: let a `config.data.object` that differs from `object_name`
through | 3 listed-object tests; the both-together control stays green |
| Round 2: switch off the fixed-owner rule | The platform-administrator
re-parent test. Over REST, the head before the rule (ee23b8e) is the
red: the battery's `[owner] cannot hand their view` answered 200 |
| Force the predicate guard condition false in plugin-security (`dist/`
rebuilt) | The dogfood `[predicate]` test (200 instead of 403) |
| The door: drop the owner check (`row.user_id !== callerId`) | The
platform-admin case (the row was removed) |
| The door: read the row as the system instead of the caller | 3 tests:
another member and both organization-admin sets (403 instead of 404) |
| The door: `AuthPlugin` does not mount it (plugin-auth `dist/` rebuilt,
preflight confirmed the marker) | 5 dogfood `[door]` tests. The
generic-delete control stays green, as expected. |
The `/meta` pin is structural, because no code path serves the object as
metadata, so it has no mutation to ablate.
</details>
## Verification
Round 2, on head `deedb538` (main `81cd9eb0` merged; no conflicts):
- Suites under `os-verify-lock`: spec `platform-object-names.test.ts`
12/12; platform-objects 110 files, 1512 tests; plugin-auth 215 files,
3693 passed / 10 skipped; plugin-security 241 files, 5203 passed / 45
skipped; cli `organization-ownership-inventory.test.ts` 26/26. Typecheck
exit 0 for platform-objects, plugin-auth, plugin-security and cli.
- `pnpm lint` (root eslint, `--no-inline-config`) on every changed file:
exit 0. `check-i18n-bundles`: OK. `check:system-context-census`: OK (166
read sites). `check:doc-anchors`: OK.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands` listed 133
commands. All 133 exit 0 on `deedb538` (`check:type-check-debt` run
under `os-verify-lock` after the battery), reconciled with `--ran`: 133
derived, 133 run, 0 NOT-MEASURED.
- Dogfood battery: `pnpm --filter @objectstack/dogfood test` under
`os-verify-lock`, `VERDICT command-exit 0 · held the lock 1583s` (shared
box), Test Files 399 passed / 3 skipped (402), Tests 3296 passed / 28
skipped (3324), no failures. `check:type-check-debt` after it: exit 0.
- The battery on the earlier round-2 head `ee23b8ea` had exactly one
red, `[owner] cannot hand their view` (200, not 403). That was the
re-parent gap the fixed-owner rule closes. CI on `9f7adf09` and
`d4773b47` found the census row and the retired `titleFormat`; both are
fixed on this head.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmNjCFNPZLkGxdFdgAq)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent a9b959a commit 4008e62
32 files changed
Lines changed: 1931 additions & 34 deletions
File tree
- .changeset
- content/docs/permissions
- docs/audits
- packages
- cli/src/utils
- platform-objects
- scripts
- src
- apps/translations
- identity
- plugins
- plugin-auth/src
- plugin-security/src/objects
- qa/dogfood/test
- spec/src/system/constants
- scripts
| 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 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
| 146 | + | |
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
| |||
574 | 575 | | |
575 | 576 | | |
576 | 577 | | |
| 578 | + | |
577 | 579 | | |
578 | 580 | | |
579 | 581 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
135 | | - | |
| 135 | + | |
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
| |||
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
| 200 | + | |
201 | 201 | | |
202 | | - | |
203 | | - | |
| 202 | + | |
| 203 | + | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
| |||
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
| 220 | + | |
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
224 | | - | |
| 224 | + | |
225 | 225 | | |
226 | 226 | | |
227 | 227 | | |
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | | - | |
| 238 | + | |
239 | 239 | | |
240 | 240 | | |
241 | | - | |
| 241 | + | |
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
246 | | - | |
| 246 | + | |
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
252 | | - | |
| 252 | + | |
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
260 | | - | |
| 260 | + | |
261 | 261 | | |
262 | 262 | | |
263 | 263 | | |
| |||
307 | 307 | | |
308 | 308 | | |
309 | 309 | | |
310 | | - | |
| 310 | + | |
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
318 | 318 | | |
319 | 319 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | | - | |
| 38 | + | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
| |||
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
| 169 | + | |
169 | 170 | | |
170 | 171 | | |
171 | 172 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
795 | 795 | | |
796 | 796 | | |
797 | 797 | | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
798 | 802 | | |
799 | 803 | | |
800 | 804 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
130 | 131 | | |
131 | 132 | | |
132 | 133 | | |
| |||
268 | 269 | | |
269 | 270 | | |
270 | 271 | | |
| 272 | + | |
271 | 273 | | |
272 | 274 | | |
273 | 275 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1414 | 1414 | | |
1415 | 1415 | | |
1416 | 1416 | | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
1417 | 1449 | | |
1418 | 1450 | | |
1419 | 1451 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1414 | 1414 | | |
1415 | 1415 | | |
1416 | 1416 | | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
1417 | 1449 | | |
1418 | 1450 | | |
1419 | 1451 | | |
| |||
0 commit comments