Repository navigation
Commit 4d278c9
feat(spec): a strict response contract for GET /auth/me/permissions, and hasAnyQueue on IOwnershipQueueService (#28704)
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XRMukpcDXWQXQ4GaVYCkErv)_
Fixes #28508
Clause-②: yes (widening)
Metadata-protocol: added
## 维护者速读
- **改了什么**:`GET /auth/me/permissions` 第一次有了 spec 里的严格响应契约
`MePermissionsResponseSchema`(`@objectstack/spec/api`),覆盖匿名、降级(无
SecurityPlugin)、完整、故障关闭四种响应体,声明了 `hasAnyQueue:
boolean`,未声明的同级键直接拒绝。`IOwnershipQueueService` 契约加上可选方法
`hasAnyQueue?(organizationId)`,路由改为通过契约读取,删掉本地的 `AnyQueueReader`
可选类型。控制台 `@object-ui/permissions` 的 `MePermissionsResponse` 改为从 spec
派生,不再手写。
- **为什么改**:#28508 的 spec 一半(服务端一半已由 #28654
合入)。控制台每个会话读一次这个响应来驱动整个权限层,之前只有 ui
里手写的接口,服务端多发或少发一个键都没人拦。手写接口已经漂移:它读一个服务端从不发送的 `roles` 键(服务端的键是
`positions`),所以 `MePermissionsProvider` 下的 `usePermissions().roles`
一直是空数组。
- **风险与回滚**:线上响应字节不变。`hasAnyQueue?` 是可选方法(与 `declaredQueueNamed?`
一样),仓库外的旧实现照样满足契约,路由对缺这个方法的实现答
`false`。控制台行为只有一处变化:`usePermissions().roles` 从恒为空变为服务端的
`positions`(仓库内没有生产代码读它)。`@object-ui/permissions` 导出的
`MePermissionsResponse` 变为 spec 的联合类型,按它写的测试夹具随之迁移(`roles` →
`positions`,加 `hasAnyQueue`)。回滚即 revert 本 PR。
- **先看**:`packages/spec/src/api/auth.zod.ts`(三个
schema)、`packages/spec/src/contracts/ownership-queue-service.ts`(新方法)、`packages/spec/src/api/me-permissions-response.test.ts`(契约测试
12 例)、`ui/packages/permissions/src/MePermissionsProvider.tsx`(派生类型与
`roles`)。
- **两分钟验证**:`pnpm --filter @objectstack/spec exec vitest run
src/api/me-permissions-response.test.ts`,预期 12 passed;`pnpm --filter
@objectstack/plugin-hono-server exec vitest run
src/current-user-endpoints-has-any-queue.test.ts`,预期 9
passed(每个真实响应体都过严格 schema)。
- **需要你做的**:无。contract review owed:入队前由席位按 `CONTRACT_REVIEW_TIER` 给出 `##
Contract review` PASS(567e3dc8 上的首轮为 FAIL,本版逐条修复,待增量复审)。
## What changes
- **`MePermissionsResponseSchema`**
(`packages/spec/src/api/auth.zod.ts`), a
`z.discriminatedUnion('authenticated', …)` of two strict branches:
- `MePermissionsAnonymousResponseSchema`: `{ authenticated: false }` and
nothing else, so nothing about a user can ride on the anonymous body.
- `MePermissionsAuthenticatedResponseSchema`: `userId`, `tenantId`
(nullable), `positions`, `permissionSets`, `objects` (record of
`EffectiveObjectPermissionSchema`), `fields` (record of a strict `{
readable, editable }`), `systemPermissions` and `tabPermissions` (both
optional: absent only on the degraded body, where absent means unknown,
and present-and-empty means none), and **`hasAnyQueue: boolean`**
(required on every authenticated body).
- Types `MePermissionsResponse`, `MePermissionsAnonymousResponse`,
`MePermissionsAuthenticatedResponse` (`z.input`). The three schemas are
isomorphic, so they take ADR-0122 `Iso_` pins in
`type-alias-convention.pin.test.ts` (count 808 → 811) rather than
`Parsed` aliases.
- **`IOwnershipQueueService.hasAnyQueue?(organizationId: string | null):
Promise<boolean>`**
(`packages/spec/src/contracts/ownership-queue-service.ts`): `true`
exactly when some name would resolve through `resolveQueue`.
**Optional**, like `declaredQueueNamed?`, so an implementation written
before it still satisfies the contract; the route answers `false`
without it. Its docblock names `/auth/me/permissions` as the one carrier
and says it discloses no name and no count. plugin-sharing's
`OwnershipQueueService` already implements it (#28654); only its
docblock changes.
- **`@objectstack/plugin-hono-server`** (`current-user-endpoints.ts`):
the route reads the method as `Pick<IOwnershipQueueService,
'hasAnyQueue'>` through the kernel locator, keeping the runtime `typeof`
guard so an older implementation still answers `false`.
`mePermissionsBody` (the full and closed bodies) returns
`MePermissionsAuthenticatedResponse`, so tsc refuses a key the schema
does not declare. The degraded literal and the anonymous `{
authenticated: false }` stay bare `c.json({...})` literals: an inline
`satisfies` hid both from the `check:route-envelope` census (found 7,
pinned 9), so they are bound by the strict parse in the route and door
tests instead. The wire is byte-for-byte unchanged.
- **`@object-ui/permissions`**
(`ui/packages/permissions/src/MePermissionsProvider.tsx`):
`MePermissionsResponse` is re-exported from `@objectstack/spec/api`
(with `MePermissionsAuthenticatedResponse` beside it) in place of the
hand-written interface `check:spec-symbols` flagged. The provider reads
grants only from the authenticated branch. The interface's `roles` key
was drift (the server sends `positions`), so the context's `roles` now
carries `positions`. `ui/apps/console/src/components/studioEntry.ts`
narrows on `authenticated` before reading `systemPermissions`. Test
fixtures across 13 UI packages typed as the response move `roles: [...]`
→ `positions: [...]` and add `hasAnyQueue: false`; builders that answer
`authenticated: true` are typed as the authenticated branch; three `{
readable: false }` field entries gain `editable: false` (the strict
entry declares both); the anonymous fixtures are `{ authenticated: false
}`.
- **Skill index.** An `objectstack-api` row in `TRANSITIVE_ALLOWLIST`
(`packages/spec/scripts/lib/skill-map-guards.ts`) lists the 13
transitive pointers the index already published, so the new
`auth.zod.ts` → `security/permission.zod.ts` edge no longer drags 22
more into `skills/objectstack-api/references/_index.md` (the file is now
identical to main's).
- **Variant-doc ledger.** The `authenticated:false|true` union is
declared `not-authorable` in `packages/spec/variant-docs.json`.
- **Generated artifacts** regenerated with `pnpm --filter
@objectstack/spec check:generated --fix`: api-surface, export-origins,
declaration-map, authorable-surface and json-schema manifests, the
reference docs (`content/docs/references/api/auth.mdx`, `index.mdx` 1582
→ 1585) and the strictness ledger counts (472 → 475).
- **Changeset** `.changeset/28508-me-permissions-response-contract.md`:
**`minor` for `@objectstack/spec`** (Metadata-protocol `added`: the
authorable-surface baseline moves, per the precedent of #28440 / #28447
/ #28595), `patch` for `@objectstack/plugin-hono-server`,
`@objectstack/plugin-sharing` and `@object-ui/permissions`, with the
`roles` → `positions` mapping for a fixture author.
## Tests
- **Contract** (`packages/spec/src/api/me-permissions-response.test.ts`,
new, 12 cases): the anonymous, full, closed and degraded bodies parse;
`hasAnyQueue` is declared, must be a boolean (`'yes'`, `2` refused at
path `hasAnyQueue`) and is required; an unknown sibling on the
authenticated body is refused with `unrecognized_keys` naming the key,
not dropped; an unknown sibling on the anonymous body is refused too
(`hasAnyQueue`, `userId`); a field entry is closed; absent and empty
`systemPermissions` both parse and stay distinct; a tab visibility
outside the four is refused at `tabPermissions.sales`; a non-boolean
discriminator matches no branch.
- **Route**
(`plugin-hono-server/src/current-user-endpoints-has-any-queue.test.ts`):
every body the handler answers in the 9 cases (full, degraded, closed,
anonymous, service absent / without the method / throwing) is now also
parsed through `MePermissionsResponseSchema` and must have no issues.
- **HTTP door** (`verify/src/me-permissions-has-any-queue.test.ts`, 5
cases): the raw body each real boot answers is parsed through
`MePermissionsResponseSchema` and must have no issues, before
`hasAnyQueue` is read.
<details><summary>Red first, green after, ablations</summary>
- **Red on main.** The contract test against `origin/main`'s
`auth.zod.ts`: every case red (the three schemas do not exist).
- **Green.** Contract 12/12, route 9/9, door 5/5.
- **Ablation A, strictness dropped** (`.strict()` → `.passthrough()` on
the authenticated branch): the contract case "an unknown sibling is
refused on the authenticated body" goes red, 1 failed / 11 passed.
Restored: blob == HEAD.
- **Ablation C, the route sends an undeclared key**
(`ablation-replace.mjs` adds `ablation28508ExtraKey` to
`mePermissionsBody`; `ablation-dist-preflight` finds the marker in
plugin-hono-server `dist/index.js` and `dist/index.mjs`): route test 7
failed / 2 passed (the degraded and anonymous cases do not go through
`mePermissionsBody`, by design), door test 5 failed (5). Restored: blob
== HEAD, rebuilt, marker absent. This is the reading that the route and
door tests are bound to the strict schema.
- **Ablation B, not measured.** Renaming `hasAnyQueue` in the spec
schema was refused by the spec build itself (the `gen:schema` baseline
gate refuses a key removal), so dist stayed at HEAD and no test reading
dist could see it. Ablation A covers the contract side and C the
consumer side.
</details>
## Acceptance notes
- **Acceptance 1 (declared strictly, unknown sibling refused)**: met by
the strict authenticated and anonymous branches and pinned by the
contract test. `EffectiveObjectPermissionSchema` (the value type of
`objects`) is the existing `security/permission.zod` schema and keeps
its own tolerance; this PR makes the top level and the `fields` entries
strict and does not re-shape the shared permission schema.
- **Contract method optional** (contract review on `567e3dc8`, must-fix
2): `hasAnyQueue?` like `declaredQueueNamed?`; a required member would
have been an undeclared break for an out-of-tree implementer.
- **No client method.** A typed `auth.mePermissions()` in
`@objectstack/client` was tried and dropped: `client-url-conformance`
requires every SDK URL to match an exact route-ledger entry, and
`/auth/me/permissions` sits in no ledger (it matched only the `*
/auth/**` wildcard). The seat files the typed client method as its own
card.
- **Metadata-protocol `added`, `Clause-②: yes (widening)`** (contract
review must-fix 1): the PR moves
`packages/spec/authorable-surface/api.json` (11 new schema keys), and
`check-changeset` reads a moved baseline under `none` as
`none-baseline-moved`. The kind follows the baseline, as in #28440 /
#28447 / #28595, so `@objectstack/spec` ships `minor`.
- **Console reads the typed contract.** `@object-ui/permissions` now
derives its response type from the spec, so a key the server adds or
drops shows up in the console's typecheck. The toolbar consumer of
`hasAnyQueue` (#28428) is not wired here.
## Verification
All readings on head `0eb64410` (the final commit), on main `3944d2e1`.
The first round (head `567e3dc8`, whole-workspace build 118/118) is in
the PR history; this round changes the spec contract, the changeset and
`ui/`, so everything below was re-measured after rebuilding the spec,
plugin-sharing, plugin-hono-server and `@object-ui/permissions`.
- **Contract review fixes.** Must-fix 1: changeset and body declare
`Clause-②: yes (widening)` / `Metadata-protocol: added`,
`@objectstack/spec` `minor`; `node scripts/check-changeset-no-major.mjs
--base origin/main` with `GITHUB_EVENT_PATH` set to a pull-request event
carrying this body: exit 0. Red first: the same command with
`567e3dc8`'s changeset and the old `none` body exits 1 with CI's refusal
("metadata-protocol declares `none` while an authorable-surface or
authorable-defaults baseline moved · Moved:
packages/spec/authorable-surface/api.json"). Must-fix 2: `hasAnyQueue?`
optional. Should-fix (a): `skills/objectstack-api/references/_index.md`
is identical to main's (`git diff origin/main` empty). Should-fix (b):
the doc cross-reference is in `auth.zod.ts`.
- **UI.** `pnpm --dir ui run check:spec-symbols`: exit 0 ("0 untriaged
collisions"). Typecheck (`tsc --noEmit` and `tsc -p tsconfig.test.json`)
of `@object-ui/permissions`, plugin-kanban, plugin-calendar,
plugin-detail, plugin-gantt, plugin-form, plugin-view, plugin-list,
plugin-grid, fields, plugin-report and app-shell, plus `apps/console`
`tsc --noEmit`: 0 errors each. Tests: `@object-ui/permissions` 15 files,
166/166; every UI test file that names the response type, the provider
or the route (143 files across those packages): Test Files 143 passed
(143), Tests 1761 passed (1761). eslint on the provider, the package
entry and `studioEntry.ts`: exit 0.
- **Package suites.** `@objectstack/spec`: 23185 passed, 1 todo.
`@objectstack/plugin-hono-server`: 466/466.
`@objectstack/plugin-sharing`: 1302/1302. Door test: 5/5. VERDICT
command-exit 0.
- **Typecheck.** `pnpm --filter @objectstack/spec --filter
@objectstack/plugin-sharing --filter @objectstack/plugin-hono-server
--filter @objectstack/verify typecheck`: exit 0.
- **Generated artifacts.** `pnpm --filter @objectstack/spec
check:generated`: exit 0 after `--fix` regenerated `gen:skill-refs` for
the allowlist row.
- **Derived gates.** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`: 115 commands, **all 115 exit 0 on
`0eb64410`**; `--ran`: "115 derived famil(ies) accounted for — 115 run,
0 NOT-MEASURED".
- **Not local.** `test:repo`, the full Test Core shards and the full UI
suite are CI's. No full dogfood battery is owed: no `!` in the title and
no `(narrowing)` arm.
<details><summary>Gate record (115 lines, COMMAND :: exit N)</summary>
```text
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:authz-resolver :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-stale-fill :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:route-envelope :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5kcJADmewNSX53U3gf)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 272fec0 commit 4d278c9
143 files changed
Lines changed: 1018 additions & 468 deletions
File tree
- .changeset
- content/docs/references
- api
- docs/audits/2026-07-unknown-key-strictness-ledger.counts
- packages
- plugins
- plugin-hono-server/src
- plugin-sharing/src
- spec
- api-surface
- authorable-surface
- declaration-map
- export-origins
- json-schema.manifest
- scripts/lib
- src
- api
- contracts
- verify/src
- ui
- apps/console/src
- __tests__
- components
- pages
- settings/__tests__
- system
- services
- packages
- app-shell/src
- console
- __tests__
- ai/__tests__
- home/__tests__
- hooks/__tests__
- layout/__tests__
- providers/__tests__
- views
- __tests__
- metadata-admin
- studio-design
- fields/src/widgets
- permissions/src
- __tests__
- plugin-calendar/src
- plugin-detail/src
- __tests__
- renderers/__tests__
- plugin-form/src
- plugin-gantt/src
- plugin-grid/src
- __tests__
- hooks
- plugin-kanban/src
- plugin-list/src/__tests__
- plugin-report/src/__tests__
- plugin-view/src/__tests__
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | | - | |
| 21 | + | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 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 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
71 | 190 | | |
72 | 191 | | |
73 | 192 | | |
| |||
0 commit comments