@objectstack/plugin-webhooks 17.0.0-rc.0 → 17.0.0-rc.1

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,384 @@
1
1
  # @objectstack/plugin-webhooks
2
2
 
3
+ ## 17.0.0-rc.1
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
+ ### Patch Changes
59
+
60
+ - bb1ce2e: fix(plugin-auth,plugin-webhooks): retire a dead degrade branch and an implicit transitive dependency (ADR-0116 follow-ups, #4187)
61
+
62
+ Two concrete findings from the ADR-0116 consumer-side audit, plus the
63
+ authoring rule that would have prevented both.
64
+
65
+ **`plugin-auth` claimed a fallback it did not have.** `init()` ran
66
+ `const dataEngine = ctx.getService('data'); if (!dataEngine) { warn('No data
67
+ engine service found - auth will use in-memory storage') }`. That branch could
68
+ never execute: `getService` **throws** for an unregistered service rather than
69
+ returning `undefined`, and this plugin declares a hard dependency on ObjectQL
70
+ (which registers `data` unconditionally), so a kernel without the engine fails
71
+ even earlier with `Dependency … not found`. The branch is removed and the real
72
+ contract is declared — `requiresServices: ['data', 'manifest']` — which also
73
+ replaces a trailing `// manifest service required` comment with the
74
+ machine-checked form of the same claim. `AuthManager` keeps its own optional
75
+ `dataEngine` guards: it is usable outside the plugin.
76
+
77
+ **`plugin-webhook-outbox` was protected only transitively.** It resolves
78
+ `manifest` in `init()` with no fallback while depending on
79
+ `com.objectstack.service.messaging`, which in turn depends on ObjectQL, the
80
+ actual provider. That works today and would have broken silently the day
81
+ messaging stopped depending on the engine — surfacing as a crash inside an
82
+ unrelated plugin's init. It now declares `requiresServices: ['manifest']`
83
+ directly.
84
+
85
+ Neither change alters ordering or boot outcomes on any current composition:
86
+ both plugins were already ordered correctly. What changes is what a broken
87
+ composition _says_, and that the guarantees are now checked rather than
88
+ inherited.
89
+
90
+ Docs: `content/docs/plugins/anatomy.mdx` gains the three ADR-0116 fields and
91
+ the decision rule for resolving a service inside `init()` (hard dependency vs
92
+ `optionalDependencies` + `requiresServices`), including the two traps behind
93
+ these fixes — don't rely on a transitive provider, and don't write an
94
+ `if (!svc)` fallback after a bare `getService`. The api-registry example
95
+ declares the contract on all seven of its plugins instead of relying on
96
+ `kernel.use()` order.
97
+
98
+ - 2e836de: chore(packaging): CHANGELOG.md ships in every npm tarball (#4261)
99
+
100
+ The AGENTS.md post-task checklist requires breaking changesets to carry their
101
+ FROM → TO migration because "this text ships to consumers as `CHANGELOG.md`
102
+ inside the npm package and is what an upgrading agent greps after the tombstone
103
+ error." That delivery path was severed for 68 of the 69 publishable packages:
104
+ npm packs `package.json` / `README*` / `LICENSE*` unconditionally but — unlike
105
+ older npm versions — not `CHANGELOG.md`, and the canonical
106
+ `"files": ["dist", "README.md"]` whitelist never named it. Measured on npm
107
+ 10.9.7: `npm pack --dry-run` on `@objectstack/types` shipped 3 files while its
108
+ 70KB `CHANGELOG.md` stayed behind. Only `@objectstack/spec` listed it
109
+ explicitly.
110
+
111
+ The tombstone-error scenario is precisely the one where the repo is out of
112
+ reach — the upgrading agent has `node_modules` and nothing else — so the
113
+ migration text has to ride in the tarball. Every publishable package now
114
+ declares `CHANGELOG.md` in `files`, and the canonical whitelist is
115
+ `["dist", "README.md", "CHANGELOG.md"]`.
116
+
117
+ The other half is the gate: `check:published-files` gains a fifth invariant,
118
+ COMPLETE — a whitelist that fails to cover `CHANGELOG.md` fails the
119
+ always-required lint job, so the next package cannot silently sever the path
120
+ again. `@objectstack/spec`'s per-package EXTRA_ENTRIES exemption dissolves
121
+ into the canonical set.
122
+
123
+ Consumer-visible change: one more file per install (the package's changelog,
124
+ e.g. 70.8KB for `@objectstack/types`), and `grep -r "removed key"
125
+ node_modules/@objectstack/*/CHANGELOG.md` now finds the migration it was
126
+ promised.
127
+
128
+ - a225ef5: fix(runtime,webhooks): the path object wins on /data/:object/query, and the webhook envelope owns its keys (#3946)
129
+
130
+ Follow-up sweep for the shape behind #3897 and #3933 — a trusted, server-derived
131
+ value written into an object literal with a caller-controlled bag spread OVER
132
+ it. Both of those were in the same block of REST code, so the pattern was swept
133
+ across all 1313 non-test TypeScript files in `packages/`. Nine candidate sites;
134
+ one real, one worth hardening, seven verified clean (recorded in #3946 so the
135
+ next sweep does not re-litigate them).
136
+
137
+ **`POST /data/:object/query` (runtime dispatcher).** The `/data` domain built
138
+ `{ object: objectName, ...body }`, so `{"object":"other", …}` in the body moved
139
+ the read to a different object than the URL named.
140
+
141
+ This is NOT an authorization bypass, and the tests pin why: `callData` gates
142
+ API exposure on `params.object`, so the gate followed the body and agreed with
143
+ the read — an object hidden by `apiEnabled: false` was refused either way. What
144
+ broke is that the URL stopped describing the operation (audit trails, logs, and
145
+ anything keyed on the request path saw object A while object B was read), and
146
+ that one endpoint spoke a second dialect of the contract the REST side had just
147
+ standardised on: the path object wins. The other handlers in that file never had
148
+ the problem — they nest caller data (`data: body`, `query: normalized`) instead
149
+ of splatting it, and the GET-by-id branch already allowlists its query params
150
+ against exactly this pollution.
151
+
152
+ **Webhook delivery envelope.** `auto-enqueuer` built
153
+ `{ object, recordId, action, timestamp, ...payload }`, letting an event payload
154
+ rewrite the envelope a subscriber receives. Behaviour-neutral for the engine's
155
+ own publishers — `data.record.*` payloads are `{ recordId, after, changes }`
156
+ with record fields nested under `after`, so none of those four keys collide
157
+ today — but the shape was wrong, and the `payload.id` fallback right above it
158
+ suggests publishers that flatten record fields do exist. Envelope keys are
159
+ written last now.
160
+
161
+ - 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)
162
+
163
+ Two changes with different weights, from one sweep of every in-repo engine
164
+ call site that still speaks a deprecated alias.
165
+
166
+ **The bug — three dropped sorts.** #4346 made the engine fold `filter`→`where`
167
+ and `top`→`limit` on all six methods. The other four pairs in
168
+ `RPC_QUERY_ALIAS_SLOTS` (`select`, `sort`, `skip`, `populate`) are folded at
169
+ the RPC/wire layer only — their values need shape lowering that belongs to
170
+ those layers — and a **direct `engine.find()` never crosses that layer**. Three
171
+ call sites passed `sort` there, so it rode onto the AST untouched, every
172
+ driver's `Array.isArray(query.orderBy)` guard declined to emit an ORDER BY, and
173
+ the query returned an ordinary-looking, arbitrarily-ordered result:
174
+
175
+ | call site | asked for | actually got |
176
+ | ----------------------------------- | ------------------------------------------------- | --------------------------- |
177
+ | `share-link-routes.ts` | shared AI conversation messages, `created_at asc` | messages in arbitrary order |
178
+ | `runtime/domains/share-links.ts` | same route, runtime-domain copy | same |
179
+ | `share-link-service.ts` `listLinks` | the 200 most recent share links | an arbitrary 200 |
180
+
181
+ All three combine the dropped sort with a `limit` — the "latest N" shape whose
182
+ failure #4226 spelled out: an unapplied sort returns rows in arbitrary order,
183
+ which `limit` then slices into an arbitrary page. #4226 fixed that in the wire
184
+ normalizer; these calls sit one layer below it. `listLinks` had no test at all,
185
+ which is why it went unnoticed. Now pinned — on the option bag the engine
186
+ receives, not on row order, because the failure is that the key never becomes
187
+ `orderBy` and a fake engine honouring either spelling would pass either way.
188
+
189
+ **The cleanup — 27 no-op renames.** Every remaining in-repo engine call passing
190
+ `filter` now passes `where` (approvals 5, auth 2, reports 6, sharing 11,
191
+ webhooks 2, plus the one `filters` in a spec doc example). These are strict
192
+ no-ops since #4346 folds the alias — the point is that the framework stops
193
+ depending on a spelling it asks users to migrate off, which is a prerequisite
194
+ for ever retiring the aliases. Service-level `filter` PARAMETERS (each
195
+ service's own public API, e.g. `listRequests(filter)`) are deliberately
196
+ untouched — those are not engine option bags.
197
+
198
+ Two of the renamed calls were live victims of the #4346 bug rather than
199
+ cosmetic: `auth-manager`'s `stampIdentitySource` read the table's first row via
200
+ `findOne({filter})` and counted the whole table via `count({filter})`, so a
201
+ federated sign-in never stamped `source: 'idp_provisioned'`. #4346 already
202
+ corrected the behaviour; this makes the call say what it means.
203
+
204
+ - cc2de0e: chore(packaging): 20 packages stop publishing their sources, tests and build tooling (#4248)
205
+
206
+ These 20 packages declared no `files` field, so npm fell back to packing the
207
+ whole package directory. `npm pack --dry-run` on `@objectstack/plugin-webhooks`
208
+ listed **21 files** — 15 under `src/`, three of them unit tests
209
+ (`auto-enqueuer.test.ts`, `bootstrap-declared-webhooks.test.ts`, …), plus the
210
+ build-time `scripts/i18n-extract.config.ts`. `dist/` lands on top of that at
211
+ publish time rather than instead of it, so consumers were installing the
212
+ TypeScript sources and the test suite alongside the artifact they asked for.
213
+
214
+ Each now declares `"files": ["dist", "README.md"]`, matching the 29 packages
215
+ that already did. Nothing a consumer imports moves: every `main` / `types` /
216
+ `exports` target in all 20 already resolved inside `dist/`, which the new
217
+ `check:published-files` guard verifies rather than assumes. The visible change
218
+ is a smaller install and a smaller dependency-scanning surface — `npm pack` on
219
+ `@objectstack/plugin-webhooks` now yields 2 files plus `dist/`.
220
+
221
+ The other half of the fix is the gate. Half the packages declaring `files` and
222
+ half not was the #3786 shape — a hand-copied convention with nothing enforcing
223
+ it, where whoever forgets the line gets no signal at all. `check:published-files`
224
+ (new, wired into the always-required `lint` job) holds every non-private
225
+ workspace package to four invariants: `files` is **declared**; it is
226
+ **sufficient** (covers every entry point, so tightening a whitelist cannot ship
227
+ a package that fails to resolve); it is **minimal** (admits no test, test-harness
228
+ config or build script); and anything beyond `dist` + `README.md` is
229
+ **registered** with a reason, reconciled in both directions so a stale exemption
230
+ is an error rather than dead text. `@objectstack/spec` is the one package with
231
+ registered extras — its `.zod.ts` sources, JSON Schemas, liveness ledgers and
232
+ `CHANGELOG.md` are product, not build input.
233
+
234
+ This also closes an assumption #4206 was resting on. Excluding `<pkg>/scripts/**`
235
+ from the docs-drift implementation test is sound only while no package publishes
236
+ `scripts/` as runtime code; that held, but it held because someone read all three
237
+ offenders by hand. It is now checked on every PR.
238
+
239
+ - Updated dependencies [6a67d7a]
240
+ - Updated dependencies [0ecc656]
241
+ - Updated dependencies [06772eb]
242
+ - Updated dependencies [270650f]
243
+ - Updated dependencies [3aef718]
244
+ - Updated dependencies [1ea6bce]
245
+ - Updated dependencies [c1dcacd]
246
+ - Updated dependencies [ad303ed]
247
+ - Updated dependencies [32ccb23]
248
+ - Updated dependencies [f5a4ef0]
249
+ - Updated dependencies [2d3e255]
250
+ - Updated dependencies [7d7521f]
251
+ - Updated dependencies [5dc4d02]
252
+ - Updated dependencies [05154a1]
253
+ - Updated dependencies [9b6fe7c]
254
+ - Updated dependencies [8c711fb]
255
+ - Updated dependencies [09e4547]
256
+ - Updated dependencies [91f4c78]
257
+ - Updated dependencies [820eff9]
258
+ - Updated dependencies [8d895ff]
259
+ - Updated dependencies [f6472d7]
260
+ - Updated dependencies [78caf51]
261
+ - Updated dependencies [62a789b]
262
+ - Updated dependencies [789ad63]
263
+ - Updated dependencies [2af1988]
264
+ - Updated dependencies [0af50a3]
265
+ - Updated dependencies [2e836de]
266
+ - Updated dependencies [12a19a8]
267
+ - Updated dependencies [41dcda3]
268
+ - Updated dependencies [c8124e5]
269
+ - Updated dependencies [a1a4140]
270
+ - Updated dependencies [217e2e6]
271
+ - Updated dependencies [86a71d1]
272
+ - Updated dependencies [d5c75e2]
273
+ - Updated dependencies [03d26f7]
274
+ - Updated dependencies [4384921]
275
+ - Updated dependencies [3c628ce]
276
+ - Updated dependencies [7cb922e]
277
+ - Updated dependencies [1d22114]
278
+ - Updated dependencies [b5f9397]
279
+ - Updated dependencies [ed77493]
280
+ - Updated dependencies [58a03d2]
281
+ - Updated dependencies [dc530b4]
282
+ - Updated dependencies [e59786e]
283
+ - Updated dependencies [bcf1112]
284
+ - Updated dependencies [9774b78]
285
+ - Updated dependencies [b07d829]
286
+ - Updated dependencies [a648e96]
287
+ - Updated dependencies [a47ac06]
288
+ - Updated dependencies [e4c61a7]
289
+ - Updated dependencies [cc60165]
290
+ - Updated dependencies [081aa6f]
291
+ - Updated dependencies [91f4c78]
292
+ - Updated dependencies [e8d0c21]
293
+ - Updated dependencies [45dc446]
294
+ - Updated dependencies [c1d44f7]
295
+ - Updated dependencies [ab9fb5c]
296
+ - Updated dependencies [f985b3f]
297
+ - Updated dependencies [9a4932a]
298
+ - Updated dependencies [f9fc874]
299
+ - Updated dependencies [011b386]
300
+ - Updated dependencies [7777e8f]
301
+ - Updated dependencies [507b92a]
302
+ - Updated dependencies [7309c81]
303
+ - Updated dependencies [a8dcc37]
304
+ - Updated dependencies [20bc1ec]
305
+ - Updated dependencies [90c2b15]
306
+ - Updated dependencies [42eeb7d]
307
+ - Updated dependencies [01e124d]
308
+ - Updated dependencies [7ce02eb]
309
+ - Updated dependencies [a13827e]
310
+ - Updated dependencies [7733604]
311
+ - Updated dependencies [40e420f]
312
+ - Updated dependencies [d13004a]
313
+ - Updated dependencies [be7360c]
314
+ - Updated dependencies [5b47ab5]
315
+ - Updated dependencies [b09d8d9]
316
+ - Updated dependencies [b09d8d9]
317
+ - Updated dependencies [8675db6]
318
+ - Updated dependencies [b09d8d9]
319
+ - Updated dependencies [3eb1b2b]
320
+ - Updated dependencies [59b85c0]
321
+ - Updated dependencies [6e357ed]
322
+ - Updated dependencies [d6938bf]
323
+ - Updated dependencies [31e0be9]
324
+ - Updated dependencies [4bfd455]
325
+ - Updated dependencies [ffd2ce2]
326
+ - Updated dependencies [62f8017]
327
+ - Updated dependencies [a831df1]
328
+ - Updated dependencies [f752ee3]
329
+ - Updated dependencies [a1b61e0]
330
+ - Updated dependencies [cd6b9f2]
331
+ - Updated dependencies [2cb6d3c]
332
+ - Updated dependencies [af2a095]
333
+ - Updated dependencies [ec796d5]
334
+ - Updated dependencies [e87fea1]
335
+ - Updated dependencies [c65e529]
336
+ - Updated dependencies [3ca34c1]
337
+ - Updated dependencies [239c3a3]
338
+ - Updated dependencies [94a0bbc]
339
+ - Updated dependencies [d6bfb3d]
340
+ - Updated dependencies [a2266a6]
341
+ - Updated dependencies [d25a0ec]
342
+ - Updated dependencies [667b83e]
343
+ - Updated dependencies [627b188]
344
+ - Updated dependencies [8d4eae7]
345
+ - Updated dependencies [857a6cf]
346
+ - Updated dependencies [65a3a84]
347
+ - Updated dependencies [ccd9397]
348
+ - Updated dependencies [bca935b]
349
+ - Updated dependencies [d92c72d]
350
+ - Updated dependencies [c54c822]
351
+ - Updated dependencies [8dcc0f5]
352
+ - Updated dependencies [75b9e51]
353
+ - Updated dependencies [0a2f233]
354
+ - Updated dependencies [8621cdd]
355
+ - Updated dependencies [6f23667]
356
+ - Updated dependencies [5d21a48]
357
+ - Updated dependencies [19365b7]
358
+ - Updated dependencies [b7ed26d]
359
+ - Updated dependencies [b3a3d83]
360
+ - Updated dependencies [7a55913]
361
+ - Updated dependencies [35accbf]
362
+ - Updated dependencies [6038de7]
363
+ - Updated dependencies [eb95d97]
364
+ - Updated dependencies [e4c2dc8]
365
+ - Updated dependencies [1bd2795]
366
+ - Updated dependencies [8186a70]
367
+ - Updated dependencies [a329cca]
368
+ - Updated dependencies [6eec18c]
369
+ - Updated dependencies [4d7bebf]
370
+ - Updated dependencies [821ac7a]
371
+ - Updated dependencies [8f81731]
372
+ - Updated dependencies [8b50cb3]
373
+ - Updated dependencies [8c2db68]
374
+ - Updated dependencies [22b5e54]
375
+ - Updated dependencies [0166bd5]
376
+ - Updated dependencies [9b702dc]
377
+ - Updated dependencies [ab16331]
378
+ - @objectstack/spec@17.0.0-rc.1
379
+ - @objectstack/core@17.0.0-rc.1
380
+ - @objectstack/service-messaging@17.0.0-rc.1
381
+
3
382
  ## 17.0.0-rc.0
4
383
 
5
384
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -180,12 +180,21 @@ var AutoEnqueuer = class {
180
180
  headers: sub.headers,
181
181
  signingSecret: sub.secret,
182
182
  timeoutMs: sub.timeoutMs,
183
+ // [#3946] Envelope keys are written LAST so the event payload
184
+ // cannot rewrite them. Behaviour-neutral for the engine's own
185
+ // publishers — `data.record.*` payloads are
186
+ // `{ recordId, after, changes }`, with record fields nested
187
+ // under `after`, so none of these four keys collide today. It
188
+ // is the shape that was wrong: a publisher that flattened
189
+ // record fields into the payload (the `payload.id` fallback
190
+ // above suggests some do) would have silently rewritten the
191
+ // `object` / `action` / `timestamp` a subscriber receives.
183
192
  payload: {
193
+ ...payload,
184
194
  object: event.object,
185
195
  recordId,
186
196
  action,
187
- timestamp: event.timestamp,
188
- ...payload
197
+ timestamp: event.timestamp
189
198
  }
190
199
  }).catch(
191
200
  (err) => _optionalChain([this, 'access', _25 => _25.logger, 'access', _26 => _26.warn, 'optionalCall', _27 => _27("[webhook-auto-enqueuer] enqueue failed", {
@@ -267,7 +276,7 @@ async function bootstrapDeclaredWebhooks(engine, metadataService, logger, subscr
267
276
  }
268
277
  try {
269
278
  const existing = await engine.find(subscriptionsObject, {
270
- filter: { name: wh.name },
279
+ where: { name: wh.name },
271
280
  limit: 1,
272
281
  context: SYSTEM_CTX
273
282
  });
@@ -352,7 +361,7 @@ function bindWebhookProvenanceStamp(engine, logger) {
352
361
  if (!data || typeof data !== "object") return;
353
362
  try {
354
363
  const rows = await engine.find("sys_webhook", {
355
- filter: { id },
364
+ where: { id },
356
365
  fields: ["id", "managed_by", "customized"],
357
366
  limit: 1,
358
367
  context: SYSTEM_CTX2
@@ -387,6 +396,16 @@ var WebhookOutboxPlugin = class {
387
396
  this.version = "2.0.0";
388
397
  this.type = "standard";
389
398
  this.dependencies = ["com.objectstack.service.messaging"];
399
+ /**
400
+ * `init()` registers this plugin's schema through `manifest` with no
401
+ * fallback. Until #4187 that was safe only TRANSITIVELY — messaging happens
402
+ * to depend on ObjectQL, which provides `manifest` — so the guarantee would
403
+ * have evaporated silently the day messaging stopped depending on the
404
+ * engine, and the failure would have surfaced as an unrelated plugin's
405
+ * init crash. Declaring the requirement directly makes the kernel check it
406
+ * regardless of what messaging depends on.
407
+ */
408
+ this.requiresServices = ["manifest"];
390
409
  }
391
410
  async init(ctx) {
392
411
  const manifest = ctx.getService("manifest");
@@ -546,7 +565,7 @@ var WebhookOutboxPlugin = class {
546
565
  const userId = await this.resolveSessionUserId(ctx, c);
547
566
  if (!userId) {
548
567
  return c.json(
549
- { success: false, error: "unauthenticated", message: "Sign in to redeliver webhook deliveries." },
568
+ { success: false, error: { code: "UNAUTHENTICATED", message: "Sign in to redeliver webhook deliveries." } },
550
569
  401
551
570
  );
552
571
  }
@@ -554,12 +573,12 @@ var WebhookOutboxPlugin = class {
554
573
  try {
555
574
  body = await c.req.json();
556
575
  } catch (e9) {
557
- return c.json({ success: false, error: "invalid_body", message: "Request body must be JSON." }, 400);
576
+ return c.json({ success: false, error: { code: "INVALID_REQUEST", message: "Request body must be JSON." } }, 400);
558
577
  }
559
578
  const deliveryId = typeof _optionalChain([body, 'optionalAccess', _99 => _99.deliveryId]) === "string" ? body.deliveryId.trim() : "";
560
579
  if (!deliveryId) {
561
580
  return c.json(
562
- { success: false, error: "missing_delivery_id", message: "Body must include `deliveryId: string`." },
581
+ { success: false, error: { code: "MISSING_REQUIRED_FIELD", message: "Body must include `deliveryId: string`." } },
563
582
  400
564
583
  );
565
584
  }
@@ -569,15 +588,15 @@ var WebhookOutboxPlugin = class {
569
588
  return c.json({ success: true, data: { id: row.id, status: row.status } });
570
589
  } catch (err) {
571
590
  const code = _optionalChain([err, 'optionalAccess', _103 => _103.code]);
572
- if (code === "not_found") {
573
- return c.json({ success: false, error: "not_found", message: err.message }, 404);
591
+ if (code === "RESOURCE_NOT_FOUND") {
592
+ return c.json({ success: false, error: { code, message: err.message } }, 404);
574
593
  }
575
- if (code === "not_eligible") {
576
- return c.json({ success: false, error: "not_eligible", message: err.message }, 409);
594
+ if (code === "DELIVERY_NOT_ELIGIBLE") {
595
+ return c.json({ success: false, error: { code, message: err.message } }, 409);
577
596
  }
578
597
  _optionalChain([ctx, 'access', _104 => _104.logger, 'access', _105 => _105.error, 'optionalCall', _106 => _106("[webhook-outbox] redeliver failed", err)]);
579
598
  return c.json(
580
- { success: false, error: "internal_error", message: _nullishCoalesce(_optionalChain([err, 'optionalAccess', _107 => _107.message]), () => ( String(err))) },
599
+ { success: false, error: { code: "INTERNAL_ERROR", message: _nullishCoalesce(_optionalChain([err, 'optionalAccess', _107 => _107.message]), () => ( String(err))) } },
581
600
  500
582
601
  );
583
602
  }