@objectstack/plugin-webhooks 17.0.0-rc.1 → 17.0.0-rc.3
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 +262 -0
- package/dist/{chunk-6EPMRZ7I.js → chunk-3QGZLM3T.js} +8 -3
- package/dist/chunk-3QGZLM3T.js.map +1 -0
- package/dist/{chunk-QUTQSOQC.cjs → chunk-JQUVS5KK.cjs} +8 -3
- package/dist/chunk-JQUVS5KK.cjs.map +1 -0
- package/dist/index.cjs +158 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +113 -13
- package/dist/index.js.map +1 -1
- package/dist/schema.cjs +2 -2
- package/dist/schema.d.cts +576 -417
- package/dist/schema.d.ts +576 -417
- package/dist/schema.js +1 -1
- package/dist/{translations-Y3CXWOTM.js → translations-2VGD4XRF.js} +17 -9
- package/dist/translations-2VGD4XRF.js.map +1 -0
- package/dist/{translations-CBQRUIS5.cjs → translations-BWS57U2V.cjs} +17 -9
- package/dist/translations-BWS57U2V.cjs.map +1 -0
- package/package.json +5 -5
- package/dist/chunk-6EPMRZ7I.js.map +0 -1
- package/dist/chunk-QUTQSOQC.cjs.map +0 -1
- package/dist/translations-CBQRUIS5.cjs.map +0 -1
- package/dist/translations-Y3CXWOTM.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,267 @@
|
|
|
1
1
|
# @objectstack/plugin-webhooks
|
|
2
2
|
|
|
3
|
+
## 17.0.0-rc.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [2e284b2]
|
|
8
|
+
- @objectstack/spec@17.0.0-rc.3
|
|
9
|
+
- @objectstack/core@17.0.0-rc.3
|
|
10
|
+
- @objectstack/service-messaging@17.0.0-rc.3
|
|
11
|
+
|
|
12
|
+
## 17.0.0-rc.2
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- f2445c9: feat(spec,objectql,client,plugin-webhooks): predicate writes get an honest bulk event contract (#4639)
|
|
17
|
+
|
|
18
|
+
A `multi: true` update/delete reaches `IDataDriver.updateMany` / `deleteMany`,
|
|
19
|
+
which are contracted to resolve an affected row COUNT and nothing else. That
|
|
20
|
+
satisfies neither `DataEvent.recordId` (required) nor `before` / `after` /
|
|
21
|
+
`changes`, so before #4626 the engine fabricated a per-record event with
|
|
22
|
+
`recordId: ''` and `after: <count>` — an event every schema-compliant consumer
|
|
23
|
+
must reject, and one the webhook enqueuer's `?? 'unknown'` fallback turned into
|
|
24
|
+
a real delivery naming an unidentifiable record. #4626 removed the fabrication
|
|
25
|
+
and published nothing instead: honest, but it left webhooks, knowledge sync and
|
|
26
|
+
`subscribeData` silent for every predicate write.
|
|
27
|
+
|
|
28
|
+
Bulk writes now get their **own** contract rather than impersonating a
|
|
29
|
+
per-record one or going dark:
|
|
30
|
+
|
|
31
|
+
- **New `BulkDataEvent`** (`@objectstack/spec/api`): `data.records.updated` /
|
|
32
|
+
`data.records.deleted` — note the plural — carrying `id`, `type`, `object`,
|
|
33
|
+
`matched`, `userId?`, `timestamp`. Deliberately a separate schema from
|
|
34
|
+
`DataEvent`, not a widened one: a consumer that receives
|
|
35
|
+
`data.records.updated` knows from the type alone that no `recordId` is
|
|
36
|
+
coming, instead of discovering an empty string at runtime.
|
|
37
|
+
- **Engine** publishes it from the `multi: true` branches of `update()` /
|
|
38
|
+
`delete()`, validated with `BulkDataEventSchema.parse` before publish. A
|
|
39
|
+
predicate that matched **zero** rows publishes nothing (no data changed — this
|
|
40
|
+
is what keeps an idle background sweep from becoming an hourly "0 records"
|
|
41
|
+
delivery), and a driver that resolves a non-count publishes nothing and warns
|
|
42
|
+
rather than asserting a number it cannot verify. Per-record writes are
|
|
43
|
+
untouched, including a scalar `where.id` with `multi: true`, which is still a
|
|
44
|
+
single-record target and still emits `data.record.deleted`.
|
|
45
|
+
- **Webhooks**: two new opt-in triggers, `bulk_update` and `bulk_delete`
|
|
46
|
+
(`WebhookTriggerType`, and the `sys_webhook.triggers` multi-select). They are
|
|
47
|
+
**not** extra sources for `create` / `update` / `delete`: the delivered body
|
|
48
|
+
has no `recordId` and no record, so routing it to existing per-record
|
|
49
|
+
subscribers would hand them a payload missing every field they read — the
|
|
50
|
+
same class of breakage as the old `recordId: ''`, from the other direction. A
|
|
51
|
+
webhook that wants both subscribes to both. Bulk deliveries dedup on the
|
|
52
|
+
producer's event uuid, since two sweeps in the same millisecond are genuinely
|
|
53
|
+
different events that a timestamp-based key would collapse.
|
|
54
|
+
- **Client SDK**: new `client.events.subscribeBulkData(object, cb)`, with the
|
|
55
|
+
same loud boundary validation as `subscribeData`. Kept a separate method for
|
|
56
|
+
the same reason — delivering a `BulkDataEvent` to a `(event: DataEvent) =>
|
|
57
|
+
void` callback would recreate exactly the "typed field, `undefined` at
|
|
58
|
+
runtime" defect #4626 removed. `subscribeData`'s own guard was also tightened
|
|
59
|
+
from `data.` to `data.record.`, so an aggregate event is ignored rather than
|
|
60
|
+
rejected as off-contract.
|
|
61
|
+
- **Knowledge sync** now says out loud that a predicate write leaves its index
|
|
62
|
+
stale. A knowledge index is a per-record projection and `matched: 40` names no
|
|
63
|
+
record, so no event shape could drive it — the durable fix is reconciliation,
|
|
64
|
+
tracked in #4672.
|
|
65
|
+
|
|
66
|
+
The event carries no `where` predicate. The only one available at publish time
|
|
67
|
+
is the middleware-composed AST, whose filter embeds the security layer's
|
|
68
|
+
injected row scoping (RLS, sharing) — publishing it would ship tenant scoping
|
|
69
|
+
internals to whatever external URL a webhook points at.
|
|
70
|
+
|
|
71
|
+
Also pays off a measurement debt from #4655, which claimed the write-path cost
|
|
72
|
+
of event publishing had been measured but never published the numbers:
|
|
73
|
+
`packages/objectql/src/engine-data-events.bench.ts` measures it. Against an
|
|
74
|
+
in-memory driver, publishing costs ~7–9µs per event (insert 0.021ms vs 0.012ms,
|
|
75
|
+
single-id update 0.013ms vs 0.007ms). A bulk write pays that **once** regardless
|
|
76
|
+
of how many rows matched (0.040ms vs 0.034ms over a 100-row match set), so its
|
|
77
|
+
relative cost shrinks as the match set grows.
|
|
78
|
+
|
|
79
|
+
### Patch Changes
|
|
80
|
+
|
|
81
|
+
- 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.
|
|
82
|
+
|
|
83
|
+
**The Phase 4 ruling.** The phase had two halves, and they got opposite verdicts:
|
|
84
|
+
|
|
85
|
+
- **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.
|
|
86
|
+
- **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.
|
|
87
|
+
|
|
88
|
+
**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.
|
|
89
|
+
|
|
90
|
+
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.
|
|
91
|
+
|
|
92
|
+
**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.
|
|
93
|
+
|
|
94
|
+
**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.
|
|
95
|
+
|
|
96
|
+
Tracked in #4544. This closes the ADR-0078 loop end to end: author-time error, runtime warning, one shared predicate deciding both.
|
|
97
|
+
|
|
98
|
+
- 462b713: fix(objectql,client): `subscribeData` callbacks receive real `DataEvent`s — the producer now fulfils the declared contract (#4626)
|
|
99
|
+
|
|
100
|
+
`@objectstack/spec/api`'s `DataEvent` declares top-level `id` (uuid,
|
|
101
|
+
required), `type`, `object`, `recordId` (required), `changes?`, `before?`,
|
|
102
|
+
`after?`, `userId?`, `timestamp`. But the producer (the ObjectQL engine)
|
|
103
|
+
published a raw `RealtimeEventPayload` envelope with `{ recordId, after,
|
|
104
|
+
changes }` nested under `payload` and never generated `id`/`userId`, while the
|
|
105
|
+
client SDK force-cast that envelope into the callback (`callback(event as any
|
|
106
|
+
as DataEvent)`). Subscribers who wrote `event.recordId` / `event.changes` —
|
|
107
|
+
exactly what the types promised — compiled green and read `undefined` at
|
|
108
|
+
runtime. The data-side twin of #4602.
|
|
109
|
+
|
|
110
|
+
Producer now fulfils the contract:
|
|
111
|
+
|
|
112
|
+
- `ObjectQL.insert()` / `update()` / `delete()` build a true `DataEvent`
|
|
113
|
+
(generated uuid `id`, flattened top-level fields, `userId` from the
|
|
114
|
+
execution context when the write names an actor) and validate it with
|
|
115
|
+
`DataEventSchema.parse` before publishing. The transport envelope is
|
|
116
|
+
unchanged (`RealtimeEventPayload`, with `payload` carrying the complete
|
|
117
|
+
`DataEvent`), so subscribers keep receiving `{ type, object, payload,
|
|
118
|
+
timestamp }` on the wire.
|
|
119
|
+
- A batch insert publishes one event **per record** (as before), each with its
|
|
120
|
+
own event id.
|
|
121
|
+
- **A multi-row write (`multi: true` → `updateMany` / `deleteMany`) now
|
|
122
|
+
publishes nothing.** Those driver methods return only an affected count, so
|
|
123
|
+
there is no record for a required `recordId` to name; the engine logs a
|
|
124
|
+
warning naming the gap instead of publishing the previous fabrication
|
|
125
|
+
(`recordId: ''`, `after: <affected count>`), which every schema-compliant
|
|
126
|
+
consumer had to reject. **Consequence: webhooks and knowledge sync no longer
|
|
127
|
+
fire for bulk writes** — they previously fired once with an unusable body. A
|
|
128
|
+
real bulk event contract is tracked in #4639.
|
|
129
|
+
|
|
130
|
+
Consumers validate or read the fulfilled shape instead of guessing:
|
|
131
|
+
|
|
132
|
+
- `@objectstack/client`'s `subscribeData` (and therefore
|
|
133
|
+
`@objectstack/client-react`'s `useDataSubscription` /
|
|
134
|
+
`useDataSubscriptionCallback` / `useAutoRefresh`, which delegate to it)
|
|
135
|
+
unwraps the envelope and runs `DataEventSchema.safeParse` at the boundary.
|
|
136
|
+
An off-contract payload is rejected loudly (handler error, callback never
|
|
137
|
+
invoked) — never coerced or passed through. The `as any as DataEvent`
|
|
138
|
+
double-cast is gone, and the `recordId` option now filters on the fulfilled
|
|
139
|
+
event.
|
|
140
|
+
- `@objectstack/plugin-webhooks`' auto-enqueuer reads the required
|
|
141
|
+
`recordId` directly; its `recordId ?? id ?? after?.id ?? before?.id ??
|
|
142
|
+
'unknown'` fallback chain is gone, and an off-contract event is dropped with
|
|
143
|
+
a warning rather than delivered under the literal id `'unknown'`. Delivered
|
|
144
|
+
webhook bodies now also carry the event's `id`/`type`/`userId`; the record
|
|
145
|
+
itself stays nested under `after` and the envelope keys (`object`,
|
|
146
|
+
`recordId`, `action`, `timestamp`) still win.
|
|
147
|
+
- `@objectstack/service-knowledge`'s event sync reads the record from `after`
|
|
148
|
+
(create/update) and the id from `recordId` (delete) for `data.record.*`.
|
|
149
|
+
It previously indexed the envelope itself as if it were the row, and never
|
|
150
|
+
resolved an id for deletes.
|
|
151
|
+
|
|
152
|
+
- Updated dependencies [430dcc2]
|
|
153
|
+
- Updated dependencies [e6ac4bd]
|
|
154
|
+
- Updated dependencies [80334c7]
|
|
155
|
+
- Updated dependencies [ce5242c]
|
|
156
|
+
- Updated dependencies [a7163ea]
|
|
157
|
+
- Updated dependencies [e6e9379]
|
|
158
|
+
- Updated dependencies [98877c9]
|
|
159
|
+
- Updated dependencies [98877c9]
|
|
160
|
+
- Updated dependencies [e6b1b69]
|
|
161
|
+
- Updated dependencies [ad047d2]
|
|
162
|
+
- Updated dependencies [2826d1e]
|
|
163
|
+
- Updated dependencies [5a84d41]
|
|
164
|
+
- Updated dependencies [20b1a9e]
|
|
165
|
+
- Updated dependencies [203a449]
|
|
166
|
+
- Updated dependencies [ac37fc6]
|
|
167
|
+
- Updated dependencies [4820f55]
|
|
168
|
+
- Updated dependencies [462d9c4]
|
|
169
|
+
- Updated dependencies [7d21581]
|
|
170
|
+
- Updated dependencies [f2445c9]
|
|
171
|
+
- Updated dependencies [23338c3]
|
|
172
|
+
- Updated dependencies [5b843fb]
|
|
173
|
+
- Updated dependencies [b4487aa]
|
|
174
|
+
- Updated dependencies [65ca83a]
|
|
175
|
+
- Updated dependencies [67bf2e2]
|
|
176
|
+
- Updated dependencies [c6d1cb4]
|
|
177
|
+
- Updated dependencies [36030ff]
|
|
178
|
+
- Updated dependencies [6117f7b]
|
|
179
|
+
- Updated dependencies [e533b0b]
|
|
180
|
+
- Updated dependencies [cdf4d9a]
|
|
181
|
+
- Updated dependencies [aee1806]
|
|
182
|
+
- Updated dependencies [c13350b]
|
|
183
|
+
- Updated dependencies [c13350b]
|
|
184
|
+
- Updated dependencies [9ca2d85]
|
|
185
|
+
- Updated dependencies [c13350b]
|
|
186
|
+
- Updated dependencies [891d345]
|
|
187
|
+
- Updated dependencies [a52e2ef]
|
|
188
|
+
- Updated dependencies [5293114]
|
|
189
|
+
- Updated dependencies [20bc357]
|
|
190
|
+
- Updated dependencies [5966c2a]
|
|
191
|
+
- Updated dependencies [2382580]
|
|
192
|
+
- Updated dependencies [d9fa683]
|
|
193
|
+
- Updated dependencies [3c7bcc0]
|
|
194
|
+
- Updated dependencies [4b6cac7]
|
|
195
|
+
- Updated dependencies [7631964]
|
|
196
|
+
- Updated dependencies [ac471a0]
|
|
197
|
+
- Updated dependencies [60ae58e]
|
|
198
|
+
- Updated dependencies [ce92674]
|
|
199
|
+
- Updated dependencies [9f601e8]
|
|
200
|
+
- Updated dependencies [51c5227]
|
|
201
|
+
- Updated dependencies [a4a85c8]
|
|
202
|
+
- Updated dependencies [07a4e26]
|
|
203
|
+
- Updated dependencies [ec975f1]
|
|
204
|
+
- Updated dependencies [eb4204b]
|
|
205
|
+
- Updated dependencies [4f13be2]
|
|
206
|
+
- Updated dependencies [61cc079]
|
|
207
|
+
- Updated dependencies [0e96e46]
|
|
208
|
+
- Updated dependencies [d52d4fe]
|
|
209
|
+
- Updated dependencies [742cebb]
|
|
210
|
+
- Updated dependencies [ce92674]
|
|
211
|
+
- Updated dependencies [cf2c9b7]
|
|
212
|
+
- Updated dependencies [833b512]
|
|
213
|
+
- Updated dependencies [0f9faa2]
|
|
214
|
+
- Updated dependencies [7cf42fe]
|
|
215
|
+
- Updated dependencies [5966c2a]
|
|
216
|
+
- Updated dependencies [040ecd2]
|
|
217
|
+
- Updated dependencies [f78dd83]
|
|
218
|
+
- Updated dependencies [a2cd18a]
|
|
219
|
+
- Updated dependencies [4638aaa]
|
|
220
|
+
- Updated dependencies [0222d3c]
|
|
221
|
+
- Updated dependencies [071d0dc]
|
|
222
|
+
- Updated dependencies [0a936ea]
|
|
223
|
+
- Updated dependencies [023c00b]
|
|
224
|
+
- Updated dependencies [155507e]
|
|
225
|
+
- Updated dependencies [7bba90b]
|
|
226
|
+
- Updated dependencies [7e05d8e]
|
|
227
|
+
- Updated dependencies [061406d]
|
|
228
|
+
- Updated dependencies [c1f344b]
|
|
229
|
+
- Updated dependencies [9c93465]
|
|
230
|
+
- Updated dependencies [ebb209c]
|
|
231
|
+
- Updated dependencies [63b33e6]
|
|
232
|
+
- Updated dependencies [2a44c1d]
|
|
233
|
+
- Updated dependencies [695cfbd]
|
|
234
|
+
- Updated dependencies [7445149]
|
|
235
|
+
- Updated dependencies [071d0dc]
|
|
236
|
+
- Updated dependencies [0848bea]
|
|
237
|
+
- Updated dependencies [d51bed2]
|
|
238
|
+
- Updated dependencies [b8b3c64]
|
|
239
|
+
- Updated dependencies [0c0fbd9]
|
|
240
|
+
- Updated dependencies [f3141d8]
|
|
241
|
+
- Updated dependencies [5a84d41]
|
|
242
|
+
- Updated dependencies [fd3013a]
|
|
243
|
+
- Updated dependencies [21676eb]
|
|
244
|
+
- Updated dependencies [e336549]
|
|
245
|
+
- Updated dependencies [d40f43a]
|
|
246
|
+
- Updated dependencies [e5e7ee0]
|
|
247
|
+
- Updated dependencies [a2ebea2]
|
|
248
|
+
- Updated dependencies [800bdb0]
|
|
249
|
+
- Updated dependencies [04f1182]
|
|
250
|
+
- Updated dependencies [5647006]
|
|
251
|
+
- Updated dependencies [38f7e4f]
|
|
252
|
+
- Updated dependencies [c57f3cf]
|
|
253
|
+
- Updated dependencies [97faca3]
|
|
254
|
+
- Updated dependencies [ad5fe25]
|
|
255
|
+
- Updated dependencies [ea90179]
|
|
256
|
+
- Updated dependencies [ce92674]
|
|
257
|
+
- Updated dependencies [5ef0b5b]
|
|
258
|
+
- Updated dependencies [48fbacb]
|
|
259
|
+
- Updated dependencies [355e951]
|
|
260
|
+
- Updated dependencies [dadb43f]
|
|
261
|
+
- @objectstack/spec@17.0.0-rc.2
|
|
262
|
+
- @objectstack/core@17.0.0-rc.2
|
|
263
|
+
- @objectstack/service-messaging@17.0.0-rc.2
|
|
264
|
+
|
|
3
265
|
## 17.0.0-rc.1
|
|
4
266
|
|
|
5
267
|
### Minor Changes
|
|
@@ -86,7 +86,7 @@ var SysWebhook = ObjectSchema.create({
|
|
|
86
86
|
group: "Definition"
|
|
87
87
|
}),
|
|
88
88
|
triggers: Field.select(
|
|
89
|
-
["create", "update", "delete"],
|
|
89
|
+
["create", "update", "delete", "bulk_update", "bulk_delete"],
|
|
90
90
|
{
|
|
91
91
|
label: "Triggers",
|
|
92
92
|
required: false,
|
|
@@ -94,7 +94,12 @@ var SysWebhook = ObjectSchema.create({
|
|
|
94
94
|
// an array; the auto-enqueuer parser also tolerates the legacy
|
|
95
95
|
// comma-separated / JSON-string forms so existing rows keep working.
|
|
96
96
|
multiple: true,
|
|
97
|
-
|
|
97
|
+
// [#4639] `bulk_*` fire on predicate writes (`multi: true`), whose
|
|
98
|
+
// delivery carries `matched` instead of a record — opt-in precisely
|
|
99
|
+
// because that body is a different shape. Kept in step with
|
|
100
|
+
// `WebhookTriggerType` (`@objectstack/spec/automation`), which is the
|
|
101
|
+
// contract the auto-enqueuer validates against.
|
|
102
|
+
description: "Record events that fire this webhook (bulk_* deliver a count, not a record)",
|
|
98
103
|
group: "Definition"
|
|
99
104
|
}
|
|
100
105
|
),
|
|
@@ -178,4 +183,4 @@ var SysWebhook = ObjectSchema.create({
|
|
|
178
183
|
export {
|
|
179
184
|
SysWebhook
|
|
180
185
|
};
|
|
181
|
-
//# sourceMappingURL=chunk-
|
|
186
|
+
//# sourceMappingURL=chunk-3QGZLM3T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sys-webhook.object.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_webhook — Outbound HTTP integration configuration (runtime).\n *\n * Persists a single {@link Webhook} envelope per row so administrators\n * can author, enable/disable, and edit webhook subscriptions from the\n * Studio UI without code changes. The canonical Zod schema for the\n * `definition_json` envelope lives at `@objectstack/spec/automation/webhook`.\n *\n * ## Two authoring doors, one row\n * Rows land here two ways, distinguished by the `managed_by` provenance column:\n * - **admin** — created/edited directly through this object's CRUD UI.\n * - **package** — declared in code (`defineStack({ webhooks })` /\n * `defineWebhook()`) and materialized on boot by\n * `bootstrapDeclaredWebhooks` (#3461). Re-seeded every boot, but an admin\n * edit stamps `customized: true` and freezes the row (seed-not-clobber,\n * mirrors `sys_sharing_rule` #2909).\n *\n * One row per `name`. This plugin's {@link AutoEnqueuer} loads active rows on\n * boot + on `sys_webhook:changed` events, and turns matching `data.record.*`\n * events into deliveries on the shared `service-messaging` HTTP outbox\n * (ADR-0018 M3 — `sys_http_delivery`, drained by the messaging dispatcher).\n *\n * Ownership (ADR-0029 K2.a): this object is **owned by\n * `@objectstack/plugin-webhooks`** — the plugin that consumes these rows. It\n * used to live in the `@objectstack/platform-objects` monolith and be imported\n * here; the definition now lives with its owner so the plugin ships both data\n * and behavior as one unit.\n *\n * Platform-wide on purpose: every project (standalone, single-tenant,\n * cloud) can integrate with external systems (Slack, Stripe, internal\n * services) the same way.\n *\n * @namespace sys\n */\nexport const SysWebhook = ObjectSchema.create({\n name: 'sys_webhook',\n label: 'Webhook',\n pluralLabel: 'Webhooks',\n icon: 'webhook',\n isSystem: true,\n managedBy: 'config',\n // Authoring a webhook from the UI requires a structured form for the\n // headers / auth / retry / payload blocks — the generic JSON textarea\n // is acceptable as a v1 until a dedicated builder lands. Re-enable\n // create/edit/delete so admins can at least toggle `active` and edit\n // simple URL/method fields without round-tripping through code.\n userActions: { create: true, edit: true, delete: true, import: false },\n description: 'Outbound HTTP webhook subscription. Declared in code via defineStack({ webhooks }) / defineWebhook() (materialized into rows on boot) or authored directly in the Studio editor; dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.',\n displayNameField: 'name',\n nameField: 'name', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{label}',\n highlightFields: ['name', 'object_name', 'url', 'active', 'updated_at'],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n inactive: {\n type: 'grid',\n name: 'inactive',\n label: 'Inactive',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: false }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_object: {\n type: 'grid',\n name: 'by_object',\n label: 'By Object',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['object_name', 'label', 'url', 'active', 'updated_at'],\n sort: [{ field: 'object_name', order: 'asc' }, { field: 'label', order: 'asc' }],\n grouping: { fields: [{ field: 'object_name', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n all_webhooks: {\n type: 'grid',\n name: 'all_webhooks',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Webhook ID', required: true, readonly: true, group: 'System' }),\n\n name: Field.text({\n label: 'Name',\n required: true,\n maxLength: 100,\n description: 'Unique snake_case name — referenced in logs and audit',\n group: 'Definition',\n }),\n\n label: Field.text({\n label: 'Display Label',\n required: false,\n maxLength: 200,\n group: 'Definition',\n }),\n\n object_name: Field.text({\n label: 'Object',\n required: false,\n maxLength: 100,\n // Object picker (same widget as sys_sharing_rule) instead of a free-text\n // machine name. Falls back to a text input when the widget is unavailable.\n widget: 'object-ref',\n description: 'Short object name whose record events (create/update/delete) fire this webhook',\n group: 'Definition',\n }),\n\n triggers: Field.select(\n ['create', 'update', 'delete', 'bulk_update', 'bulk_delete'],\n {\n label: 'Triggers',\n required: false,\n // Multi-select instead of a hand-typed comma-separated string. Stored as\n // an array; the auto-enqueuer parser also tolerates the legacy\n // comma-separated / JSON-string forms so existing rows keep working.\n multiple: true,\n // [#4639] `bulk_*` fire on predicate writes (`multi: true`), whose\n // delivery carries `matched` instead of a record — opt-in precisely\n // because that body is a different shape. Kept in step with\n // `WebhookTriggerType` (`@objectstack/spec/automation`), which is the\n // contract the auto-enqueuer validates against.\n description: 'Record events that fire this webhook (bulk_* deliver a count, not a record)',\n group: 'Definition',\n },\n ),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'External endpoint that receives the POST',\n group: 'Definition',\n }),\n\n method: Field.select(\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],\n {\n label: 'HTTP Method',\n required: true,\n // Select instead of free text. Option values are lowercased by the\n // Field.select helper (get/post/…); the auto-enqueuer upper-cases the\n // resolved method before delivery, so existing 'POST' rows and the\n // lowercase option values both normalise correctly.\n defaultValue: 'post',\n description: 'HTTP method used for the callback request',\n group: 'Definition',\n },\n ),\n\n description: Field.textarea({ label: 'Description', required: false, group: 'Definition' }),\n\n active: Field.boolean({\n label: 'Active',\n required: true,\n defaultValue: true,\n description: 'Inactive webhooks are skipped by the dispatcher',\n group: 'Definition',\n }),\n\n definition_json: Field.textarea({\n label: 'Definition',\n required: true,\n description: 'Serialised Webhook JSON (see @objectstack/spec/automation/webhook) — full headers/auth/retry/payload config',\n group: 'Definition',\n }),\n\n // ── Provenance (#3461 — record-authoritative seed-not-clobber) ──\n // Mirrors sys_sharing_rule (#2909). Both columns are `readonly`: the\n // engine strips them from non-system payloads (forge/clear-proof), while\n // bootstrapDeclaredWebhooks and the provenance stamp hook write with\n // isSystem. Deliberately NOT a write gate: webhooks are a first-class admin\n // authoring/tuning surface — admins may edit or deactivate a package row;\n // the seeder simply stops overwriting it once `customized` is stamped.\n managed_by: Field.select(\n ['platform', 'package', 'admin'],\n {\n label: 'Managed By',\n required: false,\n readonly: true,\n defaultValue: 'admin',\n description:\n 'Record provenance: platform = framework built-in / package = app/package-declared ' +\n '(boot-seeded from defineStack webhooks) / admin = created in Setup.',\n group: 'System',\n },\n ),\n\n customized: Field.boolean({\n label: 'Customized',\n required: false,\n readonly: true,\n defaultValue: false,\n description:\n 'Set when an admin edits a package-declared webhook; boot seeding will no longer ' +\n 'overwrite the row (a deactivated noisy webhook survives redeploys). Meaningless on admin rows.',\n group: 'System',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n { fields: ['name'], unique: true },\n { fields: ['object_name'] },\n { fields: ['active', 'object_name'] },\n ],\n});\n"],"mappings":"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","names":[]}
|
|
@@ -86,7 +86,7 @@ var SysWebhook = _data.ObjectSchema.create({
|
|
|
86
86
|
group: "Definition"
|
|
87
87
|
}),
|
|
88
88
|
triggers: _data.Field.select(
|
|
89
|
-
["create", "update", "delete"],
|
|
89
|
+
["create", "update", "delete", "bulk_update", "bulk_delete"],
|
|
90
90
|
{
|
|
91
91
|
label: "Triggers",
|
|
92
92
|
required: false,
|
|
@@ -94,7 +94,12 @@ var SysWebhook = _data.ObjectSchema.create({
|
|
|
94
94
|
// an array; the auto-enqueuer parser also tolerates the legacy
|
|
95
95
|
// comma-separated / JSON-string forms so existing rows keep working.
|
|
96
96
|
multiple: true,
|
|
97
|
-
|
|
97
|
+
// [#4639] `bulk_*` fire on predicate writes (`multi: true`), whose
|
|
98
|
+
// delivery carries `matched` instead of a record — opt-in precisely
|
|
99
|
+
// because that body is a different shape. Kept in step with
|
|
100
|
+
// `WebhookTriggerType` (`@objectstack/spec/automation`), which is the
|
|
101
|
+
// contract the auto-enqueuer validates against.
|
|
102
|
+
description: "Record events that fire this webhook (bulk_* deliver a count, not a record)",
|
|
98
103
|
group: "Definition"
|
|
99
104
|
}
|
|
100
105
|
),
|
|
@@ -178,4 +183,4 @@ var SysWebhook = _data.ObjectSchema.create({
|
|
|
178
183
|
|
|
179
184
|
|
|
180
185
|
exports.SysWebhook = SysWebhook;
|
|
181
|
-
//# sourceMappingURL=chunk-
|
|
186
|
+
//# sourceMappingURL=chunk-JQUVS5KK.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/objectstack/objectstack/packages/plugins/plugin-webhooks/dist/chunk-JQUVS5KK.cjs","../src/sys-webhook.object.ts"],"names":[],"mappings":"AAAA;ACEA,8CAAoC;AAoC7B,IAAM,WAAA,EAAa,kBAAA,CAAa,MAAA,CAAO;AAAA,EAC5C,IAAA,EAAM,aAAA;AAAA,EACN,KAAA,EAAO,SAAA;AAAA,EACP,WAAA,EAAa,UAAA;AAAA,EACb,IAAA,EAAM,SAAA;AAAA,EACN,QAAA,EAAU,IAAA;AAAA,EACV,SAAA,EAAW,QAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,WAAA,EAAa,EAAE,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA;AAAA,EACrE,WAAA,EAAa,uPAAA;AAAA,EACb,gBAAA,EAAkB,MAAA;AAAA,EAClB,SAAA,EAAW,MAAA;AAAA;AAAA,EACX,WAAA,EAAa,SAAA;AAAA,EACb,eAAA,EAAiB,CAAC,MAAA,EAAQ,aAAA,EAAe,KAAA,EAAO,QAAA,EAAU,YAAY,CAAA;AAAA,EAEtE,SAAA,EAAW;AAAA,IACT,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,MAAA;AAAA,MACN,IAAA,EAAM,QAAA;AAAA,MACN,KAAA,EAAO,QAAA;AAAA,MACP,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,EAAU,MAAA,EAAQ,cAAc,CAAA;AAAA,MAClD,OAAA,EAAS,CAAC,OAAA,EAAS,aAAA,EAAe,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,YAAY,CAAA;AAAA,MACzE,MAAA,EAAQ,CAAC,EAAE,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,QAAA,EAAU,KAAA,EAAO,KAAK,CAAC,CAAA;AAAA,MAC7D,IAAA,EAAM,CAAC,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MACvC,UAAA,EAAY,EAAE,QAAA,EAAU,GAAG;AAAA,IAC7B,CAAA;AAAA,IACA,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,MAAA;AAAA,MACN,IAAA,EAAM,UAAA;AAAA,MACN,KAAA,EAAO,UAAA;AAAA,MACP,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,EAAU,MAAA,EAAQ,cAAc,CAAA;AAAA,MAClD,OAAA,EAAS,CAAC,OAAA,EAAS,aAAA,EAAe,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,YAAY,CAAA;AAAA,MACzE,MAAA,EAAQ,CAAC,EAAE,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,QAAA,EAAU,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MAC9D,IAAA,EAAM,CAAC,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MACvC,UAAA,EAAY,EAAE,QAAA,EAAU,GAAG;AAAA,IAC7B,CAAA;AAAA,IACA,SAAA,EAAW;AAAA,MACT,IAAA,EAAM,MAAA;AAAA,MACN,IAAA,EAAM,WAAA;AAAA,MACN,KAAA,EAAO,WAAA;AAAA,MACP,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,EAAU,MAAA,EAAQ,cAAc,CAAA;AAAA,MAClD,OAAA,EAAS,CAAC,aAAA,EAAe,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,YAAY,CAAA;AAAA,MAC/D,IAAA,EAAM,CAAC,EAAE,KAAA,EAAO,aAAA,EAAe,KAAA,EAAO,MAAM,CAAA,EAAG,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MAC/E,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAC,EAAE,KAAA,EAAO,aAAA,EAAe,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,MAAM,CAAC,EAAE,CAAA;AAAA,MAC/E,UAAA,EAAY,EAAE,QAAA,EAAU,IAAI;AAAA,IAC9B,CAAA;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,IAAA,EAAM,MAAA;AAAA,MACN,IAAA,EAAM,cAAA;AAAA,MACN,KAAA,EAAO,KAAA;AAAA,MACP,IAAA,EAAM,EAAE,QAAA,EAAU,QAAA,EAAU,MAAA,EAAQ,cAAc,CAAA;AAAA,MAClD,OAAA,EAAS,CAAC,OAAA,EAAS,aAAA,EAAe,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,YAAY,CAAA;AAAA,MACzE,IAAA,EAAM,CAAC,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MACvC,UAAA,EAAY,EAAE,QAAA,EAAU,GAAG;AAAA,IAC7B;AAAA,EACF,CAAA;AAAA,EAEA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,WAAA,CAAM,IAAA,CAAK,EAAE,KAAA,EAAO,YAAA,EAAc,QAAA,EAAU,IAAA,EAAM,QAAA,EAAU,IAAA,EAAM,KAAA,EAAO,SAAS,CAAC,CAAA;AAAA,IAEvF,IAAA,EAAM,WAAA,CAAM,IAAA,CAAK;AAAA,MACf,KAAA,EAAO,MAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,WAAA,EAAa,4DAAA;AAAA,MACb,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,KAAA,EAAO,WAAA,CAAM,IAAA,CAAK;AAAA,MAChB,KAAA,EAAO,eAAA;AAAA,MACP,QAAA,EAAU,KAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,WAAA,EAAa,WAAA,CAAM,IAAA,CAAK;AAAA,MACtB,KAAA,EAAO,QAAA;AAAA,MACP,QAAA,EAAU,KAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA;AAAA;AAAA,MAGX,MAAA,EAAQ,YAAA;AAAA,MACR,WAAA,EAAa,gFAAA;AAAA,MACb,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,QAAA,EAAU,WAAA,CAAM,MAAA;AAAA,MACd,CAAC,QAAA,EAAU,QAAA,EAAU,QAAA,EAAU,aAAA,EAAe,aAAa,CAAA;AAAA,MAC3D;AAAA,QACE,KAAA,EAAO,UAAA;AAAA,QACP,QAAA,EAAU,KAAA;AAAA;AAAA;AAAA;AAAA,QAIV,QAAA,EAAU,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMV,WAAA,EAAa,6EAAA;AAAA,QACb,KAAA,EAAO;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IAEA,GAAA,EAAK,WAAA,CAAM,IAAA,CAAK;AAAA,MACd,KAAA,EAAO,YAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,SAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAa,0CAAA;AAAA,MACb,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,MAAA,EAAQ,WAAA,CAAM,MAAA;AAAA,MACZ,CAAC,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,QAAQ,CAAA;AAAA,MACxC;AAAA,QACE,KAAA,EAAO,aAAA;AAAA,QACP,QAAA,EAAU,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKV,YAAA,EAAc,MAAA;AAAA,QACd,WAAA,EAAa,2CAAA;AAAA,QACb,KAAA,EAAO;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IAEA,WAAA,EAAa,WAAA,CAAM,QAAA,CAAS,EAAE,KAAA,EAAO,aAAA,EAAe,QAAA,EAAU,KAAA,EAAO,KAAA,EAAO,aAAa,CAAC,CAAA;AAAA,IAE1F,MAAA,EAAQ,WAAA,CAAM,OAAA,CAAQ;AAAA,MACpB,KAAA,EAAO,QAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,YAAA,EAAc,IAAA;AAAA,MACd,WAAA,EAAa,iDAAA;AAAA,MACb,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,eAAA,EAAiB,WAAA,CAAM,QAAA,CAAS;AAAA,MAC9B,KAAA,EAAO,YAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,WAAA,EAAa,kHAAA;AAAA,MACb,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASD,UAAA,EAAY,WAAA,CAAM,MAAA;AAAA,MAChB,CAAC,UAAA,EAAY,SAAA,EAAW,OAAO,CAAA;AAAA,MAC/B;AAAA,QACE,KAAA,EAAO,YAAA;AAAA,QACP,QAAA,EAAU,KAAA;AAAA,QACV,QAAA,EAAU,IAAA;AAAA,QACV,YAAA,EAAc,OAAA;AAAA,QACd,WAAA,EACE,uJAAA;AAAA,QAEF,KAAA,EAAO;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IAEA,UAAA,EAAY,WAAA,CAAM,OAAA,CAAQ;AAAA,MACxB,KAAA,EAAO,YAAA;AAAA,MACP,QAAA,EAAU,KAAA;AAAA,MACV,QAAA,EAAU,IAAA;AAAA,MACV,YAAA,EAAc,KAAA;AAAA,MACd,WAAA,EACE,gLAAA;AAAA,MAEF,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,UAAA,EAAY,WAAA,CAAM,QAAA,CAAS;AAAA,MACzB,KAAA,EAAO,YAAA;AAAA,MACP,QAAA,EAAU,IAAA;AAAA,MACV,YAAA,EAAc,OAAA;AAAA,MACd,QAAA,EAAU,IAAA;AAAA,MACV,KAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,IAED,UAAA,EAAY,WAAA,CAAM,QAAA,CAAS,EAAE,KAAA,EAAO,YAAA,EAAc,QAAA,EAAU,KAAA,EAAO,KAAA,EAAO,SAAS,CAAC;AAAA,EACtF,CAAA;AAAA,EAEA,OAAA,EAAS;AAAA,IACP,EAAE,MAAA,EAAQ,CAAC,MAAM,CAAA,EAAG,MAAA,EAAQ,KAAK,CAAA;AAAA,IACjC,EAAE,MAAA,EAAQ,CAAC,aAAa,EAAE,CAAA;AAAA,IAC1B,EAAE,MAAA,EAAQ,CAAC,QAAA,EAAU,aAAa,EAAE;AAAA,EACtC;AACF,CAAC,CAAA;ADtDD;AACA;AACE;AACF,gCAAC","file":"/home/runner/work/objectstack/objectstack/packages/plugins/plugin-webhooks/dist/chunk-JQUVS5KK.cjs","sourcesContent":[null,"// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_webhook — Outbound HTTP integration configuration (runtime).\n *\n * Persists a single {@link Webhook} envelope per row so administrators\n * can author, enable/disable, and edit webhook subscriptions from the\n * Studio UI without code changes. The canonical Zod schema for the\n * `definition_json` envelope lives at `@objectstack/spec/automation/webhook`.\n *\n * ## Two authoring doors, one row\n * Rows land here two ways, distinguished by the `managed_by` provenance column:\n * - **admin** — created/edited directly through this object's CRUD UI.\n * - **package** — declared in code (`defineStack({ webhooks })` /\n * `defineWebhook()`) and materialized on boot by\n * `bootstrapDeclaredWebhooks` (#3461). Re-seeded every boot, but an admin\n * edit stamps `customized: true` and freezes the row (seed-not-clobber,\n * mirrors `sys_sharing_rule` #2909).\n *\n * One row per `name`. This plugin's {@link AutoEnqueuer} loads active rows on\n * boot + on `sys_webhook:changed` events, and turns matching `data.record.*`\n * events into deliveries on the shared `service-messaging` HTTP outbox\n * (ADR-0018 M3 — `sys_http_delivery`, drained by the messaging dispatcher).\n *\n * Ownership (ADR-0029 K2.a): this object is **owned by\n * `@objectstack/plugin-webhooks`** — the plugin that consumes these rows. It\n * used to live in the `@objectstack/platform-objects` monolith and be imported\n * here; the definition now lives with its owner so the plugin ships both data\n * and behavior as one unit.\n *\n * Platform-wide on purpose: every project (standalone, single-tenant,\n * cloud) can integrate with external systems (Slack, Stripe, internal\n * services) the same way.\n *\n * @namespace sys\n */\nexport const SysWebhook = ObjectSchema.create({\n name: 'sys_webhook',\n label: 'Webhook',\n pluralLabel: 'Webhooks',\n icon: 'webhook',\n isSystem: true,\n managedBy: 'config',\n // Authoring a webhook from the UI requires a structured form for the\n // headers / auth / retry / payload blocks — the generic JSON textarea\n // is acceptable as a v1 until a dedicated builder lands. Re-enable\n // create/edit/delete so admins can at least toggle `active` and edit\n // simple URL/method fields without round-tripping through code.\n userActions: { create: true, edit: true, delete: true, import: false },\n description: 'Outbound HTTP webhook subscription. Declared in code via defineStack({ webhooks }) / defineWebhook() (materialized into rows on boot) or authored directly in the Studio editor; dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.',\n displayNameField: 'name',\n nameField: 'name', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{label}',\n highlightFields: ['name', 'object_name', 'url', 'active', 'updated_at'],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n inactive: {\n type: 'grid',\n name: 'inactive',\n label: 'Inactive',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: false }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_object: {\n type: 'grid',\n name: 'by_object',\n label: 'By Object',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['object_name', 'label', 'url', 'active', 'updated_at'],\n sort: [{ field: 'object_name', order: 'asc' }, { field: 'label', order: 'asc' }],\n grouping: { fields: [{ field: 'object_name', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n all_webhooks: {\n type: 'grid',\n name: 'all_webhooks',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook' },\n columns: ['label', 'object_name', 'url', 'method', 'active', 'updated_at'],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Webhook ID', required: true, readonly: true, group: 'System' }),\n\n name: Field.text({\n label: 'Name',\n required: true,\n maxLength: 100,\n description: 'Unique snake_case name — referenced in logs and audit',\n group: 'Definition',\n }),\n\n label: Field.text({\n label: 'Display Label',\n required: false,\n maxLength: 200,\n group: 'Definition',\n }),\n\n object_name: Field.text({\n label: 'Object',\n required: false,\n maxLength: 100,\n // Object picker (same widget as sys_sharing_rule) instead of a free-text\n // machine name. Falls back to a text input when the widget is unavailable.\n widget: 'object-ref',\n description: 'Short object name whose record events (create/update/delete) fire this webhook',\n group: 'Definition',\n }),\n\n triggers: Field.select(\n ['create', 'update', 'delete', 'bulk_update', 'bulk_delete'],\n {\n label: 'Triggers',\n required: false,\n // Multi-select instead of a hand-typed comma-separated string. Stored as\n // an array; the auto-enqueuer parser also tolerates the legacy\n // comma-separated / JSON-string forms so existing rows keep working.\n multiple: true,\n // [#4639] `bulk_*` fire on predicate writes (`multi: true`), whose\n // delivery carries `matched` instead of a record — opt-in precisely\n // because that body is a different shape. Kept in step with\n // `WebhookTriggerType` (`@objectstack/spec/automation`), which is the\n // contract the auto-enqueuer validates against.\n description: 'Record events that fire this webhook (bulk_* deliver a count, not a record)',\n group: 'Definition',\n },\n ),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'External endpoint that receives the POST',\n group: 'Definition',\n }),\n\n method: Field.select(\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],\n {\n label: 'HTTP Method',\n required: true,\n // Select instead of free text. Option values are lowercased by the\n // Field.select helper (get/post/…); the auto-enqueuer upper-cases the\n // resolved method before delivery, so existing 'POST' rows and the\n // lowercase option values both normalise correctly.\n defaultValue: 'post',\n description: 'HTTP method used for the callback request',\n group: 'Definition',\n },\n ),\n\n description: Field.textarea({ label: 'Description', required: false, group: 'Definition' }),\n\n active: Field.boolean({\n label: 'Active',\n required: true,\n defaultValue: true,\n description: 'Inactive webhooks are skipped by the dispatcher',\n group: 'Definition',\n }),\n\n definition_json: Field.textarea({\n label: 'Definition',\n required: true,\n description: 'Serialised Webhook JSON (see @objectstack/spec/automation/webhook) — full headers/auth/retry/payload config',\n group: 'Definition',\n }),\n\n // ── Provenance (#3461 — record-authoritative seed-not-clobber) ──\n // Mirrors sys_sharing_rule (#2909). Both columns are `readonly`: the\n // engine strips them from non-system payloads (forge/clear-proof), while\n // bootstrapDeclaredWebhooks and the provenance stamp hook write with\n // isSystem. Deliberately NOT a write gate: webhooks are a first-class admin\n // authoring/tuning surface — admins may edit or deactivate a package row;\n // the seeder simply stops overwriting it once `customized` is stamped.\n managed_by: Field.select(\n ['platform', 'package', 'admin'],\n {\n label: 'Managed By',\n required: false,\n readonly: true,\n defaultValue: 'admin',\n description:\n 'Record provenance: platform = framework built-in / package = app/package-declared ' +\n '(boot-seeded from defineStack webhooks) / admin = created in Setup.',\n group: 'System',\n },\n ),\n\n customized: Field.boolean({\n label: 'Customized',\n required: false,\n readonly: true,\n defaultValue: false,\n description:\n 'Set when an admin edits a package-declared webhook; boot seeding will no longer ' +\n 'overwrite the row (a deactivated noisy webhook survives redeploys). Meaningless on admin rows.',\n group: 'System',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n { fields: ['name'], unique: true },\n { fields: ['object_name'] },\n { fields: ['active', 'object_name'] },\n ],\n});\n"]}
|