Repository navigation
Commit 217ba78
feat(spec): an api trigger declares a third-party sender's signature scheme (config.signature), keyed by secretRef (#29995)
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwB3Ps3dJobVVkjf2zYiwJKE7)_
Part of #29983 — the contract half. The card's need (an `api` hook
admits a DocuSign Connect post) is met only once the `api` trigger
verifies the declaration; that is the `domain:server` slice listed
below, which the seat files after this lands. Merging this PR must leave
the card open.
## 维护者速读
- **改了什么:** `api` 触发流的开始节点可以声明
`config.signature`:第三方发送方的签名方式(头名、HMAC-SHA256、hex/base64、签的是原始 body 还是
`T.RAW_BODY`、时间戳在头里的哪个元素),密钥沿用同一节点的 `config.secretRef`。声明后它**取代**该 hook
的 `x-objectstack-signature` 校验,每个 hook 只有一个校验器。`ITriggerApiService`
合约文字写明实现方义务。只动 `@objectstack/spec`(minor),不动服务端。
- **为什么改:** hotclm 的电子签名步骤要接 DocuSign Connect
回调(`X-DocuSign-Signature-1`,raw body 的 base64
HMAC-SHA256),平台只认自己的签名,只能轮询。triage 定向为 api 触发器上的声明式校验器,合约先行。
- **风险与回滚:** 鉴权边界。服务端未实现前,声明了 `signature` 的 hook 仍只认
`x-objectstack-signature`,厂商的每次回调都 401,不会放行未验证请求(fail closed)。body-only
方案(DocuSign)没有防重放窗口,合约照实写明,不另造。回滚 = revert 本 PR。
- **先看:**
`packages/spec/src/automation/api-trigger-signature.zod.ts`(形状、六类拒绝含保留头名、`apiTriggerVerifierOf`
三态);`packages/spec/src/contracts/trigger-api-service.ts` 新增的「declared
scheme REPLACES that check」一条(常数时间比较、300 秒窗口、单一 401)。
- **两分钟验证:** `pnpm --filter @objectstack/spec exec vitest run
src/automation/api-trigger-signature.test.ts`,预期 `26 passed`。
- **需要你做的:** 请审阅这份鉴权边界合约(形状、密钥只走 `secretRef`、body-only
无重放窗口这三点),通过后由席位落地并派服务端卡。
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — skills/objectstack-automation/SKILL.md
(the "outside the `api` door's HMAC" line),
skills/objectstack-automation/references/integration-recipes.md (recipe
4, "cannot post here today"), skills/objectstack-api/SKILL.md (the "the
hook checks only `x-objectstack-signature`" case), and a
`config.signature` section in content/docs/automation/flows.mdx beside
its `secretRef` paragraph. The three skills lines say correctly today
that a vendor-signed callback cannot be admitted and stay correct until
the server slice serves the key, so that slice teaches them (and binds
the new `signs` union in `variant-docs.json` to the flows.mdx section),
not this PR.
Prior rulings read: api trigger signature,secretref,invalid_signature →
0 hits; none; thread: none; repo: objectstack-ai/objectstack. Widened
set: signature,hmac,webhook,inbound hook,replay → ADR-0138 D7 (and its
F1), ADR-0041 Tier 1, read; none reversed (see Acceptance notes).
ADR-0087: not-required (no-migration-prescription), in the changeset.
## What changes
**The shape**
(`packages/spec/src/automation/api-trigger-signature.zod.ts`, exported
from `@objectstack/spec/automation`). An `api` flow's start node
`config` gains one optional key, `signature`: a discriminated union on
`signs`, both arms closed (`strictObject`).
DocuSign Connect, base64 HMAC-SHA256 of the raw body:
```ts
config: {
hookId: 'docusign',
secretRef: 'DOCUSIGN_CONNECT_KEY', // the key, by name: one key per hook, never inline
signature: {
signs: 'body',
header: 'X-DocuSign-Signature-1',
algorithm: 'hmac-sha256',
encoding: 'base64',
},
}
```
Stripe-style, `Stripe-Signature: t=1700000000,v1=HEX` where HEX is the
HMAC-SHA256 of `1700000000.RAW_BODY`:
```ts
config: {
hookId: 'payments',
secretRef: 'PAYMENTS_WEBHOOK_KEY',
signature: {
signs: 'timestamp.body',
header: 'Stripe-Signature',
algorithm: 'hmac-sha256',
encoding: 'hex',
timestampElement: 't', // where the timestamp comes from
digestElement: 'v1', // the element(s) carrying the digest
},
}
```
GitHub-style costs one optional key on the body arm: `{ signs: 'body',
header: 'X-Hub-Signature-256', algorithm: 'hmac-sha256', encoding:
'hex', prefix: 'sha256=' }`.
| Key | Arm | Values |
|:--|:--|:--|
| `signs` | both (the discriminator) | `body` or `timestamp.body` |
| `header` | both | an RFC 9110 field name, matched without regard to
case; never a reserved name (`API_TRIGGER_SIGNATURE_RESERVED_HEADERS`,
below) |
| `algorithm` | both | `hmac-sha256` (closed enum, one value) |
| `encoding` | both | `hex` or `base64` (RFC 4648 standard alphabet) |
| `prefix` | `body` only, optional | visible ASCII, no space, no comma |
| `timestampElement`, `digestElement` | `timestamp.body` only, required
| a `NAME` of the header's comma-separated `NAME=value` elements |
**The key.** A declared scheme verifies under the key the same start
node's `config.secretRef` names, resolved exactly as it is today (the
engine's `hookSecretResolver` reads `secretRef` through the credential
resolver at every post, never the write-only channel). Measured against
the current shape, "conflicting" means two sources for the hook's one
key: a literal `config.secret` (inline, and a second source) beside
`signature` is refused, and so is a `signature` with no `secretRef`. The
write-only channel cannot conflict, because `secretRef` already wins
over it at verification.
**One verifier per hook.** `apiTriggerVerifierOf(config)` is the one
reader the engine, trigger and lint share. It answers
`objectstack-signature` (no `signature` key: today's behaviour,
untouched), `declared` (the parsed scheme plus the `secretRef` name), or
`unusable` (a `signature` that is present and refused). It decides on
the key's presence, never on whether it parses, so a broken declaration
cannot fall back to the platform check.
**Refused at the flow parse** (`FlowSchema`'s `superRefine`, beside the
`secretRef` arm, each anchored at `nodes.N.config.signature...`): no key
reference; a literal `secret` beside it (the cleared `''` form
included); an algorithm other than `hmac-sha256`; an encoding other than
`hex`/`base64`; a `signs` that is neither form; `timestamp.body` with no
`timestampElement` (no timestamp source) or no `digestElement`, or the
same name for both; a body-only scheme naming a timestamp or digest
element; a `prefix` on the timestamped arm; a header that is not a field
name; a `header` naming, in any case, `x-objectstack-signature` (the
platform's own header, which stays the platform's: one verifier per
hook, and a body-only scheme on it would re-admit the `sha256=` form
ruling B on #22805 refuses inbound, with no replay bound) or another
name in the closed `API_TRIGGER_SIGNATURE_RESERVED_HEADERS` list
(`authorization`, `proxy-authorization`, `cookie`, `host`,
`content-length`, `transfer-encoding`, `x-idempotency-key`: the
request's credentials, routing or framing, or the hook's delivery id),
refused by the shape itself (a second `pattern` on `header`, built from
the list with each letter in both cases, so each arm schema and the
published JSON Schema refuse it too); a non-object value; and a
`signature` on a flow that binds no `api` trigger.
**The contract text**
(`packages/spec/src/contracts/trigger-api-service.ts`,
`handleInboundHook`). A new item states what the implementer owes for a
declared scheme: it replaces `x-objectstack-signature` (never read on
such a hook, so a valid one admits nothing; a post without the declared
header is refused); the header parse for each arm, a field the sender
repeats read as ONE value (the values joined with `, ` in order, what
`Headers.get` answers), never the first or last field alone; the HMAC
over the raw body as received, or over `T.RAW_BODY` with `T` exactly as
received; decode by `encoding` and compare the 32 bytes **in constant
time**, any one digest element matching; for `timestamp.body`, the same
300-second window as `x-objectstack-signature`
(`HTTP_SIGNATURE_TOLERANCE_SECONDS` in `@objectstack/core`), never one
of its own; for `body`, plainly **no replay window** (the sender offers
nothing to bound one with, and the contract adds none); and every
failure keeps the one `401 INVALID_SIGNATURE` with one body, an
`unusable` declaration included (whether the trigger declines to arm it
or refuses each post). It also states the planned state honestly: a
trigger that predates this form verifies `x-objectstack-signature`
alone, so it refuses every post a declared sender makes.
**Ripples.** Type aliases `ApiTriggerSignature`,
`ApiTriggerBodySignature`, `ApiTriggerTimestampedSignature` with three
ADR-0122 isomorphism pins (828 to 831);
`API_TRIGGER_SIGNATURE_RESERVED_HEADERS` exported (frozen, pinned
whole), and `header`'s JSON Schema gains the reserved-name `pattern` (an
`allOf` of two patterns); the two arms are exported as their own defs so
`authorable-surface/automation.json` records their keys (a union def has
no top-level `properties`, so without them the surface would not move);
`api-surface`, `export-origins`, `declaration-map`,
`json-schema.manifest`, the references pages and the strictness-ledger
counts regenerated, plus one hand-written strictness-ledger triage row
(`authorable`, strict from birth);
`skills/objectstack-automation/references/_index.md` is the
`gen:skill-refs` line for the new module (the generator-owned carve-out,
ruling on #11705); `packages/spec/llms.txt` counts the module (218
schemas, automation 16); `packages/spec/variant-docs.json` declares the
new `signs: body | timestamp.body` union `exempt:
generated-reference-only` until the server slice writes the flows.mdx
section and binds it. No server package is touched.
## Why
Card #29983, from the hotclm app: its e-signature step needs DocuSign
Connect's envelope-status callback, which signs the raw body with
`X-DocuSign-Signature-1` (base64 HMAC-SHA256 under a Connect key). The
`api` hook admits a post only by `x-objectstack-signature`, so the
callback is refused `401 INVALID_SIGNATURE` and the app polls instead.
Triage 6617546776 set the direction (a declarative verifier on the `api`
trigger, contract first, `domain:spec`) and the floor (single verifier,
constant-time compare, a replay bound where the sender offers one, never
an inline key, the maintainer reviews the contract).
## Server slice (`domain:server`, filed by the seat once this lands)
- [ ] `packages/triggers/trigger-api`: read
`apiTriggerVerifierOf(binding.config)` at `start()`. `unusable` never
arms a hook that admits a post and never falls back to
`x-objectstack-signature`; whether the trigger declines to arm it or
refuses each post, every post to it gets the one `401
INVALID_SIGNATURE`.
- [ ] `serveInboundHook` / `handleRequest`: pass the declared header's
value (today only `x-objectstack-signature` and `x-idempotency-key`
reach `handleRequest`), on both faces (the self-hosted mount and
`handleInboundHook`), read with `Headers.get` so a repeated field
arrives as its one comma-joined value, never the first field alone.
- [ ] The verifier per the contract text: header parse per arm
(timestamp element once, decimal as `x-objectstack-signature` writes
`t`; digest elements one or more; unknown elements ignored); HMAC-SHA256
over the exact body bytes (`request.text()` re-encodes valid UTF-8 byte
for byte; a non-UTF-8 body mismatches and fails closed); decode
hex/base64 to 32 bytes; `timingSafeEqual`; the window from core's
`HTTP_SIGNATURE_TOLERANCE_SECONDS`.
- [ ] Every failure through `refuseUnverified` with its own logged cause
(header absent, malformed, prefix missing, no digest, mismatch, outside
window, declaration unusable) and the one `401 INVALID_SIGNATURE`
answer.
- [ ] Pins: a DocuSign vector (real base64 HMAC) admitted; a
Stripe-style vector admitted, with a second `v1` element (key roll) and
an outside-window `t` refused; a valid `x-objectstack-signature` on a
declared hook refused; a post without the declared header refused; a
body-only header sent twice refused; every failure's status and body
identical; an `unusable` declaration answers that same `401` and admits
nothing.
- [ ] Runtime `HttpDispatcher`: confirm the forwarded `Request` keeps
the vendor header (one pin).
- [ ] `validate-flow-trigger-readiness` (required): a hook with
`secretRef` and `signature` does not grade ready while no trigger serves
the key; once the trigger serves it, the grade names the verifier the
hook has (`apiTriggerVerifierOf`), and an `unusable` declaration never
grades ready.
- [ ] Skills and docs from the `Skills-docs:` line above, and the
`variant-docs.json` exemption replaced by a binding to the new flows.mdx
section.
## Tests
- `packages/spec/src/automation/api-trigger-signature.test.ts` (new, 26
cases): the three senders parse whole and are served as written; the
reader answers one verifier per hook (absent: `objectstack-signature`;
usable: `declared` with the `secretRef`; eight present-and-refused
shapes: `unusable`, never `objectstack-signature`); no key can ask for a
second check or a fallback (`optional`, `fallback`, `also` refused on
both arms); the replay rule's two halves (`timestamp.body` without a
source refused at `timestampElement`; a body-only scheme naming one
refused); each parse refusal at its path with `code: 'custom'`; and the
reserved headers (`x-objectstack-signature` in three spellings on both
arms, the seat's `sha256=` shape included, refused at `header` and read
`unusable`, the message leading with the reason both arms share and
naming no issue id; the closed list pinned whole and frozen, each other
name refused in three spellings on both arms; each arm schema alone and
the union refusing every reserved name; near names such as
`X-Objectstack-Signature-1` still parse). Ablated (the reserved-name
pattern removed), the three reserved-header pins go red.
- `packages/spec/src/type-alias-convention.pin.test.ts`: three
`Iso_automation_apiTriggerSignature__*` pins, count 831.
## Acceptance notes
- **The connector-level alternative, measured and not taken.** A
connector-owned inbound webhook would revive a family
`@objectstack/spec` 17 retired: the connector `triggers` array with
`type: 'webhook'` (ADR-0049, `connector-triggers-retirement.test.ts`),
whose ruling keeps connector-event triggers in ADR-0041's third tier as
their own trigger package, with subscribe/unsubscribe, signature
verification and a dedupe cursor. It would also need a second anonymous
route on the ADR-0069 allow-list (ruling A admits exactly one), its own
queue ingest, its own 401 policy and replay window. `connector.zod.ts`
already directs an external event to "an `api` flow". The `api` trigger
already owns the route, the allow-list row, queue ingest, the one 401
and the window, so the verifier belongs on it; I did not judge the
alternative clearly better, so I built the brief.
- **Liveness: no `planned` + `authorWarn` row is reachable, measured.**
`flow.nodes.config` is `z.record(z.string(), z.unknown())`;
`check-liveness.mts#childShape` answers a record by its value shape,
which is not an object here, so no row can sit under `config` (a
`children` row there would be an orphan). The sibling `secretRef` has no
row for the same reason. What holds meanwhile: the describe text and the
contract say a trigger that does not implement the form refuses every
post `401`, and the current trigger does exactly that (it reads only
`x-objectstack-signature`), so the key fails closed, never open. A lint
warning until the slice lands would be the server card's to add.
- **`metadata-form` reconciliation: not offered in a form.** The `api`
start node's settings are edited in the Studio flow inspector
(`FlowNodeInspector`, which offers the literal `secret`), not in a
`platform-objects` metadata form, so the reconciliation pin does not
move. Offering `secretRef` and `signature` there is a `domain:ui`
follow-up, not in this card.
- **ADR-0138 D7 / F1.** D7 kept the webhook signature vocabulary out of
`ApiEndpointSchema` and named the choice between endpoints gaining it
and webhooks keeping the signed inbound-hook channel as a follow-up
filed with its executor. This places it on the inbound-hook channel; the
executor is the server slice above, filed by the seat right after this
lands, and the key fails closed until then.
- **Ruling B on #22805** (the body-only `sha256=` form of
`x-objectstack-signature` is refused inbound because it signs no time)
is unchanged: it governs the platform's own header. A declared body-only
scheme is the author stating what a vendor that offers nothing else
signs, and the contract states its missing replay window. Since contract
review round 1, a declaration cannot re-admit that form on the
platform's own header: `header: 'x-objectstack-signature'` is refused in
any case.
- `prefix` lives on the body arm only; the timestamped arm locates its
digest by `digestElement` (`v1`), which is also what lets a sender roll
its key (two `v1` elements).
- Security: nothing filed. The read of today's verifier found no
weakness to report.
## Verification
Head `bb371a5c3103db11490cb2cc4c39c378b588a755`, merge base `9049be26a`.
`main` has moved four commits since (#29977, #29989, #29927 and a qa fix
at `0b99c2b1`); none touches a file this PR changes (`git diff
--name-only HEAD...origin/main` against this branch's files: no
overlap), so the head is not merged with it.
Every reading below is on `bb371a5c`, after `pnpm --filter
@objectstack/spec build`:
- `pnpm --filter @objectstack/spec exec vitest run
src/automation/api-trigger-signature.test.ts`: 26 passed.
- `pnpm --filter @objectstack/spec test` (under `os-verify-lock`): 809
files passed, 24303 tests passed, 1 todo, exit 0.
- `pnpm --filter @objectstack/spec typecheck`: exit 0
(`check:test-typecheck` OK, debt unchanged at 131 pinned signatures).
- `node scripts/check-doc-authoring.mjs`: exit 0 (no internal issue id
in customer-facing spec text; the generated references page carries
none).
- `pnpm --filter @objectstack/spec run check:generated` after `--fix`:
exit 0; the references page and the published JSON Schema carry the
reserved-name `pattern` (`header`: `allOf` of the field-name pattern and
the reserved-name lookahead).
- eslint over the changed `.ts` files: exit 0.
- `GITHUB_EVENT_NAME=pull_request node
scripts/check-changeset-no-major.mjs --base origin/main --event` (this
title and body): exit 0, level axis `yes (widening)` / `added`.
- Ablation: with the reserved-name pattern removed from `header`, three
pins go red (`x-objectstack-signature` in any case on both arms, the
other reserved names on both arms, each arm schema and the union
refusing every reserved name), 23 stay green; restored, 26 pass.
- `pnpm --filter @objectstack/spec test:repo`: 89 files, 1524 tests
passed, exit 0.
<details><summary>Derived gates on <code>bb371a5c</code>: 107 derived,
107 run, 0 NOT-MEASURED (<code>dispatch-gates --ran</code> exit
0)</summary>
Every command `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives for this diff (107, the same set as
at `db0c0f86`), run once each on `bb371a5c` after a fresh spec build,
with `@objectstack/formula` and `@objectstack/lint` built and the
teardown self-test's positive-control commit fetched: all 107 exit 0.
Reconciliation: `107 derived famil(ies) accounted for — 107 run, 0
NOT-MEASURED`. Earlier rounds, for the record: at `db0c0f86` the first
sweep found two real reds (`check:llms-txt` inventory counts,
`check:variant-docs` the new union undeclared), fixed in that commit; CI
on `ce07e1f6` found the doc authoring guard red (an issue id in a
refusal message), fixed in `4194519e`.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic (a
parse refusal walk and a reader, the same kind
`api-trigger-secret-ref.zod.ts` carries; the verifier is the server
slice's).
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes (eslint over the changed
files, in Verification above; the repo-wide run is CI's).
- [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 (`check:generated`: all 14 up to date).
- [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 (the `维护者速读` section is
the Chinese summary for the maintainer).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzggC8errMtPRYBUmoY
Body refreshed by spec seat 3 (fix rounds 1 and 2, final readings on
bb371a5): 2026-11-04T02:21Z
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 46d2b65 commit 217ba78
23 files changed
Lines changed: 1102 additions & 19 deletions
File tree
- .changeset
- content/docs/references
- automation
- docs/audits
- 2026-07-unknown-key-strictness-ledger.counts
- packages/spec
- api-surface
- authorable-surface
- declaration-map
- export-origins
- json-schema.manifest
- src
- automation
- contracts
- skills/objectstack-automation/references
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
29 | 33 | | |
30 | 34 | | |
31 | 35 | | |
| |||
| 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 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
| 113 | + | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
120 | 121 | | |
121 | 122 | | |
122 | 123 | | |
| |||
0 commit comments