@objectstack/plugin-webhooks 17.0.0-rc.5 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,2031 @@
1
1
  # @objectstack/plugin-webhooks
2
2
 
3
+ ## 17.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f5a4ef0: refactor!: ADR-0112 batch 2 — sweep the lowercase error-code emitters (#4003)
8
+
9
+ Continues #3841 per ADR-0112. Batch 1 (#3988) settled the vocabulary and closed
10
+ the set; this batch moves the emitters that still spoke lowercase `snake_case`
11
+ onto it.
12
+
13
+ **Wire-visible change.** Error codes on these surfaces change spelling. Generic
14
+ conditions collapse onto the standard catalog rather than keeping a synonym:
15
+ `unauthorized`/`unauthenticated` → `UNAUTHENTICATED`, `forbidden` →
16
+ `PERMISSION_DENIED`, `not_found` → `RESOURCE_NOT_FOUND`, `internal` →
17
+ `INTERNAL_ERROR`, `unavailable` → `SERVICE_UNAVAILABLE`, `not_supported` →
18
+ `NOT_IMPLEMENTED`, `bad_request` → `INVALID_REQUEST`. Domain conditions get codes
19
+ registered in `ERROR_CODE_LEDGER` (`MARKETPLACE_STORAGE_FAILED`,
20
+ `PLUGIN_MANIFEST_INVALID`, `ITEM_LOCKED`, `DELIVERY_NOT_ELIGIBLE`, …). Swept:
21
+ `cloud-connection`, `plugin-auth`, `hono`, `metadata-protocol`, `rest`,
22
+ `service-messaging`, `service-automation`, `trigger-api`.
23
+
24
+ Branch on `error.code` values rather than pattern-matching their case: the
25
+ console's fix for the same rename (objectui#2977) reads codes case-insensitively
26
+ for exactly this reason, and that is the pattern to copy in your own consumers if
27
+ you support servers on both sides of the change.
28
+
29
+ **Four routes stop putting a code in the message slot.** The webhook redeliver
30
+ route, the API-trigger webhook, and two `rest` routes answered
31
+ `{ success: false, error: '<code>', message }` — the code occupying `error`, the
32
+ declared object envelope nowhere. They now emit `error: { code, message }`, and
33
+ three API-trigger branches gained a message they never had. Clients reading
34
+ `body.error` as a string on those routes must read `body.error.code`.
35
+
36
+ **`ConnectorErrorCategory` / `ConnectorRetryStrategy`** (ADR-0112 D9a):
37
+ `@objectstack/spec` exported two mutually incompatible `ErrorCategory` types and
38
+ two `RetryStrategy` types. The connector-side pair is renamed; importers of the
39
+ `integration` subpath update the name. Side effect: the api-side `ErrorCategory`
40
+ and `RetryStrategy` now appear in the generated API reference at all — the name
41
+ collision had been silently dropping them.
42
+
43
+ **`OAUTH_REGISTER_FAILED` replaces an unbounded code source.** The OAuth client
44
+ registration route put better-auth's arbitrary `body.error` string straight into
45
+ `error.code`. The code is now ours and the upstream discriminator moved to
46
+ `details.upstreamError`.
47
+
48
+ **Not swept, deliberately.** `sys_metadata_audit.code` keeps its lowercase values
49
+ (ADR-0112 D6b): it is persisted audit history, and the same column holds
50
+ non-error outcomes (`ok`, `lock_override`). Diagnostics records that ship inside a
51
+ 200 keep theirs (D6c), as do field-level codes (D6, #3977) and the CLI's
52
+ `--json` output contract.
53
+
54
+ A `check:error-code-casing` CI guard now fails on a new lowercase literal in a
55
+ code position, since the ledger's casing rule can only police codes that someone
56
+ registers.
57
+
58
+ - f2445c9: feat(spec,objectql,client,plugin-webhooks): predicate writes get an honest bulk event contract (#4639)
59
+
60
+ A `multi: true` update/delete reaches `IDataDriver.updateMany` / `deleteMany`,
61
+ which are contracted to resolve an affected row COUNT and nothing else. That
62
+ satisfies neither `DataEvent.recordId` (required) nor `before` / `after` /
63
+ `changes`, so before #4626 the engine fabricated a per-record event with
64
+ `recordId: ''` and `after: <count>` — an event every schema-compliant consumer
65
+ must reject, and one the webhook enqueuer's `?? 'unknown'` fallback turned into
66
+ a real delivery naming an unidentifiable record. #4626 removed the fabrication
67
+ and published nothing instead: honest, but it left webhooks, knowledge sync and
68
+ `subscribeData` silent for every predicate write.
69
+
70
+ Bulk writes now get their **own** contract rather than impersonating a
71
+ per-record one or going dark:
72
+
73
+ - **New `BulkDataEvent`** (`@objectstack/spec/api`): `data.records.updated` /
74
+ `data.records.deleted` — note the plural — carrying `id`, `type`, `object`,
75
+ `matched`, `userId?`, `timestamp`. Deliberately a separate schema from
76
+ `DataEvent`, not a widened one: a consumer that receives
77
+ `data.records.updated` knows from the type alone that no `recordId` is
78
+ coming, instead of discovering an empty string at runtime.
79
+ - **Engine** publishes it from the `multi: true` branches of `update()` /
80
+ `delete()`, validated with `BulkDataEventSchema.parse` before publish. A
81
+ predicate that matched **zero** rows publishes nothing (no data changed — this
82
+ is what keeps an idle background sweep from becoming an hourly "0 records"
83
+ delivery), and a driver that resolves a non-count publishes nothing and warns
84
+ rather than asserting a number it cannot verify. Per-record writes are
85
+ untouched, including a scalar `where.id` with `multi: true`, which is still a
86
+ single-record target and still emits `data.record.deleted`.
87
+ - **Webhooks**: two new opt-in triggers, `bulk_update` and `bulk_delete`
88
+ (`WebhookTriggerType`, and the `sys_webhook.triggers` multi-select). They are
89
+ **not** extra sources for `create` / `update` / `delete`: the delivered body
90
+ has no `recordId` and no record, so routing it to existing per-record
91
+ subscribers would hand them a payload missing every field they read — the
92
+ same class of breakage as the old `recordId: ''`, from the other direction. A
93
+ webhook that wants both subscribes to both. Bulk deliveries dedup on the
94
+ producer's event uuid, since two sweeps in the same millisecond are genuinely
95
+ different events that a timestamp-based key would collapse.
96
+ - **Client SDK**: new `client.events.subscribeBulkData(object, cb)`, with the
97
+ same loud boundary validation as `subscribeData`. Kept a separate method for
98
+ the same reason — delivering a `BulkDataEvent` to a `(event: DataEvent) =>
99
+ void` callback would recreate exactly the "typed field, `undefined` at
100
+ runtime" defect #4626 removed. `subscribeData`'s own guard was also tightened
101
+ from `data.` to `data.record.`, so an aggregate event is ignored rather than
102
+ rejected as off-contract.
103
+ - **Knowledge sync** now says out loud that a predicate write leaves its index
104
+ stale. A knowledge index is a per-record projection and `matched: 40` names no
105
+ record, so no event shape could drive it — the durable fix is reconciliation,
106
+ tracked in #4672.
107
+
108
+ The event carries no `where` predicate. The only one available at publish time
109
+ is the middleware-composed AST, whose filter embeds the security layer's
110
+ injected row scoping (RLS, sharing) — publishing it would ship tenant scoping
111
+ internals to whatever external URL a webhook points at.
112
+
113
+ Also pays off a measurement debt from #4655, which claimed the write-path cost
114
+ of event publishing had been measured but never published the numbers:
115
+ `packages/objectql/src/engine-data-events.bench.ts` measures it. Against an
116
+ in-memory driver, publishing costs ~7–9µs per event (insert 0.021ms vs 0.012ms,
117
+ single-id update 0.013ms vs 0.007ms). A bulk write pays that **once** regardless
118
+ of how many rows matched (0.040ms vs 0.034ms over a 100-row match set), so its
119
+ relative cost shrinks as the match set grows.
120
+
121
+ - 69f1dfd: fix(webhooks): materialize stack-declared webhooks into the dispatcher (#3461)
122
+
123
+ A webhook authored declaratively — `defineStack({ webhooks })` / `defineWebhook()`,
124
+ validated against the spec `WebhookSchema` — was a **silent no-op**. The runtime
125
+ dispatcher (`AutoEnqueuer`) fans out off `sys_webhook` DATA rows (`object_name` /
126
+ `active`), which until now were only ever written by hand through the object's
127
+ CRUD UI. Nothing turned a declared webhook (`object` / `isActive`) into a
128
+ dispatchable row, so authoring `webhooks:` on a stack produced `webhook` metadata
129
+ that never fired (ADR-0078). The showcase app itself shipped a `webhooks:` entry
130
+ that did nothing.
131
+
132
+ `@objectstack/plugin-webhooks` now bridges the two on boot:
133
+
134
+ - **`bootstrapDeclaredWebhooks`** reads declared `webhook` metadata from the
135
+ ObjectQL registry (where the manifest decomposition already parks
136
+ `stack.webhooks`), validates each through `WebhookSchema.parse()` — the spec
137
+ schema finally has a real consumer — and materializes it into a `sys_webhook`
138
+ row, mapping `object → object_name`, `isActive → active`, and stashing the full
139
+ envelope (headers / secret / retry / timeout) in `definition_json`. The
140
+ auto-enqueuer's first cache refresh then picks the row up and dispatches it.
141
+ - **Seed-not-clobber provenance** (mirrors `sys_sharing_rule`, #2909): `sys_webhook`
142
+ gains `managed_by` / `customized` columns. Declared webhooks re-seed every boot
143
+ as `managed_by: 'package'`, but a row an admin created (`managed_by: 'admin'`) or
144
+ edited in Setup (`customized: true`, stamped by a `beforeUpdate` hook) is never
145
+ overwritten — a deactivated noisy webhook survives redeploys.
146
+
147
+ Connector-declared `webhooks` remain not-yet-enforced (that is a separate seam,
148
+ #3197). Registering `webhook` as a first-class metadata type + enrolling it in the
149
+ liveness `GOVERNED` set is a tracked follow-up.
150
+
151
+ Migration: none required. Existing hand-authored `sys_webhook` rows default to
152
+ `managed_by: 'admin'` and are never touched by the seeder. Anyone who authored
153
+ `webhooks:` on a stack expecting it to fire will find it now does — review those
154
+ declarations (especially `url` / `isActive`) before upgrading.
155
+
156
+ ### Patch Changes
157
+
158
+ - 257d97a: ADR-0078 Phase 4, decided rather than deferred: the silent skips stop being silent at runtime. The registry — the one choke point every metadata door goes through — now emits a functional-completeness diagnostic at registration, and the webhook enqueuer's zero-trigger skip warns instead of returning `null` wordlessly.
159
+
160
+ **The Phase 4 ruling.** The phase had two halves, and they got opposite verdicts:
161
+
162
+ - **Generative rule sweep: rejected — not deferred.** A generator can enumerate candidates ("which optional keys might be load-bearing?") but cannot verify runtime skip sites, and a rule without its skip-site citation is a false prescription — this campaign shipped four of those and every one was caught by the verification pass a generator would skip. The route is structurally wrong; no amount of waiting produces the evidence that would fix it.
163
+ - **Registration-time diagnostics: built now.** The evidence was already in hand, not pending: #3896 (Setup authoring inserted `sys_sharing_rule` rows directly, bypassing the schema that "required" `criteria`) and cloud's `rowColor.mapping` (an `as never` cast bypassed tsc) prove that doors which skip Zod and lint are real. The author-time gate only protects metadata that passes through `os build` / `validate` / `lint`; `SchemaRegistry.registerObject` is where _every_ door converges — declared stacks, plugin objects, `extend` contributions, `saveMetaItem`, raw `registerObject` calls.
164
+
165
+ **Same predicate, same rule ids, different posture.** The registry calls the same `checkFieldCompleteness` that `validate-functional-completeness` uses, so the boot log carries the _same rule ids_ the lint reports (`field/summary-without-operations`, …) — an operator or an AI reading the log greps the id straight into the same docs and suppression story. But the registry **warns and never throws**: ADR-0078 §1's error severity means _the instance is dead_, not _the system is dead_ — an inert field must not kill a boot that thousands of healthy objects share. Errors block at author time; the registry's job is to make sure the silence never survives to runtime unobserved.
166
+
167
+ One line per object with every finding aggregated (not per request — the hot path stays free; not per finding — a three-dead-field object is one greppable line). Follows `warnStrippedLegacyApiMethods` (#3543) exactly: module-level once-per-object dedup, injectable `warn`, pure observation that never mutates the schema.
168
+
169
+ **The webhook skip now names itself.** `auto-enqueuer.ts`'s `if (triggers.size === 0) return null` sat under a comment blessing the empty case as "a manual-only webhook" — a mode #3196 removed (no manual fire path exists). The skip now warns with the author-time rule id (`webhook/without-triggers`), and the comment tells the truth. Only _active_ rows reach the parse (`where: { active: true }` — verified, not assumed), so a deliberately disabled webhook stays warning-free.
170
+
171
+ **Scope honesty:** field rules and the webhook rule get the runtime twin. `view/layout-without-binding` stays author-time-only — views don't register through this choke point and the renderer half of the evidence lives in objectui.
172
+
173
+ Tracked in #4544. This closes the ADR-0078 loop end to end: author-time error, runtime warning, one shared predicate deciding both.
174
+
175
+ - bb1ce2e: fix(plugin-auth,plugin-webhooks): retire a dead degrade branch and an implicit transitive dependency (ADR-0116 follow-ups, #4187)
176
+
177
+ Two concrete findings from the ADR-0116 consumer-side audit, plus the
178
+ authoring rule that would have prevented both.
179
+
180
+ **`plugin-auth` claimed a fallback it did not have.** `init()` ran
181
+ `const dataEngine = ctx.getService('data'); if (!dataEngine) { warn('No data
182
+ engine service found - auth will use in-memory storage') }`. That branch could
183
+ never execute: `getService` **throws** for an unregistered service rather than
184
+ returning `undefined`, and this plugin declares a hard dependency on ObjectQL
185
+ (which registers `data` unconditionally), so a kernel without the engine fails
186
+ even earlier with `Dependency … not found`. The branch is removed and the real
187
+ contract is declared — `requiresServices: ['data', 'manifest']` — which also
188
+ replaces a trailing `// manifest service required` comment with the
189
+ machine-checked form of the same claim. `AuthManager` keeps its own optional
190
+ `dataEngine` guards: it is usable outside the plugin.
191
+
192
+ **`plugin-webhook-outbox` was protected only transitively.** It resolves
193
+ `manifest` in `init()` with no fallback while depending on
194
+ `com.objectstack.service.messaging`, which in turn depends on ObjectQL, the
195
+ actual provider. That works today and would have broken silently the day
196
+ messaging stopped depending on the engine — surfacing as a crash inside an
197
+ unrelated plugin's init. It now declares `requiresServices: ['manifest']`
198
+ directly.
199
+
200
+ Neither change alters ordering or boot outcomes on any current composition:
201
+ both plugins were already ordered correctly. What changes is what a broken
202
+ composition _says_, and that the guarantees are now checked rather than
203
+ inherited.
204
+
205
+ Docs: `content/docs/plugins/anatomy.mdx` gains the three ADR-0116 fields and
206
+ the decision rule for resolving a service inside `init()` (hard dependency vs
207
+ `optionalDependencies` + `requiresServices`), including the two traps behind
208
+ these fixes — don't rely on a transitive provider, and don't write an
209
+ `if (!svc)` fallback after a bare `getService`. The api-registry example
210
+ declares the contract on all seven of its plugins instead of relying on
211
+ `kernel.use()` order.
212
+
213
+ - 2e836de: chore(packaging): CHANGELOG.md ships in every npm tarball (#4261)
214
+
215
+ The AGENTS.md post-task checklist requires breaking changesets to carry their
216
+ FROM → TO migration because "this text ships to consumers as `CHANGELOG.md`
217
+ inside the npm package and is what an upgrading agent greps after the tombstone
218
+ error." That delivery path was severed for 68 of the 69 publishable packages:
219
+ npm packs `package.json` / `README*` / `LICENSE*` unconditionally but — unlike
220
+ older npm versions — not `CHANGELOG.md`, and the canonical
221
+ `"files": ["dist", "README.md"]` whitelist never named it. Measured on npm
222
+ 10.9.7: `npm pack --dry-run` on `@objectstack/types` shipped 3 files while its
223
+ 70KB `CHANGELOG.md` stayed behind. Only `@objectstack/spec` listed it
224
+ explicitly.
225
+
226
+ The tombstone-error scenario is precisely the one where the repo is out of
227
+ reach — the upgrading agent has `node_modules` and nothing else — so the
228
+ migration text has to ride in the tarball. Every publishable package now
229
+ declares `CHANGELOG.md` in `files`, and the canonical whitelist is
230
+ `["dist", "README.md", "CHANGELOG.md"]`.
231
+
232
+ The other half is the gate: `check:published-files` gains a fifth invariant,
233
+ COMPLETE — a whitelist that fails to cover `CHANGELOG.md` fails the
234
+ always-required lint job, so the next package cannot silently sever the path
235
+ again. `@objectstack/spec`'s per-package EXTRA_ENTRIES exemption dissolves
236
+ into the canonical set.
237
+
238
+ Consumer-visible change: one more file per install (the package's changelog,
239
+ e.g. 70.8KB for `@objectstack/types`), and `grep -r "removed key"
240
+ node_modules/@objectstack/*/CHANGELOG.md` now finds the migration it was
241
+ promised.
242
+
243
+ - 462b713: fix(objectql,client): `subscribeData` callbacks receive real `DataEvent`s — the producer now fulfils the declared contract (#4626)
244
+
245
+ `@objectstack/spec/api`'s `DataEvent` declares top-level `id` (uuid,
246
+ required), `type`, `object`, `recordId` (required), `changes?`, `before?`,
247
+ `after?`, `userId?`, `timestamp`. But the producer (the ObjectQL engine)
248
+ published a raw `RealtimeEventPayload` envelope with `{ recordId, after,
249
+ changes }` nested under `payload` and never generated `id`/`userId`, while the
250
+ client SDK force-cast that envelope into the callback (`callback(event as any
251
+ as DataEvent)`). Subscribers who wrote `event.recordId` / `event.changes` —
252
+ exactly what the types promised — compiled green and read `undefined` at
253
+ runtime. The data-side twin of #4602.
254
+
255
+ Producer now fulfils the contract:
256
+
257
+ - `ObjectQL.insert()` / `update()` / `delete()` build a true `DataEvent`
258
+ (generated uuid `id`, flattened top-level fields, `userId` from the
259
+ execution context when the write names an actor) and validate it with
260
+ `DataEventSchema.parse` before publishing. The transport envelope is
261
+ unchanged (`RealtimeEventPayload`, with `payload` carrying the complete
262
+ `DataEvent`), so subscribers keep receiving `{ type, object, payload,
263
+ timestamp }` on the wire.
264
+ - A batch insert publishes one event **per record** (as before), each with its
265
+ own event id.
266
+ - **A multi-row write (`multi: true` → `updateMany` / `deleteMany`) now
267
+ publishes nothing.** Those driver methods return only an affected count, so
268
+ there is no record for a required `recordId` to name; the engine logs a
269
+ warning naming the gap instead of publishing the previous fabrication
270
+ (`recordId: ''`, `after: <affected count>`), which every schema-compliant
271
+ consumer had to reject. **Consequence: webhooks and knowledge sync no longer
272
+ fire for bulk writes** — they previously fired once with an unusable body. A
273
+ real bulk event contract is tracked in #4639.
274
+
275
+ Consumers validate or read the fulfilled shape instead of guessing:
276
+
277
+ - `@objectstack/client`'s `subscribeData` (and therefore
278
+ `@objectstack/client-react`'s `useDataSubscription` /
279
+ `useDataSubscriptionCallback` / `useAutoRefresh`, which delegate to it)
280
+ unwraps the envelope and runs `DataEventSchema.safeParse` at the boundary.
281
+ An off-contract payload is rejected loudly (handler error, callback never
282
+ invoked) — never coerced or passed through. The `as any as DataEvent`
283
+ double-cast is gone, and the `recordId` option now filters on the fulfilled
284
+ event.
285
+ - `@objectstack/plugin-webhooks`' auto-enqueuer reads the required
286
+ `recordId` directly; its `recordId ?? id ?? after?.id ?? before?.id ??
287
+ 'unknown'` fallback chain is gone, and an off-contract event is dropped with
288
+ a warning rather than delivered under the literal id `'unknown'`. Delivered
289
+ webhook bodies now also carry the event's `id`/`type`/`userId`; the record
290
+ itself stays nested under `after` and the envelope keys (`object`,
291
+ `recordId`, `action`, `timestamp`) still win.
292
+ - `@objectstack/service-knowledge`'s event sync reads the record from `after`
293
+ (create/update) and the id from `recordId` (delete) for `data.record.*`.
294
+ It previously indexed the envelope itself as if it were the row, and never
295
+ resolved an id for deletes.
296
+
297
+ - a225ef5: fix(runtime,webhooks): the path object wins on /data/:object/query, and the webhook envelope owns its keys (#3946)
298
+
299
+ Follow-up sweep for the shape behind #3897 and #3933 — a trusted, server-derived
300
+ value written into an object literal with a caller-controlled bag spread OVER
301
+ it. Both of those were in the same block of REST code, so the pattern was swept
302
+ across all 1313 non-test TypeScript files in `packages/`. Nine candidate sites;
303
+ one real, one worth hardening, seven verified clean (recorded in #3946 so the
304
+ next sweep does not re-litigate them).
305
+
306
+ **`POST /data/:object/query` (runtime dispatcher).** The `/data` domain built
307
+ `{ object: objectName, ...body }`, so `{"object":"other", …}` in the body moved
308
+ the read to a different object than the URL named.
309
+
310
+ This is NOT an authorization bypass, and the tests pin why: `callData` gates
311
+ API exposure on `params.object`, so the gate followed the body and agreed with
312
+ the read — an object hidden by `apiEnabled: false` was refused either way. What
313
+ broke is that the URL stopped describing the operation (audit trails, logs, and
314
+ anything keyed on the request path saw object A while object B was read), and
315
+ that one endpoint spoke a second dialect of the contract the REST side had just
316
+ standardised on: the path object wins. The other handlers in that file never had
317
+ the problem — they nest caller data (`data: body`, `query: normalized`) instead
318
+ of splatting it, and the GET-by-id branch already allowlists its query params
319
+ against exactly this pollution.
320
+
321
+ **Webhook delivery envelope.** `auto-enqueuer` built
322
+ `{ object, recordId, action, timestamp, ...payload }`, letting an event payload
323
+ rewrite the envelope a subscriber receives. Behaviour-neutral for the engine's
324
+ own publishers — `data.record.*` payloads are `{ recordId, after, changes }`
325
+ with record fields nested under `after`, so none of those four keys collide
326
+ today — but the shape was wrong, and the `payload.id` fallback right above it
327
+ suggests publishers that flatten record fields do exist. Envelope keys are
328
+ written last now.
329
+
330
+ - b5f9397: fix(sharing,runtime): a `sort` passed straight to the engine never ordered anything; migrate every in-repo engine call to canonical QueryAST keys (#4346)
331
+
332
+ Two changes with different weights, from one sweep of every in-repo engine
333
+ call site that still speaks a deprecated alias.
334
+
335
+ **The bug — three dropped sorts.** #4346 made the engine fold `filter`→`where`
336
+ and `top`→`limit` on all six methods. The other four pairs in
337
+ `RPC_QUERY_ALIAS_SLOTS` (`select`, `sort`, `skip`, `populate`) are folded at
338
+ the RPC/wire layer only — their values need shape lowering that belongs to
339
+ those layers — and a **direct `engine.find()` never crosses that layer**. Three
340
+ call sites passed `sort` there, so it rode onto the AST untouched, every
341
+ driver's `Array.isArray(query.orderBy)` guard declined to emit an ORDER BY, and
342
+ the query returned an ordinary-looking, arbitrarily-ordered result:
343
+
344
+ | call site | asked for | actually got |
345
+ | ----------------------------------- | ------------------------------------------------- | --------------------------- |
346
+ | `share-link-routes.ts` | shared AI conversation messages, `created_at asc` | messages in arbitrary order |
347
+ | `runtime/domains/share-links.ts` | same route, runtime-domain copy | same |
348
+ | `share-link-service.ts` `listLinks` | the 200 most recent share links | an arbitrary 200 |
349
+
350
+ All three combine the dropped sort with a `limit` — the "latest N" shape whose
351
+ failure #4226 spelled out: an unapplied sort returns rows in arbitrary order,
352
+ which `limit` then slices into an arbitrary page. #4226 fixed that in the wire
353
+ normalizer; these calls sit one layer below it. `listLinks` had no test at all,
354
+ which is why it went unnoticed. Now pinned — on the option bag the engine
355
+ receives, not on row order, because the failure is that the key never becomes
356
+ `orderBy` and a fake engine honouring either spelling would pass either way.
357
+
358
+ **The cleanup — 27 no-op renames.** Every remaining in-repo engine call passing
359
+ `filter` now passes `where` (approvals 5, auth 2, reports 6, sharing 11,
360
+ webhooks 2, plus the one `filters` in a spec doc example). These are strict
361
+ no-ops since #4346 folds the alias — the point is that the framework stops
362
+ depending on a spelling it asks users to migrate off, which is a prerequisite
363
+ for ever retiring the aliases. Service-level `filter` PARAMETERS (each
364
+ service's own public API, e.g. `listRequests(filter)`) are deliberately
365
+ untouched — those are not engine option bags.
366
+
367
+ Two of the renamed calls were live victims of the #4346 bug rather than
368
+ cosmetic: `auth-manager`'s `stampIdentitySource` read the table's first row via
369
+ `findOne({filter})` and counted the whole table via `count({filter})`, so a
370
+ federated sign-in never stamped `source: 'idp_provisioned'`. #4346 already
371
+ corrected the behaviour; this makes the call say what it means.
372
+
373
+ - 8af76ae: The i18n extractor's default locale now tracks the source instead of merging (#8543), and the approval vocabularies carry authored English labels in the contract (#8580).
374
+
375
+ - `os i18n extract` merge mode no longer applies to the default locale: `en` is a copy of the source, not a translation, so an edited label/description/help now reaches the regenerated `en` bundle instead of being silently shadowed by the stale entry forever (53 stale entries had accumulated across 6 packages under the old behavior; all rewritten here). Translated locales (`zh-CN` / `ja-JP` / `es-ES`) keep merge semantics exactly as before — no existing translation is overwritten.
376
+ - Bare-string and label-less select options now seed through the extractor's derived channel: the machine value still seeds the skeleton, but the coverage gate no longer demands "translations" of machine identifiers, and a copied value can no longer masquerade as authored display text.
377
+ - New `@objectstack/spec/contracts` exports `APPROVAL_STATUS_LABELS` and `APPROVAL_ACTION_KIND_LABELS`: the authored English for `sys_approval_request.status` (previously living only in the generated `en` bundle) and `sys_approval_action.action` (previously shipping raw machine values such as `submit` / `request_info` — the #7232 humanization missed this sibling field). Both columns derive their option labels from these maps; the regenerated `en` bundles copy them verbatim.
378
+
379
+ - aff9e56: fix(i18n): translate the platform packages' declared surface, and gate all nine bundles instead of one (#3762)
380
+
381
+ Only `platform-objects` was wired into a translation-drift check. The other
382
+ **eight** packages shipped a `scripts/i18n-extract.config.ts` that nothing ever
383
+ ran — and four of them had already drifted out of sync with the schema, exactly
384
+ the rot `pnpm check:i18n` exists to catch, one directory over.
385
+
386
+ **Translated.** `plugin-security` (45 strings per locale), `plugin-webhooks`
387
+ (15), `plugin-audit` (8), `plugin-sharing` (7) and `service-storage` (7) are now
388
+ at **zero** untranslated declared strings in zh-CN / ja-JP / es-ES — 246
389
+ translations. Most were newly _visible_ rather than newly missing: #3753 taught
390
+ the coverage detector to walk action `params`, `resultDialog`, `listViews` and
391
+ the rest of the declared surface, and these are what it found.
392
+
393
+ Wording was harvested from the repo's own bundles wherever a string was already
394
+ translated somewhere (1382 unambiguous source strings), so `Created At` reads
395
+ `创建时间` here because that is what it reads everywhere else, rather than a
396
+ fresh invention. Protocol tokens are deliberately left identical across locales:
397
+ `GET` / `POST` / `PUT` / `PATCH` / `DELETE`, `ETag`, `ACL`, `URL`.
398
+
399
+ **Gated.** `scripts/check-i18n-bundles.mjs` replaces the single-package
400
+ `pnpm check:i18n` and checks all nine. It does not restate each package's
401
+ command — it parses the one already documented in that config's own docstring
402
+ and runs it, so the documented regenerate command and the gate cannot diverge.
403
+ The coverage ratchet grows the same way, from `examples/*` to twelve configs;
404
+ eight of them sit at zero, which makes it the strict gate there.
405
+
406
+ **Fixed a real truncation bug it exposed.** `os lint --json` on a large config
407
+ came out of a pipe cut off at exactly 65536 bytes — `console.log(big)` followed
408
+ by `process.exit(1)` tears the process down before an async pipe write drains,
409
+ while an interactive run (stdout is a TTY, written synchronously) looks perfect.
410
+ Every scripted consumer silently got invalid JSON. `emitJson` in
411
+ `packages/cli/src/utils/format.ts` waits for the write to drain and sets
412
+ `process.exitCode` instead; `lint`, `i18n check` and `i18n extract` use it.
413
+ Roughly 30 other CLI commands share the pattern and are not touched here.
414
+
415
+ The nine documented regenerate commands also gain `--no-metadata-forms` (added
416
+ in #3768), since the Studio metadata-form baseline belongs to `platform-objects`
417
+ alone, not to a copy in every plugin.
418
+
419
+ Not fixed here: `platform-objects`' own 77-per-locale gap is `apps.*` /
420
+ `dashboards.*` navigation and widget labels, which live outside the `objects`
421
+ subtree and cannot be scaffolded while the package extracts with
422
+ `--objects-only`. That needs an emit decision first — tracked in #3762.
423
+
424
+ - cc2de0e: chore(packaging): 20 packages stop publishing their sources, tests and build tooling (#4248)
425
+
426
+ These 20 packages declared no `files` field, so npm fell back to packing the
427
+ whole package directory. `npm pack --dry-run` on `@objectstack/plugin-webhooks`
428
+ listed **21 files** — 15 under `src/`, three of them unit tests
429
+ (`auto-enqueuer.test.ts`, `bootstrap-declared-webhooks.test.ts`, …), plus the
430
+ build-time `scripts/i18n-extract.config.ts`. `dist/` lands on top of that at
431
+ publish time rather than instead of it, so consumers were installing the
432
+ TypeScript sources and the test suite alongside the artifact they asked for.
433
+
434
+ Each now declares `"files": ["dist", "README.md"]`, matching the 29 packages
435
+ that already did. Nothing a consumer imports moves: every `main` / `types` /
436
+ `exports` target in all 20 already resolved inside `dist/`, which the new
437
+ `check:published-files` guard verifies rather than assumes. The visible change
438
+ is a smaller install and a smaller dependency-scanning surface — `npm pack` on
439
+ `@objectstack/plugin-webhooks` now yields 2 files plus `dist/`.
440
+
441
+ The other half of the fix is the gate. Half the packages declaring `files` and
442
+ half not was the #3786 shape — a hand-copied convention with nothing enforcing
443
+ it, where whoever forgets the line gets no signal at all. `check:published-files`
444
+ (new, wired into the always-required `lint` job) holds every non-private
445
+ workspace package to four invariants: `files` is **declared**; it is
446
+ **sufficient** (covers every entry point, so tightening a whitelist cannot ship
447
+ a package that fails to resolve); it is **minimal** (admits no test, test-harness
448
+ config or build script); and anything beyond `dist` + `README.md` is
449
+ **registered** with a reason, reconciled in both directions so a stale exemption
450
+ is an error rather than dead text. `@objectstack/spec` is the one package with
451
+ registered extras — its `.zod.ts` sources, JSON Schemas, liveness ledgers and
452
+ `CHANGELOG.md` are product, not build input.
453
+
454
+ This also closes an assumption #4206 was resting on. Excluding `<pkg>/scripts/**`
455
+ from the docs-drift implementation test is sound only while no package publishes
456
+ `scripts/` as runtime code; that held, but it held because someone read all three
457
+ offenders by hand. It is now checked on every PR.
458
+
459
+ - 52281b0: chore(i18n): purge the dead sys_webhook_delivery translation block and guard against recurrence
460
+
461
+ `sys_webhook_delivery` was removed when webhook delivery moved to
462
+ `@objectstack/service-messaging` (`sys_http_delivery`, ADR-0018 M3), but a full
463
+ translation block for it lingered in the four generated plugin-webhooks i18n
464
+ bundles (en/zh-CN/ja-JP/es-ES) — dead weight bound to an object that no longer
465
+ exists, and destined to be dropped silently (with any curated strings) on the
466
+ next `os i18n extract`.
467
+
468
+ - Removed the stale `sys_webhook_delivery` block from all four locale bundles
469
+ (surgical; the `sys_webhook` block is untouched).
470
+ - Corrected three stale `sys_webhook_delivery` doc comments (platform-objects
471
+ `integration/index.ts` + `setup.app.ts`, plugin-webhooks `sys-webhook.object.ts`)
472
+ that still named it as a plugin-webhooks-owned object.
473
+ - Rolled out the platform-objects `bundle-ownership` test guard (#2834 ⑤ /
474
+ ADR-0029 D8) to the eight packages that own i18n bundles, so a stray object
475
+ block in a generated bundle now fails the build instead of dying silently.
476
+ - That guard immediately surfaced a live-object omission: `sys_capability` was
477
+ present in plugin-security's bundles with curated translations but had been
478
+ dropped from its extract config — re-added to the config so the strings are
479
+ preserved, rather than deleted.
480
+
481
+ - 30f1b74: fix(plugins): a declared item reaches its schema intact — retire the `i?.content ?? i` unwrap from plugin read paths (#8378)
482
+
483
+ Ten production reads over `SchemaRegistry.listItems` unwrapped every declared
484
+ item as `i?.content ?? i`, presuming a `{ name, content }` storage envelope.
485
+ That envelope has **no producer**. Re-measured at these seams rather than
486
+ inherited from #7519's measurement of `MetadataFacade`:
487
+
488
+ - `registerMetadataCollections` (objectql) registers each stack-collection
489
+ element as-is — `registerItem(type, item, 'name')`, no boxing;
490
+ - `loadMetaFromDb` registers `convertStoredItem(JSON.parse(record.metadata))` —
491
+ the parsed body, never the `sys_metadata` row (whose body column is
492
+ `metadata`, not `content`);
493
+ - the facade's own interim boxing of non-object values, the one writer that ever
494
+ produced the shape, was removed by #8349.
495
+
496
+ **Removal is a fix, not a cleanup.** None of the types read through these seams
497
+ — `permission`, `position`, `capability`, `object`, `sharingRule`, `webhook`,
498
+ `emailTemplate` — declares a stored `content` key; every one of them rejects it
499
+ as an unrecognized key. So wherever the key did appear the unwrap replaced a
500
+ whole authoring document with one of its values, and `''` — falsy but
501
+ non-nullish — passed `??` and then died at the reader's own `filter(Boolean)`,
502
+ dropping the item with no warning, no count and no row.
503
+
504
+ **On email templates the harm was sharpest, and it is the one users will
505
+ notice.** `content` really is a spelling an author can write there:
506
+ `EmailTemplateDefinitionSchema` lists it in its `strictObject` **aliases** table
507
+ (`content: 'bodyHtml'`). That table is a _rejection_ facility, not a conversion —
508
+ it feeds `strictUnknownKeyError`, which runs only on the `unrecognized_keys`
509
+ path and only builds a message; nothing rewrites the key, and the ADR-0087
510
+ conversion layer has no `email_template` entry either. The schema was therefore
511
+ always ready with the author's fix, and the unwrap was the one thing standing
512
+ between the author and it: the HTML string reached
513
+ `EmailTemplateDefinitionSchema.parse()`, which answered `Invalid input: expected
514
+ object, received string`, and the boot warning's `name` field came back
515
+ `undefined` — so an operator could not even tell **which** template had failed.
516
+
517
+ A template authored with `content` now yields what it was always meant to:
518
+
519
+ > Unrecognized key(s) on this email template: `content`. Did you mean
520
+ > `content` → `bodyHtml`?
521
+
522
+ …named against the template it came from, and counted as `skipped` rather than
523
+ vanishing.
524
+
525
+ No behaviour changes for spec-valid metadata: the reads hand back exactly the
526
+ documents they always did.
527
+
528
+ - 2465133: fix(plugins): sweep the service-lookup erasures out of the plugin composition roots, and fix the two alias-only HTTP reads it exposed (#4251 B5)
529
+
530
+ Batch B5 of the #4251 sweep: the seven remaining `packages/plugins/*` composition
531
+ roots. 35 lookup sites that had been erased to `any` now carry the slot's
532
+ contract, so the compiler checks what each plugin actually calls on the service
533
+ it resolved. The ratchet drops 143 sites / 32 files to 108 / 25.
534
+
535
+ **Two real defects, both of the shape this sweep exists to find.** Approvals'
536
+ actionable-link pages (ADR-0043) and sharing's public share-link REST routes each
537
+ read the HTTP server under `http-server` _only_ — the deprecated alias. The
538
+ ledger records `http.server` as canonical and as the only name present on every
539
+ provider path: `runtime.ts`'s `config.server` path registers no alias at all. On
540
+ that path both lookups threw, the surrounding `catch` swallowed it, and the
541
+ routes silently never mounted — approval e-mail action links 404'd and the
542
+ share-link surface was absent, with nothing in the log to say so. Both reads are
543
+ now canonical-first with the alias as fallback, each name in its own `try`
544
+ because `getService` throws on an empty slot (so `a() ?? b()` inside one `try`
545
+ never reaches `b` — the same correction #4393 made in metadata and
546
+ cloud-connection).
547
+
548
+ Typing choices follow the batch method: pure data-plane consumers take the
549
+ narrow contract (`IDataEngine` in reports), consumers that bind hook or
550
+ middleware seams take the engine seen whole (`IObjectQLEngine` in approvals,
551
+ sharing and pinyin-search), and slots with no contract get a **named** local
552
+ surface rather than `any` — plugin-email's `MailSettingsSurface`, and the
553
+ surfaces the consuming packages already declared (`ApprovalMessagingSurface`,
554
+ `SharingSecurityProbe`, `ReportEmail`). A named surface that omits a member
555
+ still makes the compiler name every call site; `any` says nothing.
556
+
557
+ No behaviour change beyond the two alias reads. No contract changes.
558
+
559
+ - b45c71e: fix(plugin-security,plugin-sharing,plugin-webhooks,platform-objects,service-messaging,spec): five tenant-scoped declared unique indexes become per-organization (#8554)
560
+
561
+ Five platform objects declared their uniqueness as a table-level index with bare
562
+ `unique: true`. At the DECLARED-index level that is the positional spelling of
563
+ `'global'` — the listed columns verbatim — so on a tenant-scoped object each
564
+ materialized an **installation-wide** unique index. (Field-level `unique: true`
565
+ means the opposite, per-organization, and has since #3696; `packages/lint` names
566
+ that divergence "the #4986 trap" and warns on it via
567
+ `unique/unscoped-declared-index`.) These are the fourth act of the class ruled on
568
+ 2026-08-13, after `sys_user_preference` / `sys_capability` (#8461) and
569
+ `sys_position` (#8556).
570
+
571
+ | object | package | was | now |
572
+ | ----------------------------- | ------------------- | ---------------------------------- | --------------------------------- |
573
+ | `sys_permission_set` | `plugin-security` | `[name]` global | `[name]` per organization |
574
+ | `sys_sharing_rule` | `plugin-sharing` | `[name]` global | `[name]` per organization |
575
+ | `sys_webhook` | `plugin-webhooks` | `[name]` global | `[name]` per organization |
576
+ | `sys_email_template` | `platform-objects` | `[name, locale]` global | `[name, locale]` per organization |
577
+ | `sys_notification_preference` | `service-messaging` | `[user_id, topic, channel]` global | same, per organization |
578
+
579
+ Measured live on a real engine before the fix — two organizations, the same key,
580
+ `OS_TENANCY_POSTURE=isolated`, driving the real shipped declarations. All five
581
+ reproduced identically:
582
+
583
+ ```
584
+ org_jia POST the key → 201
585
+ org_yi POST the SAME → 409 UNIQUE_VIOLATION
586
+ org_yi POST an unused → 201 ← the control that makes it an oracle
587
+ org_yi GET the key → total 0 ← refused by a row it cannot see
588
+ ```
589
+
590
+ Two consequences, both removed. **A cross-tenant existence oracle:** the 409 is a
591
+ per-value answer about a row the caller cannot read, so an organization could
592
+ enumerate another organization's permission-set, sharing-rule, webhook and
593
+ template naming. **A functional dead end:** the second organization simply could
594
+ not use the name, and the refusal did not say why. For
595
+ `sys_notification_preference` the shape is the one #8323 measured on
596
+ `sys_user_preference` — a user belonging to two organizations could not hold
597
+ independent per-topic delivery toggles.
598
+
599
+ ## ⚠️ Operators: a migration is REQUIRED, and deploying this release is not it
600
+
601
+ Respelling a declared index changes its generated **name**. On an existing
602
+ database `initObjects` is additive: it creates the new per-organization composite
603
+ at boot and **never drops the old global index**, which goes on enforcing. Until
604
+ the retirement is applied, a deployed installation that has taken this release is
605
+ still enumerable — that is asserted as a test, not assumed.
606
+
607
+ Run the migration:
608
+
609
+ ```
610
+ os migrate plan # shows one `replace_unique_index` per object, categorised `safe`
611
+ os migrate apply # no --allow-destructive needed
612
+ ```
613
+
614
+ Each object plans as **one pure relaxation**, not as two findings. That matters:
615
+ if it read as "composite missing" (safe) plus "old global index orphaned"
616
+ (destructive, opt-in), an operator applying only the safe half would keep the
617
+ global index — keep the defect — while the plan read as applied. The `#8461`
618
+ `replace_unique_index` arm covers all five unchanged (no driver change in this
619
+ release), applies CREATE-before-DROP so uniqueness is never unenforced in
620
+ between, drops the legacy index only once the replacement is confirmed present,
621
+ preserves every row, and converges to no drift.
622
+
623
+ Two columns are worth an operator's attention:
624
+
625
+ - `sys_notification_preference`'s replacement index name is **hash-suffixed** —
626
+ `uniq_sys_notification_preference_a22d7d27` — because the natural name is 70
627
+ characters and the limit is 60. That is expected, not corruption.
628
+ - Rows with no `organization_id` (platform/seed rows) stay unique **among
629
+ themselves**: the organization key part is NULL-safe
630
+ (`COALESCE(organization_id, '__global__')`, ADR-0120 D3), so seeding by name
631
+ keeps working and a tenant may hold its own row of the same name.
632
+
633
+ ## Not breaking
634
+
635
+ A relaxation admits key pairs that were previously refused and refuses nothing
636
+ that previously succeeded, so no caller that worked before fails now. Every read
637
+ path for these five objects goes through the tenant-scoped data API, so no
638
+ consumer resolves one of these names across organizations expecting at most one
639
+ row. Shipped as `patch` for that reason — the same call #8556 made for the same
640
+ shape.
641
+
642
+ Published text carrying the bare uniqueness claim was corrected at its source and
643
+ the generated reference pages regenerated (`security/permission.mdx`,
644
+ `automation/webhook.mdx`, and `integration/connector.mdx`, which embeds the same
645
+ webhook schema), together with the `sys_permission_set` field description, its
646
+ clone-dialog help text, the `sys_webhook` field description, and the matching
647
+ translation bundles in all four shipped locales.
648
+
649
+ - f46e987: fix(plugin-webhooks): a webhook holding an encrypted signing secret re-arms the moment the CryptoProvider registers, instead of ~60s later (#8022)
650
+
651
+ For roughly **60 seconds after every server start**, a webhook whose
652
+ `signing_secret` is encrypted (the population #7799 created) was **not
653
+ subscribed**. A record change in that window produced no delivery **and no
654
+ `sys_http_delivery` row at all** — no dead letter, no retry, no durable trace
655
+ that anything was missed — while `GET /api/v1/data/sys_webhook/` kept reading
656
+ `active: true`, so the webhook looked armed in Setup the whole time. It
657
+ self-healed at the next periodic cache refresh, which is why it was invisible to
658
+ anyone not watching that window.
659
+
660
+ **The fail-closed behaviour is unchanged and is not the bug.** Dropping a
661
+ subscription whose stored key cannot be recovered — rather than delivering it
662
+ unsigned — is #7799's whole point and still holds: the signature is the
663
+ receiver's only proof of origin, and a webhook that stops arriving gets
664
+ investigated while one that keeps arriving unsigned teaches the receiver to
665
+ accept unauthenticated traffic. What was wrong is that a fail-closed drop
666
+ outlived its own cause.
667
+
668
+ **The ordering.** It was never a race that sometimes went the other way. Plugins
669
+ run inside `kernel:ready`, which `runtime.start()` completes; the host's
670
+ composition root calls `engine.setCryptoProvider(...)` only _after_
671
+ `runtime.start()` returns (`packages/cli/src/commands/serve.ts`,
672
+ `packages/verify/src/harness.ts`). So `AutoEnqueuer`'s first subscription-cache
673
+ build reliably preceded the capability it needs, dropped every secret-bearing
674
+ row on what it could see, and nothing re-read until the periodic refresh.
675
+
676
+ `ObjectQL` now reports the registration (`onCryptoProviderChange(listener)`,
677
+ fired after the provider is in place), and the auto-enqueuer subscribes
678
+ **before** its first build and rebuilds the cache when it fires. Re-arming is
679
+ immediate and event-driven — no polling, and no shorter-but-still-present
680
+ window. The re-arm deliberately does not join an in-flight refresh: the build
681
+ most likely running at that moment is the pre-registration one, and joining it
682
+ would report success having re-armed nothing.
683
+
684
+ The channel is feature-detected, as `resolveSecretField` already was — this
685
+ plugin takes no dependency on `@objectstack/objectql`. An engine without it keeps
686
+ the previous behaviour, with the periodic refresh as the backstop.
687
+
688
+ **The drop is also no longer quiet.** A subscription dropped for an unresolvable
689
+ key now reports at `error` with the consequence and the fix stated in the
690
+ message, and carries an ADR-0112 `code`/`status` pair (`INTERNAL_ERROR`/500) in
691
+ its metadata — the same pair the seeder's refusal for the same cause already
692
+ carried. Per AGENTS.md it is said **once** per outage per webhook rather than
693
+ every refresh cycle, and a webhook that recovers and breaks again is loud again.
694
+
695
+ Unaffected, and verified still true: the secret's bytes appear nowhere in
696
+ `sys_webhook` or in a delivery row, deliveries carry `signature` and never the
697
+ key (#7722), and a delivery whose key exists only as ciphertext after a restart
698
+ still produces the byte-identical HMAC receivers already verify.
699
+
700
+ - 1602949: fix(plugin-webhooks): webhook custom `headers` are encrypted at rest instead of riding `definition_json` in cleartext (#7986)
701
+
702
+ `#7799` moved the webhook **signing secret** out of `sys_webhook.definition_json`
703
+ into an encrypted `signing_secret` column. It did not move the custom **headers**
704
+ map — and `headers` is the ordinary place an `Authorization: Bearer …` goes.
705
+
706
+ `sys_webhook` declares **no `enable` block at all**, so it keeps the full default
707
+ data API: an ordinary `GET /api/v1/data/sys_webhook` handed the whole header map,
708
+ credentials included, to every persona that can read the object. Unlike the
709
+ delivery table's copies, nothing ages this out — the configuration row is
710
+ retained for the life of the webhook.
711
+
712
+ This is a **scope-of-the-original-fix** finding, not a regression: the exposure
713
+ predates `#7799` and nothing that card did made it worse. What was wrong was the
714
+ conclusion a reader would reasonably draw from it — that webhook credentials are
715
+ no longer in a blob.
716
+
717
+ **What changed.** The authored `headers` map now lands in a new
718
+ `sys_webhook.headers_secret` column on the engine's encrypted credential channel,
719
+ exactly as `signing_secret` does: the engine encrypts it into `sys_secret`, the
720
+ row keeps only an opaque `secret:<id>` ref, and every read path returns a mask.
721
+ `definition_json` carries the same envelope minus both credential passengers. The
722
+ auto-enqueuer recovers the map server-side through `engine.resolveSecretField()`
723
+ on the **same** cache refresh that recovers the signing key, and the existing
724
+ boot sweep (`migrateLegacyWebhookSecrets`) now moves already-persisted cleartext
725
+ headers out of the blob in the same single, idempotent update it uses for the
726
+ key.
727
+
728
+ **Nothing about authoring changes.** Authors still write
729
+ `headers: { … }` on `defineWebhook()`, `webhook.zod.ts` is untouched, and every
730
+ authored header is still delivered on the wire byte-for-byte.
731
+
732
+ **The whole map moves, not just the credential-looking entries.** Only some
733
+ entries are credentials and the platform cannot tell which. Guessing from the
734
+ header name (`authorization`, `x-api-key`, …) is fail-**open** on exactly the
735
+ custom spellings — `X-Acme-Token` — most likely to be one, and a heuristic that
736
+ silently passes the header that mattered is worse than none because it reads as
737
+ coverage. Letting the author declare which are sensitive is a change to the
738
+ authoring envelope and belongs to the spec surface. The cost this shape is
739
+ accused of is measured and small: `definition_json` is a raw JSON textarea
740
+ pending a real builder, so what an admin loses is the ability to read back a
741
+ `Content-Type` they typed.
742
+
743
+ **Fail-closed, and symmetric with `#7799`.** With no CryptoProvider the engine
744
+ refuses the write rather than storing cleartext; a stored map that cannot be
745
+ decrypted **drops** the subscription rather than delivering it with its headers
746
+ silently missing. That drop is deliberately the same trade the signing secret
747
+ makes: against an endpoint that does not require the header, a delivery missing
748
+ its `Authorization` **succeeds** while quietly deviating from the configuration
749
+ the author wrote, and nothing records that it went out incomplete. Subscriptions
750
+ dropped this way re-arm on CryptoProvider registration exactly as `#8022` made
751
+ them — the header map is resolved on the same rebuilt cache as the key, so a
752
+ re-arm can never produce a correctly-signed delivery with no headers on it.
753
+
754
+ **This does not close the exposure end to end.** The same headers are still
755
+ written in cleartext to `sys_http_delivery.headers_json` at enqueue time, and
756
+ that table is readable over the data API (`apiMethods: ['get','list']`, 30-day
757
+ retention). Measured after this change: the credential is still recoverable
758
+ there. Closing that half needs a decision outside this package and is tracked on
759
+ #7986; `sys_email.headers_json` (the same shape, on the email delivery row) is
760
+ untouched here for the same reason.
761
+
762
+ - 06306f1: fix(service-messaging): stop persisting webhook HMAC signing secrets on every delivery row (#7722)
763
+
764
+ `sys_http_delivery` carried the caller's `signingSecret` verbatim, once per
765
+ delivery attempt, in a plain `signing_secret` column — and that table is
766
+ readable over the ordinary data API (`GET /api/v1/data/sys_http_delivery`).
767
+ Anyone who could read deliveries recovered the shared key that authenticates
768
+ ObjectStack to the receiver, for **every** subscriber at once. The signature is
769
+ the receiver's only proof of origin, so the blast radius reaches outside the
770
+ deployment: a leaked key mints payloads the receiver accepts as genuine, and
771
+ rotating it means re-coordinating with every receiver operator.
772
+
773
+ **The row now carries the signature, not the key.** A delivery's body is
774
+ decided at enqueue and replayed byte-for-byte by every retry and by
775
+ `redeliver()` — so the HMAC has exactly one correct value for the row's whole
776
+ life. `enqueue()` computes it once from the producer's secret and stores only
777
+ the result (`signature`, `sha256=<hex>`); the secret is consumed and dropped.
778
+ The stored value is what the receiver is handed on the wire anyway and is
779
+ one-way in the key, so reading a delivery row tells you what was sent, not how
780
+ to forge something else.
781
+
782
+ Signing behaviour on the wire is unchanged: `X-Objectstack-Signature` still
783
+ carries `sha256=HMAC-SHA256(raw body, secret)` and verifies against the
784
+ subscriber's secret exactly as before — now pinned by tests that recompute the
785
+ HMAC over the delivered body rather than asserting a header is merely present,
786
+ and by an at-rest guard that byte-scans every column of a real delivery table
787
+ after a real delivery.
788
+
789
+ Producers are unaffected: `enqueueHttp({ …, signingSecret })` keeps its shape
790
+ for both callers (webhook fan-out and the Flow `http` node), and the fix sits at
791
+ the outbox, so both stop writing cleartext.
792
+
793
+ **Upgrading.** The `signing_secret` column is no longer declared, so an existing
794
+ database keeps it as an unmapped column holding the old cleartext until it is
795
+ dropped: run `os migrate plan` and apply the reported `drop_column` op (it is
796
+ classified destructive, so it is never applied unattended). Until then those
797
+ rows also age out on the table's existing 30-day telemetry retention. Rotate any
798
+ signing secret that was exposed. Code reading `HttpDelivery.signingSecret` off a
799
+ row should read `signature` instead — the secret is not available there by
800
+ design.
801
+
802
+ - bbe05de: A dropped webhook subscription now leaves a durable record, and no operator action can turn that record into an unsigned delivery (#8069).
803
+
804
+ When the auto-enqueuer cannot decrypt a webhook's signing secret or its custom header map, it drops the subscription rather than delivering unsigned (#7799, #7986). Until now the drop left no `sys_http_delivery` row at all: every matching record change was discarded with nothing an operator reading the delivery table could find. `#8043` made that loud in the logs; it did not make it durable.
805
+
806
+ Each discarded event is now recorded as a `sys_http_delivery` row with `status: dead`, `attempts: 0`, and the cause and remedy in the existing `error` column — so it appears in the object's existing "Failures" view with no new lifecycle state and no migration.
807
+
808
+ The record is unsendable by construction, which is the half that matters:
809
+
810
+ - `redeliver()` refuses any terminal row with `attempts: 0`. Such a row was never sent, so re-sending it would be a **first** delivery — and a parked row carries no HMAC signature, because the secret that would have produced one is exactly what went missing. New error code `DELIVERY_NEVER_SENT` (409 on `POST /api/v1/webhooks/redeliver`).
811
+ - `redeliver()` also consults a producer-registered guard, so a webhook row whose `sys_webhook` subscription was deleted, or whose stored signing secret can no longer be recovered, is refused rather than replayed. A guard whose own lookup fails refuses too.
812
+ - The parked row never carries the authored header map, so a credential is not copied onto a row that will sit out the retention window without ever being sent.
813
+
814
+ Redelivery of a genuine dead-letter is unchanged: the same bytes, the same signature.
815
+
816
+ - bf1ea92: fix(plugin-webhooks): a stored header map that cannot be recovered parks the subscription instead of arming it and delivering the headers MISSING (#8558)
817
+
818
+ A webhook whose `sys_webhook.headers_secret` held a value that did not come back
819
+ as a header map was treated as **authored without custom headers**. The
820
+ subscription armed, every matching record change was delivered, and the entire
821
+ authored map — the ordinary place an `Authorization: Bearer …` goes — was
822
+ silently absent. Nothing logged, nothing dropped, and
823
+ `GET /api/v1/data/sys_webhook` kept reporting `active: true` with the header
824
+ column masked, so both the operator and the Setup UI still read "custom headers
825
+ are configured".
826
+
827
+ Measured end to end against a real engine, what reached the receiver was worse
828
+ than "a delivery with something missing": the request SUCCEEDED
829
+ (`sys_http_delivery.status = 'success'`) carrying a byte-correct
830
+ `X-Objectstack-Signature`. The signature is the receiver's proof the request is
831
+ genuinely ours, so a receiver that authenticates by signature had every reason
832
+ to accept a request that no longer matched the configuration its operator wrote.
833
+ Against an endpoint that requires the credential the result is a 401 nobody
834
+ attributes correctly; against one that does not — a routing `X-Tenant-Id`, an
835
+ `X-Environment: staging` — the delivery is simply wrong and nobody finds out.
836
+
837
+ The cause was one return value carrying two facts. `resolveWebhookHeaders`
838
+ answered `undefined` both for _"the author configured no custom headers"_ —
839
+ legitimate, `headers` is optional on the envelope — and for _"a map is stored
840
+ and did not come back as one"_. Its caller acts on the first reading, so the
841
+ second became the first. This is the sibling of the signing-secret collapse
842
+ (#8542) on the same seam's other credential, and the file's own header comment
843
+ already promised the opposite: _"It does not deliver partially. A row whose
844
+ stored headers cannot be resolved DROPS the subscription."_
845
+
846
+ **This path is wider than the signing-secret one, not symmetric to it.** A
847
+ signing secret is an opaque scalar, so any non-empty answer is a usable key and
848
+ only the empty string collapsed. A header map's CONTENT decides, and
849
+ `parseStoredHeaders` answers `undefined` — correctly, for its own job — for every
850
+ string that is not a flat JSON object of string values. Four states reach the
851
+ seam, all confirmed against a real engine:
852
+
853
+ - the `sys_webhook` row is deleted between the enqueuer's cache read and the
854
+ per-row dereference;
855
+ - the column holds something that is not a `secret:` ref — reachable only
856
+ through a write that bypasses the engine (a column edited in SQL, a dump
857
+ restored without its `sys_secret` rows, a seed script writing at driver level);
858
+ - the stored value decrypts to an **empty string**;
859
+ - the stored value decrypts to a perfectly readable string that is **not a flat
860
+ string map** — `{}`, `[]`, `{"X-Count": 5}`, a nested object, or any typo.
861
+ This is the widest road rather than an exotic one: `headers_secret` is an
862
+ admin-authorable field whose own description instructs the author to type a
863
+ JSON object into it, and every one of these spellings is accepted by the
864
+ ordinary data API, encrypted like any other value, and left behind a
865
+ perfectly valid ref that reads back as the mask.
866
+
867
+ The fix is at the seam, so no consumer has to re-derive the rule: presence is
868
+ already decidable there (`headers_secret` is a map only in the plaintext — at
869
+ the storage layer it is an ordinary scalar `secret` column, so a set map comes
870
+ back from the generic read path as the engine's mask and an unset one as `null`),
871
+ and stored headers that do not come back as a map now raise rather than
872
+ answering `undefined`. They therefore reach `AutoEnqueuer.attachHeaders` exactly
873
+ the way a throwing resolver already did — the subscription is parked, the
874
+ discarded event lands in `sys_http_delivery` with a cause (#8069), and the
875
+ operator gets the existing remedy-bearing say-once `error` carrying
876
+ `INTERNAL_ERROR` / `500` (ADR-0112) and naming `headers_secret`, so it cannot be
877
+ confused with the signing secret's identical-looking drop.
878
+
879
+ **Unchanged:** a webhook authored with no custom headers at all still arms and
880
+ delivers — that is a legitimate authored configuration, and it is pinned as the
881
+ control for this change, as is a webhook whose stored map resolves normally and
882
+ still delivers every header including the credential entry.
883
+
884
+ **What an operator sees after upgrading.** A webhook that was quietly delivering
885
+ without its headers stops delivering and starts reporting. Re-save the headers
886
+ as a flat JSON object of string values so the column holds a fresh ref, or
887
+ **clear** the field to `null` if the webhook is meant to send no custom headers
888
+ — an empty or unparseable header map is not the same thing as no header map,
889
+ and only the second one means "send nothing extra".
890
+
891
+ - 719a21b: fix(plugin-webhooks): a stored signing secret that cannot be recovered parks the subscription instead of arming it and delivering UNSIGNED (#8542)
892
+
893
+ A webhook whose `sys_webhook.signing_secret` held a value that did not resolve
894
+ was treated as **authored unsigned**. The subscription armed, every matching
895
+ record change was delivered, and the HMAC signature — the receiver's only proof
896
+ the delivery came from us — was silently absent. Nothing logged, nothing
897
+ dropped, and `GET /api/v1/data/sys_webhook` kept reporting `active: true` with
898
+ the secret column masked, so both the operator and the Setup UI still read
899
+ "this webhook is signed".
900
+
901
+ The cause was one return value carrying two facts. `resolveWebhookSecret`
902
+ answered `undefined` both for _"the author configured this webhook unsigned"_ —
903
+ legitimate, `secret` is optional on the envelope — and for _"a key is stored and
904
+ nothing came back"_. Its caller acts on the first reading, so the second became
905
+ the first. That is the #7799 signing invariant failing **open**, immediately
906
+ beside two adjacent failure modes that fail closed and loudly: a resolver that
907
+ throws, and an engine with no encrypted-field channel, both of which drop the
908
+ subscription and report at `error`.
909
+
910
+ Three states reach the silent path, all confirmed against a real engine:
911
+
912
+ - the `sys_webhook` row is deleted between the dispatcher's cache read and the
913
+ per-row dereference;
914
+ - the column holds something that is not a `secret:` ref — reachable only
915
+ through a write that bypasses the engine (a column edited in SQL, a dump
916
+ restored without its `sys_secret` rows, a seed script writing at driver
917
+ level). The engine's own write path defends the two obvious routes: an echoed
918
+ read-mask is dropped and cleartext is re-encrypted;
919
+ - the stored value decrypts to an **empty string** — reachable through the
920
+ ordinary data API, which accepts `signing_secret: ""`, encrypts it like any
921
+ other value, and leaves the column holding a perfectly valid ref.
922
+
923
+ The fix is at the seam, so no consumer has to re-derive the rule: presence is
924
+ already decidable there (a set secret comes back from the generic read path as
925
+ the engine's mask, an unset one as `null`), and a stored key that does not
926
+ resolve now raises rather than answering `undefined`. It therefore reaches
927
+ `AutoEnqueuer.attachSecret` exactly the way a throwing resolver already did —
928
+ the subscription is parked, the discarded event lands in `sys_http_delivery`
929
+ with a cause (#8069), and the operator gets the existing remedy-bearing
930
+ say-once `error` carrying `INTERNAL_ERROR` / `500` (ADR-0112).
931
+
932
+ **Unchanged:** a webhook authored with no secret at all still arms and delivers
933
+ unsigned — that is a legitimate authored configuration, and it is pinned as the
934
+ control for this change. The redelivery guard (#8069) keeps its behaviour in
935
+ both directions: a stored-but-unresolvable key is still refused with its own
936
+ reason, and any other failure still propagates, because "we could not check"
937
+ must never read as "allowed".
938
+
939
+ **What an operator sees after upgrading.** A webhook that was quietly delivering
940
+ unsigned stops delivering and starts reporting. If the deliveries were meant to
941
+ be signed, re-save the secret so the column holds a fresh ref. If the webhook
942
+ was meant to be unsigned, **clear** the field to `null` — an empty secret is not
943
+ the same thing as no secret, and only the second one means "unsigned".
944
+
945
+ - e3a6f6e: fix(webhooks): the subscriber's HMAC signing secret is no longer readable from `sys_webhook` over the data API (#7799)
946
+
947
+ `bootstrapDeclaredWebhooks` persisted the whole validated `Webhook` envelope —
948
+ **`secret` included** — as `definition_json: JSON.stringify(wh)`, and
949
+ `AutoEnqueuer.parseRow` read `defn.secret` straight back out to sign deliveries.
950
+ `definition_json` is an ordinary textarea on an admin-authorable object with no
951
+ restrictive `enable.apiMethods`, so a plain `GET /api/v1/data/sys_webhook`
952
+ returned the key to **every persona that can read the object**. That key is the
953
+ receiver's only proof that a delivery came from us.
954
+
955
+ This is the remaining half of #7722, which removed the same secret's per-attempt
956
+ copies from `sys_http_delivery`. Unlike the delivery table, no retention window
957
+ ever aged these out.
958
+
959
+ **What changed.** The authored key now lands in `sys_webhook.signing_secret`, a
960
+ new `type: 'secret'` column: the engine encrypts it into `sys_secret` on write,
961
+ keeps only an opaque `secret:<id>` ref on the row, and returns a mask on every
962
+ read path. `definition_json` carries the same envelope **minus** `secret`. The
963
+ auto-enqueuer recovers the plaintext server-side when it refreshes its
964
+ subscription cache.
965
+
966
+ **Nothing about authoring changes.** `packages/spec/src/automation/webhook.zod.ts`
967
+ is untouched — `defineWebhook({ secret })` is written exactly as before, and the
968
+ delivered `X-Objectstack-Signature` is byte-identical, so no receiver has to
969
+ change anything.
970
+
971
+ **Existing rows are migrated.** A boot sweep moves any cleartext
972
+ `definition_json.secret` into the encrypted column — including the rows the
973
+ seeder deliberately never rewrites (`managed_by: 'admin'`, and package rows an
974
+ admin froze with `customized: true`), which are the ones most likely to hold a
975
+ real production key. The sweep is idempotent and stores the encrypted copy in
976
+ the same update that strips the blob, so a failure can never leave a webhook
977
+ stripped _and_ unsigned. Until a row is swept, signing keeps working from the
978
+ legacy blob and the enqueuer warns that the value is still exposed.
979
+
980
+ **Fail-closed.** With no `ICryptoProvider` wired the engine refuses the write
981
+ rather than storing cleartext, so a secret-bearing webhook is skipped — and a
982
+ legacy row is left intact — with an actionable log line carrying an ADR-0112
983
+ `code`/`status` pair. It is never seeded with an exposed key in a new column.
984
+
985
+ Also adds `ObjectQL.resolveSecretField(object, recordId, field)` — the privileged,
986
+ driver-level dereference of one row's `secret`-typed field. `resolveSecret()` was
987
+ already documented for "privileged consumers … against the stored ref", but the
988
+ read mask meant no consumer could obtain that ref; this is why the webhook key
989
+ can live in the encrypted channel at all. It refuses any field not declared
990
+ `type: 'secret'`, so it cannot become a mask bypass over a `password` field
991
+ (plaintext at rest by design — ADR-0100).
992
+
993
+ - c95ac80: chore(plugin-webhooks): drop the dead sys_webhook_delivery i18n blocks
994
+
995
+ `sys_webhook_delivery` was removed from `@objectstack/plugin-webhooks` when
996
+ outbound delivery moved to `@objectstack/service-messaging` (`sys_http_delivery`,
997
+ ADR-0018 M3), but its translation blocks lingered in all four generated locale
998
+ bundles (en / zh-CN / ja-JP / es-ES) — loaded at runtime yet referenced by
999
+ nothing, since the object no longer exists in this plugin.
1000
+
1001
+ - Removed the `sys_webhook_delivery` node from each `*.objects.generated.ts`
1002
+ bundle; `WebhooksTranslations` now carries only `sys_webhook`.
1003
+ - Corrected the stale ownership comment on `SysWebhook` that still named
1004
+ `sys_webhook_delivery` as a live sibling.
1005
+
1006
+ (The dangling `SysWebhookDelivery` import in `scripts/i18n-extract.config.ts`
1007
+ was fixed independently on `main` by #3489, so it is not part of this change.)
1008
+
1009
+ - Updated dependencies [50616d9]
1010
+ - Updated dependencies [430dcc2]
1011
+ - Updated dependencies [690ccf2]
1012
+ - Updated dependencies [6a67d7a]
1013
+ - Updated dependencies [333a374]
1014
+ - Updated dependencies [9fe9c1d]
1015
+ - Updated dependencies [3d5c090]
1016
+ - Updated dependencies [e5bd768]
1017
+ - Updated dependencies [08b5a3d]
1018
+ - Updated dependencies [e027b3e]
1019
+ - Updated dependencies [e6ac4bd]
1020
+ - Updated dependencies [c2429b0]
1021
+ - Updated dependencies [445a0c2]
1022
+ - Updated dependencies [d99aeb3]
1023
+ - Updated dependencies [f6609e6]
1024
+ - Updated dependencies [4727eb8]
1025
+ - Updated dependencies [a70358a]
1026
+ - Updated dependencies [0ecc656]
1027
+ - Updated dependencies [06772eb]
1028
+ - Updated dependencies [d4e0809]
1029
+ - Updated dependencies [80334c7]
1030
+ - Updated dependencies [f63cd09]
1031
+ - Updated dependencies [97e7e3c]
1032
+ - Updated dependencies [ce5242c]
1033
+ - Updated dependencies [a7163ea]
1034
+ - Updated dependencies [e6e9379]
1035
+ - Updated dependencies [5823d59]
1036
+ - Updated dependencies [3140f9c]
1037
+ - Updated dependencies [9500ba4]
1038
+ - Updated dependencies [fa3d0cf]
1039
+ - Updated dependencies [af5a224]
1040
+ - Updated dependencies [71f76e1]
1041
+ - Updated dependencies [37b1346]
1042
+ - Updated dependencies [99736a0]
1043
+ - Updated dependencies [fe67e34]
1044
+ - Updated dependencies [fdb4f50]
1045
+ - Updated dependencies [270650f]
1046
+ - Updated dependencies [3aef718]
1047
+ - Updated dependencies [1bd5652]
1048
+ - Updated dependencies [14252d3]
1049
+ - Updated dependencies [7fb436c]
1050
+ - Updated dependencies [879ea13]
1051
+ - Updated dependencies [8828b9e]
1052
+ - Updated dependencies [1ea6bce]
1053
+ - Updated dependencies [c1dcacd]
1054
+ - Updated dependencies [ad303ed]
1055
+ - Updated dependencies [32ccb23]
1056
+ - Updated dependencies [f5a4ef0]
1057
+ - Updated dependencies [2d3e255]
1058
+ - Updated dependencies [a8940e4]
1059
+ - Updated dependencies [7d7521f]
1060
+ - Updated dependencies [5dc4d02]
1061
+ - Updated dependencies [f724f69]
1062
+ - Updated dependencies [98877c9]
1063
+ - Updated dependencies [98877c9]
1064
+ - Updated dependencies [53068c1]
1065
+ - Updated dependencies [ee58392]
1066
+ - Updated dependencies [f16e54e]
1067
+ - Updated dependencies [06be54e]
1068
+ - Updated dependencies [28ad90e]
1069
+ - Updated dependencies [76d74ec]
1070
+ - Updated dependencies [201b31f]
1071
+ - Updated dependencies [e6b1b69]
1072
+ - Updated dependencies [259459d]
1073
+ - Updated dependencies [3f7f14e]
1074
+ - Updated dependencies [e2616e0]
1075
+ - Updated dependencies [6fdc5c6]
1076
+ - Updated dependencies [8b9d71e]
1077
+ - Updated dependencies [05154a1]
1078
+ - Updated dependencies [33f5e23]
1079
+ - Updated dependencies [259af21]
1080
+ - Updated dependencies [f8644c7]
1081
+ - Updated dependencies [306ca50]
1082
+ - Updated dependencies [978fed2]
1083
+ - Updated dependencies [cfc293f]
1084
+ - Updated dependencies [587fc91]
1085
+ - Updated dependencies [de70b42]
1086
+ - Updated dependencies [9b6fe7c]
1087
+ - Updated dependencies [fb3d99b]
1088
+ - Updated dependencies [1986594]
1089
+ - Updated dependencies [6968885]
1090
+ - Updated dependencies [eaed61f]
1091
+ - Updated dependencies [cdfbee2]
1092
+ - Updated dependencies [ad4af62]
1093
+ - Updated dependencies [debe2f6]
1094
+ - Updated dependencies [d44dbfa]
1095
+ - Updated dependencies [29c6c9d]
1096
+ - Updated dependencies [d21c001]
1097
+ - Updated dependencies [ad047d2]
1098
+ - Updated dependencies [8c711fb]
1099
+ - Updated dependencies [f1cc3a3]
1100
+ - Updated dependencies [09e4547]
1101
+ - Updated dependencies [97b0798]
1102
+ - Updated dependencies [474fe39]
1103
+ - Updated dependencies [0bc685a]
1104
+ - Updated dependencies [b949059]
1105
+ - Updated dependencies [2826d1e]
1106
+ - Updated dependencies [be1c52c]
1107
+ - Updated dependencies [c5ff96d]
1108
+ - Updated dependencies [5a84d41]
1109
+ - Updated dependencies [84e7be9]
1110
+ - Updated dependencies [91f4c78]
1111
+ - Updated dependencies [ddc2527]
1112
+ - Updated dependencies [820eff9]
1113
+ - Updated dependencies [a6c3f38]
1114
+ - Updated dependencies [debc23a]
1115
+ - Updated dependencies [0f8ad09]
1116
+ - Updated dependencies [553a47f]
1117
+ - Updated dependencies [43a7a8d]
1118
+ - Updated dependencies [a98085f]
1119
+ - Updated dependencies [20b1a9e]
1120
+ - Updated dependencies [344a22a]
1121
+ - Updated dependencies [4827e91]
1122
+ - Updated dependencies [8d895ff]
1123
+ - Updated dependencies [86f7a20]
1124
+ - Updated dependencies [a3a884d]
1125
+ - Updated dependencies [cfed092]
1126
+ - Updated dependencies [203a449]
1127
+ - Updated dependencies [8f9689f]
1128
+ - Updated dependencies [73f69dc]
1129
+ - Updated dependencies [04c56aa]
1130
+ - Updated dependencies [f6472d7]
1131
+ - Updated dependencies [57a3bb3]
1132
+ - Updated dependencies [b3efeb7]
1133
+ - Updated dependencies [ddd075a]
1134
+ - Updated dependencies [88154be]
1135
+ - Updated dependencies [e8dc61e]
1136
+ - Updated dependencies [9c82146]
1137
+ - Updated dependencies [5f9a987]
1138
+ - Updated dependencies [744b8f5]
1139
+ - Updated dependencies [ac37fc6]
1140
+ - Updated dependencies [2f3e793]
1141
+ - Updated dependencies [4820f55]
1142
+ - Updated dependencies [462d9c4]
1143
+ - Updated dependencies [78caf51]
1144
+ - Updated dependencies [7d21581]
1145
+ - Updated dependencies [37785ed]
1146
+ - Updated dependencies [62a789b]
1147
+ - Updated dependencies [2e284b2]
1148
+ - Updated dependencies [d8e8d9c]
1149
+ - Updated dependencies [789ad63]
1150
+ - Updated dependencies [f2445c9]
1151
+ - Updated dependencies [94e749b]
1152
+ - Updated dependencies [ea1d916]
1153
+ - Updated dependencies [2af1988]
1154
+ - Updated dependencies [0af50a3]
1155
+ - Updated dependencies [1b49eaf]
1156
+ - Updated dependencies [ae31a19]
1157
+ - Updated dependencies [2e836de]
1158
+ - Updated dependencies [e0f300b]
1159
+ - Updated dependencies [0161c7f]
1160
+ - Updated dependencies [e900015]
1161
+ - Updated dependencies [db02d47]
1162
+ - Updated dependencies [b5bdf48]
1163
+ - Updated dependencies [23338c3]
1164
+ - Updated dependencies [12a19a8]
1165
+ - Updated dependencies [5b843fb]
1166
+ - Updated dependencies [62b6a2f]
1167
+ - Updated dependencies [7e5af5c]
1168
+ - Updated dependencies [5b4780b]
1169
+ - Updated dependencies [a933452]
1170
+ - Updated dependencies [9d1d9c7]
1171
+ - Updated dependencies [8140915]
1172
+ - Updated dependencies [a019e52]
1173
+ - Updated dependencies [e8f8f6c]
1174
+ - Updated dependencies [41dcda3]
1175
+ - Updated dependencies [7b48cf9]
1176
+ - Updated dependencies [b5404f4]
1177
+ - Updated dependencies [64fc6d5]
1178
+ - Updated dependencies [b746aa0]
1179
+ - Updated dependencies [b4487aa]
1180
+ - Updated dependencies [1007379]
1181
+ - Updated dependencies [65ca83a]
1182
+ - Updated dependencies [0bfdf46]
1183
+ - Updated dependencies [947d4f9]
1184
+ - Updated dependencies [f764691]
1185
+ - Updated dependencies [e120a5a]
1186
+ - Updated dependencies [e5bd2f6]
1187
+ - Updated dependencies [e650d67]
1188
+ - Updated dependencies [04476e7]
1189
+ - Updated dependencies [67bf2e2]
1190
+ - Updated dependencies [eaaf03c]
1191
+ - Updated dependencies [d17df80]
1192
+ - Updated dependencies [7d0e7b5]
1193
+ - Updated dependencies [c6d1cb4]
1194
+ - Updated dependencies [6513c17]
1195
+ - Updated dependencies [36030ff]
1196
+ - Updated dependencies [79228cd]
1197
+ - Updated dependencies [6117f7b]
1198
+ - Updated dependencies [e533b0b]
1199
+ - Updated dependencies [cdf4d9a]
1200
+ - Updated dependencies [aee1806]
1201
+ - Updated dependencies [c13350b]
1202
+ - Updated dependencies [c13350b]
1203
+ - Updated dependencies [2c1988c]
1204
+ - Updated dependencies [9ca2d85]
1205
+ - Updated dependencies [c13350b]
1206
+ - Updated dependencies [891d345]
1207
+ - Updated dependencies [c8124e5]
1208
+ - Updated dependencies [a52e2ef]
1209
+ - Updated dependencies [5293114]
1210
+ - Updated dependencies [376a061]
1211
+ - Updated dependencies [c142ced]
1212
+ - Updated dependencies [211abdb]
1213
+ - Updated dependencies [b3363e9]
1214
+ - Updated dependencies [eda599e]
1215
+ - Updated dependencies [a1a4140]
1216
+ - Updated dependencies [7c7e246]
1217
+ - Updated dependencies [2ef1807]
1218
+ - Updated dependencies [c519533]
1219
+ - Updated dependencies [f9a5c59]
1220
+ - Updated dependencies [f35cdc5]
1221
+ - Updated dependencies [d03fe25]
1222
+ - Updated dependencies [217e2e6]
1223
+ - Updated dependencies [2672f85]
1224
+ - Updated dependencies [20bc357]
1225
+ - Updated dependencies [11066f6]
1226
+ - Updated dependencies [916af17]
1227
+ - Updated dependencies [84c86fb]
1228
+ - Updated dependencies [2a2a9fb]
1229
+ - Updated dependencies [86a71d1]
1230
+ - Updated dependencies [c001422]
1231
+ - Updated dependencies [77022a9]
1232
+ - Updated dependencies [d5c75e2]
1233
+ - Updated dependencies [03d26f7]
1234
+ - Updated dependencies [5966c2a]
1235
+ - Updated dependencies [2382580]
1236
+ - Updated dependencies [9ea2bc5]
1237
+ - Updated dependencies [a2e157c]
1238
+ - Updated dependencies [95c4227]
1239
+ - Updated dependencies [2a61116]
1240
+ - Updated dependencies [52760bf]
1241
+ - Updated dependencies [5543020]
1242
+ - Updated dependencies [880d343]
1243
+ - Updated dependencies [6e82972]
1244
+ - Updated dependencies [d4df105]
1245
+ - Updated dependencies [4615a18]
1246
+ - Updated dependencies [f505689]
1247
+ - Updated dependencies [d9fa683]
1248
+ - Updated dependencies [606d577]
1249
+ - Updated dependencies [4384921]
1250
+ - Updated dependencies [e2798fa]
1251
+ - Updated dependencies [3c628ce]
1252
+ - Updated dependencies [c2d9098]
1253
+ - Updated dependencies [0fd8556]
1254
+ - Updated dependencies [3c7bcc0]
1255
+ - Updated dependencies [4b6cac7]
1256
+ - Updated dependencies [7631964]
1257
+ - Updated dependencies [ac471a0]
1258
+ - Updated dependencies [60ae58e]
1259
+ - Updated dependencies [7f62706]
1260
+ - Updated dependencies [667fa44]
1261
+ - Updated dependencies [37e38d1]
1262
+ - Updated dependencies [e906126]
1263
+ - Updated dependencies [ce92674]
1264
+ - Updated dependencies [08363a0]
1265
+ - Updated dependencies [444de5b]
1266
+ - Updated dependencies [a227ed7]
1267
+ - Updated dependencies [7cb922e]
1268
+ - Updated dependencies [1d22114]
1269
+ - Updated dependencies [1eb13a0]
1270
+ - Updated dependencies [c52e608]
1271
+ - Updated dependencies [9613396]
1272
+ - Updated dependencies [3f7b4ff]
1273
+ - Updated dependencies [74155c7]
1274
+ - Updated dependencies [b5f9397]
1275
+ - Updated dependencies [ed77493]
1276
+ - Updated dependencies [6908830]
1277
+ - Updated dependencies [8b06bba]
1278
+ - Updated dependencies [58a03d2]
1279
+ - Updated dependencies [2bacd1a]
1280
+ - Updated dependencies [e47b342]
1281
+ - Updated dependencies [4c54037]
1282
+ - Updated dependencies [dc530b4]
1283
+ - Updated dependencies [9f601e8]
1284
+ - Updated dependencies [6a9dec6]
1285
+ - Updated dependencies [0f7157b]
1286
+ - Updated dependencies [4dc1c7d]
1287
+ - Updated dependencies [d9bef45]
1288
+ - Updated dependencies [4dfd002]
1289
+ - Updated dependencies [f549a0d]
1290
+ - Updated dependencies [51c5227]
1291
+ - Updated dependencies [82da264]
1292
+ - Updated dependencies [f586f1a]
1293
+ - Updated dependencies [77be690]
1294
+ - Updated dependencies [4ed7ed4]
1295
+ - Updated dependencies [9b9b70f]
1296
+ - Updated dependencies [f5a9bc2]
1297
+ - Updated dependencies [e59786e]
1298
+ - Updated dependencies [2fa4ca1]
1299
+ - Updated dependencies [bcf1112]
1300
+ - Updated dependencies [baeb4f0]
1301
+ - Updated dependencies [29488cc]
1302
+ - Updated dependencies [881a3cc]
1303
+ - Updated dependencies [f5a2320]
1304
+ - Updated dependencies [ad6317b]
1305
+ - Updated dependencies [811c30c]
1306
+ - Updated dependencies [a4a85c8]
1307
+ - Updated dependencies [859cb83]
1308
+ - Updated dependencies [07a4e26]
1309
+ - Updated dependencies [9774b78]
1310
+ - Updated dependencies [8a88885]
1311
+ - Updated dependencies [deb538f]
1312
+ - Updated dependencies [b49ccfd]
1313
+ - Updated dependencies [5b89711]
1314
+ - Updated dependencies [85d95e7]
1315
+ - Updated dependencies [08cd163]
1316
+ - Updated dependencies [0c8a22f]
1317
+ - Updated dependencies [5f7669e]
1318
+ - Updated dependencies [becbe53]
1319
+ - Updated dependencies [b127c8b]
1320
+ - Updated dependencies [763931e]
1321
+ - Updated dependencies [ec975f1]
1322
+ - Updated dependencies [168f60f]
1323
+ - Updated dependencies [b07d829]
1324
+ - Updated dependencies [de9af8a]
1325
+ - Updated dependencies [eb4204b]
1326
+ - Updated dependencies [a80302a]
1327
+ - Updated dependencies [a648e96]
1328
+ - Updated dependencies [a47ac06]
1329
+ - Updated dependencies [e4c61a7]
1330
+ - Updated dependencies [cc60165]
1331
+ - Updated dependencies [474f131]
1332
+ - Updated dependencies [081aa6f]
1333
+ - Updated dependencies [91f4c78]
1334
+ - Updated dependencies [050cd82]
1335
+ - Updated dependencies [4d552af]
1336
+ - Updated dependencies [44d677c]
1337
+ - Updated dependencies [c32944d]
1338
+ - Updated dependencies [1dd780f]
1339
+ - Updated dependencies [e8d0c21]
1340
+ - Updated dependencies [244ca86]
1341
+ - Updated dependencies [546ab3c]
1342
+ - Updated dependencies [c4df271]
1343
+ - Updated dependencies [c8d6f6e]
1344
+ - Updated dependencies [0b51bb6]
1345
+ - Updated dependencies [d9971d3]
1346
+ - Updated dependencies [7dc1067]
1347
+ - Updated dependencies [4f13be2]
1348
+ - Updated dependencies [a41ba5c]
1349
+ - Updated dependencies [189854c]
1350
+ - Updated dependencies [0e3a226]
1351
+ - Updated dependencies [92a67f2]
1352
+ - Updated dependencies [9136327]
1353
+ - Updated dependencies [bf0ae99]
1354
+ - Updated dependencies [eb3e650]
1355
+ - Updated dependencies [abeb375]
1356
+ - Updated dependencies [cb3b6cd]
1357
+ - Updated dependencies [73b7234]
1358
+ - Updated dependencies [d2b97c3]
1359
+ - Updated dependencies [61cc079]
1360
+ - Updated dependencies [45dc446]
1361
+ - Updated dependencies [0e96e46]
1362
+ - Updated dependencies [c1d44f7]
1363
+ - Updated dependencies [59b794f]
1364
+ - Updated dependencies [ef4efa8]
1365
+ - Updated dependencies [cbb6a5c]
1366
+ - Updated dependencies [fc3a36a]
1367
+ - Updated dependencies [ab9fb5c]
1368
+ - Updated dependencies [69787f0]
1369
+ - Updated dependencies [5d022a1]
1370
+ - Updated dependencies [042b9ee]
1371
+ - Updated dependencies [f985b3f]
1372
+ - Updated dependencies [795b6e1]
1373
+ - Updated dependencies [d52d4fe]
1374
+ - Updated dependencies [742cebb]
1375
+ - Updated dependencies [175d789]
1376
+ - Updated dependencies [f549a0d]
1377
+ - Updated dependencies [427344c]
1378
+ - Updated dependencies [8af76ae]
1379
+ - Updated dependencies [1d4756e]
1380
+ - Updated dependencies [720c5ad]
1381
+ - Updated dependencies [a8d1e24]
1382
+ - Updated dependencies [b85cc54]
1383
+ - Updated dependencies [a36db28]
1384
+ - Updated dependencies [7a8476f]
1385
+ - Updated dependencies [518ca7a]
1386
+ - Updated dependencies [41642b0]
1387
+ - Updated dependencies [4cca74c]
1388
+ - Updated dependencies [88ef03e]
1389
+ - Updated dependencies [9a4932a]
1390
+ - Updated dependencies [3f8817a]
1391
+ - Updated dependencies [a2443e3]
1392
+ - Updated dependencies [e1554b1]
1393
+ - Updated dependencies [9e2caf3]
1394
+ - Updated dependencies [4856789]
1395
+ - Updated dependencies [81ce41a]
1396
+ - Updated dependencies [85e1e4e]
1397
+ - Updated dependencies [c3f4916]
1398
+ - Updated dependencies [55dbbba]
1399
+ - Updated dependencies [33e0385]
1400
+ - Updated dependencies [dac6a08]
1401
+ - Updated dependencies [72c3c86]
1402
+ - Updated dependencies [2d8dba3]
1403
+ - Updated dependencies [7f1a635]
1404
+ - Updated dependencies [2205363]
1405
+ - Updated dependencies [09fe58d]
1406
+ - Updated dependencies [f9fc874]
1407
+ - Updated dependencies [d62f8eb]
1408
+ - Updated dependencies [d0a5ceb]
1409
+ - Updated dependencies [a7586cd]
1410
+ - Updated dependencies [4c5e80e]
1411
+ - Updated dependencies [4b5702a]
1412
+ - Updated dependencies [011b386]
1413
+ - Updated dependencies [e18a162]
1414
+ - Updated dependencies [394b7a1]
1415
+ - Updated dependencies [ce92674]
1416
+ - Updated dependencies [0f2fdcd]
1417
+ - Updated dependencies [d6d1a50]
1418
+ - Updated dependencies [cf2c9b7]
1419
+ - Updated dependencies [8ffa8b9]
1420
+ - Updated dependencies [d127ff0]
1421
+ - Updated dependencies [674ac99]
1422
+ - Updated dependencies [833b512]
1423
+ - Updated dependencies [36d90fc]
1424
+ - Updated dependencies [7777e8f]
1425
+ - Updated dependencies [9b86cf6]
1426
+ - Updated dependencies [d063a96]
1427
+ - Updated dependencies [8825a06]
1428
+ - Updated dependencies [5087ac6]
1429
+ - Updated dependencies [677b591]
1430
+ - Updated dependencies [cf7c694]
1431
+ - Updated dependencies [ddd0f06]
1432
+ - Updated dependencies [d77d1b7]
1433
+ - Updated dependencies [0f9faa2]
1434
+ - Updated dependencies [2d1ddf0]
1435
+ - Updated dependencies [354b00f]
1436
+ - Updated dependencies [3de535b]
1437
+ - Updated dependencies [fe2e15a]
1438
+ - Updated dependencies [5b79a34]
1439
+ - Updated dependencies [502564d]
1440
+ - Updated dependencies [603cab8]
1441
+ - Updated dependencies [c757854]
1442
+ - Updated dependencies [471839d]
1443
+ - Updated dependencies [507b92a]
1444
+ - Updated dependencies [46365ab]
1445
+ - Updated dependencies [b508244]
1446
+ - Updated dependencies [df95346]
1447
+ - Updated dependencies [3dede58]
1448
+ - Updated dependencies [c6b6bb4]
1449
+ - Updated dependencies [594508e]
1450
+ - Updated dependencies [7cf42fe]
1451
+ - Updated dependencies [5966c2a]
1452
+ - Updated dependencies [0045682]
1453
+ - Updated dependencies [7309c81]
1454
+ - Updated dependencies [a8dcc37]
1455
+ - Updated dependencies [040ecd2]
1456
+ - Updated dependencies [932d7e2]
1457
+ - Updated dependencies [2f59da0]
1458
+ - Updated dependencies [d56012f]
1459
+ - Updated dependencies [f78dd83]
1460
+ - Updated dependencies [a2cd18a]
1461
+ - Updated dependencies [9051802]
1462
+ - Updated dependencies [20bc1ec]
1463
+ - Updated dependencies [1c625ca]
1464
+ - Updated dependencies [2f8328c]
1465
+ - Updated dependencies [2a6c279]
1466
+ - Updated dependencies [9319586]
1467
+ - Updated dependencies [8c8f0df]
1468
+ - Updated dependencies [8ad609c]
1469
+ - Updated dependencies [bbee302]
1470
+ - Updated dependencies [90c2b15]
1471
+ - Updated dependencies [4638aaa]
1472
+ - Updated dependencies [0222d3c]
1473
+ - Updated dependencies [08863dd]
1474
+ - Updated dependencies [071d0dc]
1475
+ - Updated dependencies [f293d45]
1476
+ - Updated dependencies [56664f5]
1477
+ - Updated dependencies [71f205d]
1478
+ - Updated dependencies [f067930]
1479
+ - Updated dependencies [414395b]
1480
+ - Updated dependencies [42eeb7d]
1481
+ - Updated dependencies [31cbe90]
1482
+ - Updated dependencies [6b7129a]
1483
+ - Updated dependencies [c5adfe1]
1484
+ - Updated dependencies [97ace2a]
1485
+ - Updated dependencies [26e1029]
1486
+ - Updated dependencies [0a936ea]
1487
+ - Updated dependencies [90bbf25]
1488
+ - Updated dependencies [f1850d8]
1489
+ - Updated dependencies [023c00b]
1490
+ - Updated dependencies [eb91eba]
1491
+ - Updated dependencies [17d0954]
1492
+ - Updated dependencies [f28ef3b]
1493
+ - Updated dependencies [42da73d]
1494
+ - Updated dependencies [01e124d]
1495
+ - Updated dependencies [ef7b5ef]
1496
+ - Updated dependencies [9514767]
1497
+ - Updated dependencies [8f20201]
1498
+ - Updated dependencies [155507e]
1499
+ - Updated dependencies [643b7c7]
1500
+ - Updated dependencies [7bba90b]
1501
+ - Updated dependencies [8813b90]
1502
+ - Updated dependencies [108ba8d]
1503
+ - Updated dependencies [2a5f04a]
1504
+ - Updated dependencies [4f740b0]
1505
+ - Updated dependencies [7ce02eb]
1506
+ - Updated dependencies [b4ad984]
1507
+ - Updated dependencies [a9f32df]
1508
+ - Updated dependencies [aeb9b27]
1509
+ - Updated dependencies [7d27da0]
1510
+ - Updated dependencies [d0d5205]
1511
+ - Updated dependencies [1a15893]
1512
+ - Updated dependencies [b70e534]
1513
+ - Updated dependencies [7e05d8e]
1514
+ - Updated dependencies [8f1851e]
1515
+ - Updated dependencies [61ea810]
1516
+ - Updated dependencies [2233a85]
1517
+ - Updated dependencies [67452d1]
1518
+ - Updated dependencies [089767f]
1519
+ - Updated dependencies [a13827e]
1520
+ - Updated dependencies [66d99ec]
1521
+ - Updated dependencies [cb43296]
1522
+ - Updated dependencies [b61afc1]
1523
+ - Updated dependencies [79021fc]
1524
+ - Updated dependencies [7733604]
1525
+ - Updated dependencies [40e420f]
1526
+ - Updated dependencies [62dd69a]
1527
+ - Updated dependencies [d13004a]
1528
+ - Updated dependencies [be7360c]
1529
+ - Updated dependencies [e15e679]
1530
+ - Updated dependencies [2ab1257]
1531
+ - Updated dependencies [0fc6219]
1532
+ - Updated dependencies [061406d]
1533
+ - Updated dependencies [e4c8b6c]
1534
+ - Updated dependencies [acb10f6]
1535
+ - Updated dependencies [605e190]
1536
+ - Updated dependencies [c6c59f1]
1537
+ - Updated dependencies [b0e78a8]
1538
+ - Updated dependencies [f31cc8d]
1539
+ - Updated dependencies [f343dc4]
1540
+ - Updated dependencies [8269e32]
1541
+ - Updated dependencies [74f7339]
1542
+ - Updated dependencies [a6c35a2]
1543
+ - Updated dependencies [c2f1002]
1544
+ - Updated dependencies [4cc4fb7]
1545
+ - Updated dependencies [97b6658]
1546
+ - Updated dependencies [28d1eb7]
1547
+ - Updated dependencies [06770c0]
1548
+ - Updated dependencies [2c26040]
1549
+ - Updated dependencies [f758cec]
1550
+ - Updated dependencies [5b47ab5]
1551
+ - Updated dependencies [b09d8d9]
1552
+ - Updated dependencies [b09d8d9]
1553
+ - Updated dependencies [8675db6]
1554
+ - Updated dependencies [b09d8d9]
1555
+ - Updated dependencies [27358d5]
1556
+ - Updated dependencies [1c3da1f]
1557
+ - Updated dependencies [c1f344b]
1558
+ - Updated dependencies [3eb1b2b]
1559
+ - Updated dependencies [9c93465]
1560
+ - Updated dependencies [a34fd2e]
1561
+ - Updated dependencies [ebb209c]
1562
+ - Updated dependencies [76bcb83]
1563
+ - Updated dependencies [59b85c0]
1564
+ - Updated dependencies [889ae47]
1565
+ - Updated dependencies [4f4c3fb]
1566
+ - Updated dependencies [78f0be8]
1567
+ - Updated dependencies [6e357ed]
1568
+ - Updated dependencies [d6938bf]
1569
+ - Updated dependencies [35f7fb4]
1570
+ - Updated dependencies [0410522]
1571
+ - Updated dependencies [63b33e6]
1572
+ - Updated dependencies [f163028]
1573
+ - Updated dependencies [814db6d]
1574
+ - Updated dependencies [a5302c7]
1575
+ - Updated dependencies [31e0be9]
1576
+ - Updated dependencies [4bfd455]
1577
+ - Updated dependencies [ffd2ce2]
1578
+ - Updated dependencies [2a44c1d]
1579
+ - Updated dependencies [7084313]
1580
+ - Updated dependencies [f07808c]
1581
+ - Updated dependencies [7ffc3d3]
1582
+ - Updated dependencies [88346ba]
1583
+ - Updated dependencies [4631592]
1584
+ - Updated dependencies [62f8017]
1585
+ - Updated dependencies [32ff033]
1586
+ - Updated dependencies [a831df1]
1587
+ - Updated dependencies [f752ee3]
1588
+ - Updated dependencies [a1b61e0]
1589
+ - Updated dependencies [cd6b9f2]
1590
+ - Updated dependencies [2cb6d3c]
1591
+ - Updated dependencies [af2a095]
1592
+ - Updated dependencies [5ac93d4]
1593
+ - Updated dependencies [695cfbd]
1594
+ - Updated dependencies [0e043d8]
1595
+ - Updated dependencies [93f267f]
1596
+ - Updated dependencies [7445149]
1597
+ - Updated dependencies [ec796d5]
1598
+ - Updated dependencies [071d0dc]
1599
+ - Updated dependencies [0024abf]
1600
+ - Updated dependencies [8dd98bf]
1601
+ - Updated dependencies [e87fea1]
1602
+ - Updated dependencies [c65e529]
1603
+ - Updated dependencies [0848bea]
1604
+ - Updated dependencies [d51bed2]
1605
+ - Updated dependencies [dadd1ad]
1606
+ - Updated dependencies [acbf364]
1607
+ - Updated dependencies [3ca34c1]
1608
+ - Updated dependencies [7adc841]
1609
+ - Updated dependencies [239c3a3]
1610
+ - Updated dependencies [b8b3c64]
1611
+ - Updated dependencies [2f2e63c]
1612
+ - Updated dependencies [4845f85]
1613
+ - Updated dependencies [486d526]
1614
+ - Updated dependencies [94a0bbc]
1615
+ - Updated dependencies [d6bfb3d]
1616
+ - Updated dependencies [8a9c079]
1617
+ - Updated dependencies [7b005b4]
1618
+ - Updated dependencies [cc3555e]
1619
+ - Updated dependencies [a2266a6]
1620
+ - Updated dependencies [d25a0ec]
1621
+ - Updated dependencies [89d7b35]
1622
+ - Updated dependencies [94f7b6a]
1623
+ - Updated dependencies [5c94f83]
1624
+ - Updated dependencies [ea936f3]
1625
+ - Updated dependencies [0c0fbd9]
1626
+ - Updated dependencies [667b83e]
1627
+ - Updated dependencies [f3141d8]
1628
+ - Updated dependencies [7687f7b]
1629
+ - Updated dependencies [5a84d41]
1630
+ - Updated dependencies [fd3013a]
1631
+ - Updated dependencies [85ec26d]
1632
+ - Updated dependencies [73e576f]
1633
+ - Updated dependencies [f6476fc]
1634
+ - Updated dependencies [69ac82c]
1635
+ - Updated dependencies [4ac12ef]
1636
+ - Updated dependencies [833ed84]
1637
+ - Updated dependencies [a18abf3]
1638
+ - Updated dependencies [c6a4eeb]
1639
+ - Updated dependencies [1659072]
1640
+ - Updated dependencies [f450ae7]
1641
+ - Updated dependencies [abceb0d]
1642
+ - Updated dependencies [627b188]
1643
+ - Updated dependencies [8d4eae7]
1644
+ - Updated dependencies [c5a5996]
1645
+ - Updated dependencies [0c302a7]
1646
+ - Updated dependencies [b88f5e8]
1647
+ - Updated dependencies [857a6cf]
1648
+ - Updated dependencies [65a3a84]
1649
+ - Updated dependencies [6633337]
1650
+ - Updated dependencies [21676eb]
1651
+ - Updated dependencies [e9cb9ab]
1652
+ - Updated dependencies [42cc219]
1653
+ - Updated dependencies [d7e0b42]
1654
+ - Updated dependencies [3510e4a]
1655
+ - Updated dependencies [f00d8d4]
1656
+ - Updated dependencies [5326b36]
1657
+ - Updated dependencies [aa4b90d]
1658
+ - Updated dependencies [ccd9397]
1659
+ - Updated dependencies [503be86]
1660
+ - Updated dependencies [54299ca]
1661
+ - Updated dependencies [ae490ef]
1662
+ - Updated dependencies [e124711]
1663
+ - Updated dependencies [dc61def]
1664
+ - Updated dependencies [bca935b]
1665
+ - Updated dependencies [d92c72d]
1666
+ - Updated dependencies [c54c822]
1667
+ - Updated dependencies [8dcc0f5]
1668
+ - Updated dependencies [75b9e51]
1669
+ - Updated dependencies [9c90ea0]
1670
+ - Updated dependencies [f61c8cf]
1671
+ - Updated dependencies [e3ef52b]
1672
+ - Updated dependencies [0a2f233]
1673
+ - Updated dependencies [8621cdd]
1674
+ - Updated dependencies [251e888]
1675
+ - Updated dependencies [07f1822]
1676
+ - Updated dependencies [e336549]
1677
+ - Updated dependencies [3bb9340]
1678
+ - Updated dependencies [1e604c4]
1679
+ - Updated dependencies [04fab5e]
1680
+ - Updated dependencies [183b4c4]
1681
+ - Updated dependencies [7f713b6]
1682
+ - Updated dependencies [d40f43a]
1683
+ - Updated dependencies [2fdb36e]
1684
+ - Updated dependencies [6f23667]
1685
+ - Updated dependencies [cde1975]
1686
+ - Updated dependencies [0bc685a]
1687
+ - Updated dependencies [20526f5]
1688
+ - Updated dependencies [efedd28]
1689
+ - Updated dependencies [5d21a48]
1690
+ - Updated dependencies [5278e11]
1691
+ - Updated dependencies [c5eef1d]
1692
+ - Updated dependencies [e5e7ee0]
1693
+ - Updated dependencies [23dba62]
1694
+ - Updated dependencies [e0f300b]
1695
+ - Updated dependencies [761a0ba]
1696
+ - Updated dependencies [c960170]
1697
+ - Updated dependencies [19365b7]
1698
+ - Updated dependencies [ba98e26]
1699
+ - Updated dependencies [b7ed26d]
1700
+ - Updated dependencies [a2ebea2]
1701
+ - Updated dependencies [800bdb0]
1702
+ - Updated dependencies [9d4dfc4]
1703
+ - Updated dependencies [1059965]
1704
+ - Updated dependencies [def5919]
1705
+ - Updated dependencies [ee264b2]
1706
+ - Updated dependencies [60b672e]
1707
+ - Updated dependencies [6b441a8]
1708
+ - Updated dependencies [ce0cfe9]
1709
+ - Updated dependencies [04f1182]
1710
+ - Updated dependencies [be87153]
1711
+ - Updated dependencies [dd0f681]
1712
+ - Updated dependencies [60f0dd8]
1713
+ - Updated dependencies [a87c5cd]
1714
+ - Updated dependencies [a47f338]
1715
+ - Updated dependencies [b3a3d83]
1716
+ - Updated dependencies [7a55913]
1717
+ - Updated dependencies [35accbf]
1718
+ - Updated dependencies [6038de7]
1719
+ - Updated dependencies [fc5f536]
1720
+ - Updated dependencies [5647006]
1721
+ - Updated dependencies [e654bfd]
1722
+ - Updated dependencies [01a7337]
1723
+ - Updated dependencies [7e4783f]
1724
+ - Updated dependencies [b45c71e]
1725
+ - Updated dependencies [f8cfbb4]
1726
+ - Updated dependencies [6e6c872]
1727
+ - Updated dependencies [2598216]
1728
+ - Updated dependencies [11949fc]
1729
+ - Updated dependencies [2c7e62d]
1730
+ - Updated dependencies [eb95d97]
1731
+ - Updated dependencies [b098b0e]
1732
+ - Updated dependencies [4d00b13]
1733
+ - Updated dependencies [1363084]
1734
+ - Updated dependencies [fa5758e]
1735
+ - Updated dependencies [38f7e4f]
1736
+ - Updated dependencies [eb7613c]
1737
+ - Updated dependencies [c57f3cf]
1738
+ - Updated dependencies [ecc9110]
1739
+ - Updated dependencies [e4c2dc8]
1740
+ - Updated dependencies [97faca3]
1741
+ - Updated dependencies [57bab76]
1742
+ - Updated dependencies [c89d18c]
1743
+ - Updated dependencies [1bd2795]
1744
+ - Updated dependencies [f7bd4e2]
1745
+ - Updated dependencies [361bd5b]
1746
+ - Updated dependencies [aac90a5]
1747
+ - Updated dependencies [3da3da5]
1748
+ - Updated dependencies [1e6ab15]
1749
+ - Updated dependencies [b90086a]
1750
+ - Updated dependencies [8186a70]
1751
+ - Updated dependencies [a329cca]
1752
+ - Updated dependencies [c87ef70]
1753
+ - Updated dependencies [3cb0618]
1754
+ - Updated dependencies [32a0874]
1755
+ - Updated dependencies [6eec18c]
1756
+ - Updated dependencies [4d7bebf]
1757
+ - Updated dependencies [821ac7a]
1758
+ - Updated dependencies [8f81731]
1759
+ - Updated dependencies [7055c22]
1760
+ - Updated dependencies [785a748]
1761
+ - Updated dependencies [3af0354]
1762
+ - Updated dependencies [866ff16]
1763
+ - Updated dependencies [5a85e67]
1764
+ - Updated dependencies [8b50cb3]
1765
+ - Updated dependencies [a0fdc56]
1766
+ - Updated dependencies [b95577a]
1767
+ - Updated dependencies [0dcbc11]
1768
+ - Updated dependencies [d88f3e9]
1769
+ - Updated dependencies [ad5fe25]
1770
+ - Updated dependencies [c183a12]
1771
+ - Updated dependencies [83c161f]
1772
+ - Updated dependencies [d8c4957]
1773
+ - Updated dependencies [b9f930b]
1774
+ - Updated dependencies [f24cb83]
1775
+ - Updated dependencies [5dbbb92]
1776
+ - Updated dependencies [ea90179]
1777
+ - Updated dependencies [1818998]
1778
+ - Updated dependencies [ce92674]
1779
+ - Updated dependencies [5ef0b5b]
1780
+ - Updated dependencies [8c2db68]
1781
+ - Updated dependencies [22b5e54]
1782
+ - Updated dependencies [0166bd5]
1783
+ - Updated dependencies [8064b07]
1784
+ - Updated dependencies [09ee21c]
1785
+ - Updated dependencies [4a56dbd]
1786
+ - Updated dependencies [289d04a]
1787
+ - Updated dependencies [f549a0d]
1788
+ - Updated dependencies [48fbacb]
1789
+ - Updated dependencies [06df4fa]
1790
+ - Updated dependencies [3fc2e48]
1791
+ - Updated dependencies [c9b809f]
1792
+ - Updated dependencies [e8f435c]
1793
+ - Updated dependencies [32386f8]
1794
+ - Updated dependencies [9b702dc]
1795
+ - Updated dependencies [ab16331]
1796
+ - Updated dependencies [41610f6]
1797
+ - Updated dependencies [69f1dfd]
1798
+ - Updated dependencies [06306f1]
1799
+ - Updated dependencies [bbe05de]
1800
+ - Updated dependencies [355e951]
1801
+ - Updated dependencies [a1dd1e4]
1802
+ - Updated dependencies [dadb43f]
1803
+ - Updated dependencies [3556b67]
1804
+ - @objectstack/spec@17.0.0
1805
+ - @objectstack/core@17.0.0
1806
+ - @objectstack/service-messaging@17.0.0
1807
+
1808
+ ## 17.0.0-rc.6
1809
+
1810
+ ### Patch Changes
1811
+
1812
+ - 2465133: fix(plugins): sweep the service-lookup erasures out of the plugin composition roots, and fix the two alias-only HTTP reads it exposed (#4251 B5)
1813
+
1814
+ Batch B5 of the #4251 sweep: the seven remaining `packages/plugins/*` composition
1815
+ roots. 35 lookup sites that had been erased to `any` now carry the slot's
1816
+ contract, so the compiler checks what each plugin actually calls on the service
1817
+ it resolved. The ratchet drops 143 sites / 32 files to 108 / 25.
1818
+
1819
+ **Two real defects, both of the shape this sweep exists to find.** Approvals'
1820
+ actionable-link pages (ADR-0043) and sharing's public share-link REST routes each
1821
+ read the HTTP server under `http-server` _only_ — the deprecated alias. The
1822
+ ledger records `http.server` as canonical and as the only name present on every
1823
+ provider path: `runtime.ts`'s `config.server` path registers no alias at all. On
1824
+ that path both lookups threw, the surrounding `catch` swallowed it, and the
1825
+ routes silently never mounted — approval e-mail action links 404'd and the
1826
+ share-link surface was absent, with nothing in the log to say so. Both reads are
1827
+ now canonical-first with the alias as fallback, each name in its own `try`
1828
+ because `getService` throws on an empty slot (so `a() ?? b()` inside one `try`
1829
+ never reaches `b` — the same correction #4393 made in metadata and
1830
+ cloud-connection).
1831
+
1832
+ Typing choices follow the batch method: pure data-plane consumers take the
1833
+ narrow contract (`IDataEngine` in reports), consumers that bind hook or
1834
+ middleware seams take the engine seen whole (`IObjectQLEngine` in approvals,
1835
+ sharing and pinyin-search), and slots with no contract get a **named** local
1836
+ surface rather than `any` — plugin-email's `MailSettingsSurface`, and the
1837
+ surfaces the consuming packages already declared (`ApprovalMessagingSurface`,
1838
+ `SharingSecurityProbe`, `ReportEmail`). A named surface that omits a member
1839
+ still makes the compiler name every call site; `any` says nothing.
1840
+
1841
+ No behaviour change beyond the two alias reads. No contract changes.
1842
+
1843
+ - Updated dependencies [3d5c090]
1844
+ - Updated dependencies [e5bd768]
1845
+ - Updated dependencies [e027b3e]
1846
+ - Updated dependencies [c2429b0]
1847
+ - Updated dependencies [445a0c2]
1848
+ - Updated dependencies [f6609e6]
1849
+ - Updated dependencies [a70358a]
1850
+ - Updated dependencies [97e7e3c]
1851
+ - Updated dependencies [8828b9e]
1852
+ - Updated dependencies [53068c1]
1853
+ - Updated dependencies [ee58392]
1854
+ - Updated dependencies [f16e54e]
1855
+ - Updated dependencies [06be54e]
1856
+ - Updated dependencies [259459d]
1857
+ - Updated dependencies [3f7f14e]
1858
+ - Updated dependencies [6968885]
1859
+ - Updated dependencies [eaed61f]
1860
+ - Updated dependencies [debe2f6]
1861
+ - Updated dependencies [97b0798]
1862
+ - Updated dependencies [43a7a8d]
1863
+ - Updated dependencies [73f69dc]
1864
+ - Updated dependencies [04c56aa]
1865
+ - Updated dependencies [b3efeb7]
1866
+ - Updated dependencies [ddd075a]
1867
+ - Updated dependencies [88154be]
1868
+ - Updated dependencies [e8dc61e]
1869
+ - Updated dependencies [2f3e793]
1870
+ - Updated dependencies [d8e8d9c]
1871
+ - Updated dependencies [94e749b]
1872
+ - Updated dependencies [ea1d916]
1873
+ - Updated dependencies [ae31a19]
1874
+ - Updated dependencies [e0f300b]
1875
+ - Updated dependencies [62b6a2f]
1876
+ - Updated dependencies [5b4780b]
1877
+ - Updated dependencies [a933452]
1878
+ - Updated dependencies [8140915]
1879
+ - Updated dependencies [7b48cf9]
1880
+ - Updated dependencies [b5404f4]
1881
+ - Updated dependencies [f764691]
1882
+ - Updated dependencies [e120a5a]
1883
+ - Updated dependencies [e650d67]
1884
+ - Updated dependencies [04476e7]
1885
+ - Updated dependencies [79228cd]
1886
+ - Updated dependencies [b3363e9]
1887
+ - Updated dependencies [2ef1807]
1888
+ - Updated dependencies [f9a5c59]
1889
+ - Updated dependencies [d03fe25]
1890
+ - Updated dependencies [2672f85]
1891
+ - Updated dependencies [11066f6]
1892
+ - Updated dependencies [916af17]
1893
+ - Updated dependencies [84c86fb]
1894
+ - Updated dependencies [2a2a9fb]
1895
+ - Updated dependencies [a2e157c]
1896
+ - Updated dependencies [95c4227]
1897
+ - Updated dependencies [2a61116]
1898
+ - Updated dependencies [d4df105]
1899
+ - Updated dependencies [e2798fa]
1900
+ - Updated dependencies [0fd8556]
1901
+ - Updated dependencies [74155c7]
1902
+ - Updated dependencies [6908830]
1903
+ - Updated dependencies [8b06bba]
1904
+ - Updated dependencies [4c54037]
1905
+ - Updated dependencies [0f7157b]
1906
+ - Updated dependencies [d9bef45]
1907
+ - Updated dependencies [f549a0d]
1908
+ - Updated dependencies [82da264]
1909
+ - Updated dependencies [f586f1a]
1910
+ - Updated dependencies [9b9b70f]
1911
+ - Updated dependencies [f5a9bc2]
1912
+ - Updated dependencies [881a3cc]
1913
+ - Updated dependencies [ad6317b]
1914
+ - Updated dependencies [8a88885]
1915
+ - Updated dependencies [5f7669e]
1916
+ - Updated dependencies [becbe53]
1917
+ - Updated dependencies [b127c8b]
1918
+ - Updated dependencies [a80302a]
1919
+ - Updated dependencies [474f131]
1920
+ - Updated dependencies [050cd82]
1921
+ - Updated dependencies [4d552af]
1922
+ - Updated dependencies [44d677c]
1923
+ - Updated dependencies [c32944d]
1924
+ - Updated dependencies [1dd780f]
1925
+ - Updated dependencies [c8d6f6e]
1926
+ - Updated dependencies [92a67f2]
1927
+ - Updated dependencies [9136327]
1928
+ - Updated dependencies [bf0ae99]
1929
+ - Updated dependencies [cb3b6cd]
1930
+ - Updated dependencies [73b7234]
1931
+ - Updated dependencies [d2b97c3]
1932
+ - Updated dependencies [59b794f]
1933
+ - Updated dependencies [fc3a36a]
1934
+ - Updated dependencies [69787f0]
1935
+ - Updated dependencies [5d022a1]
1936
+ - Updated dependencies [042b9ee]
1937
+ - Updated dependencies [f549a0d]
1938
+ - Updated dependencies [a36db28]
1939
+ - Updated dependencies [3f8817a]
1940
+ - Updated dependencies [a2443e3]
1941
+ - Updated dependencies [e1554b1]
1942
+ - Updated dependencies [4856789]
1943
+ - Updated dependencies [c3f4916]
1944
+ - Updated dependencies [33e0385]
1945
+ - Updated dependencies [2205363]
1946
+ - Updated dependencies [09fe58d]
1947
+ - Updated dependencies [d0a5ceb]
1948
+ - Updated dependencies [e18a162]
1949
+ - Updated dependencies [d6d1a50]
1950
+ - Updated dependencies [d127ff0]
1951
+ - Updated dependencies [9b86cf6]
1952
+ - Updated dependencies [8825a06]
1953
+ - Updated dependencies [5087ac6]
1954
+ - Updated dependencies [2d1ddf0]
1955
+ - Updated dependencies [354b00f]
1956
+ - Updated dependencies [3de535b]
1957
+ - Updated dependencies [fe2e15a]
1958
+ - Updated dependencies [c6b6bb4]
1959
+ - Updated dependencies [932d7e2]
1960
+ - Updated dependencies [2f59da0]
1961
+ - Updated dependencies [8ad609c]
1962
+ - Updated dependencies [bbee302]
1963
+ - Updated dependencies [08863dd]
1964
+ - Updated dependencies [56664f5]
1965
+ - Updated dependencies [31cbe90]
1966
+ - Updated dependencies [90bbf25]
1967
+ - Updated dependencies [f1850d8]
1968
+ - Updated dependencies [eb91eba]
1969
+ - Updated dependencies [17d0954]
1970
+ - Updated dependencies [42da73d]
1971
+ - Updated dependencies [643b7c7]
1972
+ - Updated dependencies [d0d5205]
1973
+ - Updated dependencies [1a15893]
1974
+ - Updated dependencies [b70e534]
1975
+ - Updated dependencies [2233a85]
1976
+ - Updated dependencies [62dd69a]
1977
+ - Updated dependencies [e15e679]
1978
+ - Updated dependencies [2ab1257]
1979
+ - Updated dependencies [4cc4fb7]
1980
+ - Updated dependencies [28d1eb7]
1981
+ - Updated dependencies [2c26040]
1982
+ - Updated dependencies [f758cec]
1983
+ - Updated dependencies [78f0be8]
1984
+ - Updated dependencies [35f7fb4]
1985
+ - Updated dependencies [a5302c7]
1986
+ - Updated dependencies [7084313]
1987
+ - Updated dependencies [0e043d8]
1988
+ - Updated dependencies [dadd1ad]
1989
+ - Updated dependencies [2f2e63c]
1990
+ - Updated dependencies [486d526]
1991
+ - Updated dependencies [89d7b35]
1992
+ - Updated dependencies [85ec26d]
1993
+ - Updated dependencies [f6476fc]
1994
+ - Updated dependencies [4ac12ef]
1995
+ - Updated dependencies [b88f5e8]
1996
+ - Updated dependencies [42cc219]
1997
+ - Updated dependencies [d7e0b42]
1998
+ - Updated dependencies [3510e4a]
1999
+ - Updated dependencies [aa4b90d]
2000
+ - Updated dependencies [54299ca]
2001
+ - Updated dependencies [dc61def]
2002
+ - Updated dependencies [251e888]
2003
+ - Updated dependencies [183b4c4]
2004
+ - Updated dependencies [2fdb36e]
2005
+ - Updated dependencies [20526f5]
2006
+ - Updated dependencies [c5eef1d]
2007
+ - Updated dependencies [e0f300b]
2008
+ - Updated dependencies [761a0ba]
2009
+ - Updated dependencies [be87153]
2010
+ - Updated dependencies [60f0dd8]
2011
+ - Updated dependencies [a87c5cd]
2012
+ - Updated dependencies [a47f338]
2013
+ - Updated dependencies [2598216]
2014
+ - Updated dependencies [2c7e62d]
2015
+ - Updated dependencies [eb7613c]
2016
+ - Updated dependencies [ecc9110]
2017
+ - Updated dependencies [f7bd4e2]
2018
+ - Updated dependencies [361bd5b]
2019
+ - Updated dependencies [1818998]
2020
+ - Updated dependencies [09ee21c]
2021
+ - Updated dependencies [f549a0d]
2022
+ - Updated dependencies [3fc2e48]
2023
+ - Updated dependencies [e8f435c]
2024
+ - Updated dependencies [41610f6]
2025
+ - @objectstack/spec@17.0.0-rc.6
2026
+ - @objectstack/service-messaging@17.0.0-rc.6
2027
+ - @objectstack/core@17.0.0-rc.6
2028
+
3
2029
  ## 17.0.0-rc.5
4
2030
 
5
2031
  ### Patch Changes