@objectstack/plugin-webhooks 5.0.0 → 5.2.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.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +35 -13
  2. package/CHANGELOG.md +9 -28
  3. package/dist/chunk-JN76ZRWN.js +164 -0
  4. package/dist/chunk-JN76ZRWN.js.map +1 -0
  5. package/dist/chunk-M4M5FWIH.cjs +15 -0
  6. package/dist/chunk-M4M5FWIH.cjs.map +1 -0
  7. package/dist/chunk-NYSUNT6X.js +15 -0
  8. package/dist/chunk-NYSUNT6X.js.map +1 -0
  9. package/dist/chunk-OW7ESXOK.cjs +164 -0
  10. package/dist/chunk-OW7ESXOK.cjs.map +1 -0
  11. package/dist/index.cjs +747 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +455 -0
  14. package/dist/index.d.ts +425 -74
  15. package/dist/index.js +712 -218
  16. package/dist/index.js.map +1 -1
  17. package/dist/outbox-bPQmKYPN.d.cts +128 -0
  18. package/dist/outbox-bPQmKYPN.d.ts +128 -0
  19. package/dist/schema.cjs +9 -0
  20. package/dist/schema.cjs.map +1 -0
  21. package/dist/schema.d.cts +4772 -0
  22. package/dist/schema.d.ts +4772 -0
  23. package/dist/schema.js +9 -0
  24. package/dist/schema.js.map +1 -0
  25. package/dist/sql-outbox.cjs +184 -0
  26. package/dist/sql-outbox.cjs.map +1 -0
  27. package/dist/sql-outbox.d.cts +54 -0
  28. package/dist/sql-outbox.d.ts +54 -0
  29. package/dist/sql-outbox.js +184 -0
  30. package/dist/sql-outbox.js.map +1 -0
  31. package/package.json +30 -10
  32. package/src/auto-enqueuer.test.ts +391 -0
  33. package/src/auto-enqueuer.ts +335 -0
  34. package/src/dispatcher.test.ts +324 -0
  35. package/src/dispatcher.ts +218 -0
  36. package/src/http-sender.ts +187 -0
  37. package/src/index.ts +48 -12
  38. package/src/memory-outbox.ts +127 -0
  39. package/src/outbox.ts +141 -0
  40. package/src/partition.ts +19 -0
  41. package/src/retention.test.ts +116 -0
  42. package/src/retention.ts +144 -0
  43. package/src/schema.ts +22 -0
  44. package/src/sql-outbox.test.ts +410 -0
  45. package/src/sql-outbox.ts +282 -0
  46. package/src/sys-webhook-delivery.object.ts +202 -0
  47. package/src/webhook-outbox-plugin.ts +280 -0
  48. package/tsconfig.json +5 -13
  49. package/tsup.config.ts +14 -0
  50. package/dist/index.d.mts +0 -104
  51. package/dist/index.mjs +0 -216
  52. package/dist/index.mjs.map +0 -1
  53. package/src/webhooks-plugin.test.ts +0 -218
  54. package/src/webhooks-plugin.ts +0 -294
@@ -1,22 +1,44 @@
1
1
 
2
- > @objectstack/plugin-webhooks@5.0.0 build /home/runner/work/framework/framework/packages/plugins/plugin-webhooks
3
- > tsup --config ../../../tsup.config.ts
2
+ > @objectstack/plugin-webhooks@5.2.0 build /home/runner/work/framework/framework/packages/plugins/plugin-webhooks
3
+ > tsup
4
4
 
5
- CLI Building entry: src/index.ts
5
+ CLI Building entry: src/index.ts, src/schema.ts, src/sql-outbox.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.1
8
- CLI Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
8
+ CLI Using tsup config: /home/runner/work/framework/framework/packages/plugins/plugin-webhooks/tsup.config.ts
9
9
  CLI Target: es2020
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.js 9.02 KB
14
- CJS dist/index.js.map 16.38 KB
15
- CJS ⚡️ Build success in 82ms
16
- ESM dist/index.mjs 7.36 KB
17
- ESM dist/index.mjs.map 15.67 KB
18
- ESM ⚡️ Build success in 83ms
13
+ ESM dist/index.js 23.08 KB
14
+ ESM dist/schema.js 175.00 B
15
+ ESM dist/sql-outbox.js 5.17 KB
16
+ ESM dist/chunk-NYSUNT6X.js 357.00 B
17
+ ESM dist/chunk-JN76ZRWN.js 6.38 KB
18
+ ESM dist/index.js.map 62.58 KB
19
+ ESM dist/schema.js.map 71.00 B
20
+ ESM dist/sql-outbox.js.map 13.98 KB
21
+ ESM dist/chunk-NYSUNT6X.js.map 1.05 KB
22
+ ESM dist/chunk-JN76ZRWN.js.map 12.85 KB
23
+ ESM ⚡️ Build success in 861ms
24
+ CJS dist/index.cjs 27.97 KB
25
+ CJS dist/sql-outbox.cjs 6.54 KB
26
+ CJS dist/schema.cjs 309.00 B
27
+ CJS dist/chunk-M4M5FWIH.cjs 444.00 B
28
+ CJS dist/chunk-OW7ESXOK.cjs 6.62 KB
29
+ CJS dist/index.cjs.map 56.74 KB
30
+ CJS dist/sql-outbox.cjs.map 16.73 KB
31
+ CJS dist/schema.cjs.map 288.00 B
32
+ CJS dist/chunk-M4M5FWIH.cjs.map 1.47 KB
33
+ CJS dist/chunk-OW7ESXOK.cjs.map 15.42 KB
34
+ CJS ⚡️ Build success in 897ms
19
35
  DTS Build start
20
- DTS ⚡️ Build success in 9342ms
21
- DTS dist/index.d.mts 3.46 KB
22
- DTS dist/index.d.ts 3.46 KB
36
+ DTS ⚡️ Build success in 29536ms
37
+ DTS dist/index.d.ts 16.80 KB
38
+ DTS dist/schema.d.ts 211.24 KB
39
+ DTS dist/sql-outbox.d.ts 2.39 KB
40
+ DTS dist/outbox-bPQmKYPN.d.ts 4.54 KB
41
+ DTS dist/index.d.cts 16.80 KB
42
+ DTS dist/schema.d.cts 211.24 KB
43
+ DTS dist/sql-outbox.d.cts 2.39 KB
44
+ DTS dist/outbox-bPQmKYPN.d.cts 4.54 KB
package/CHANGELOG.md CHANGED
@@ -1,33 +1,14 @@
1
1
  # @objectstack/plugin-webhooks
2
2
 
3
- ## 5.0.0
3
+ ## 5.2.0
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Updated dependencies [2f9073a]
8
- - @objectstack/spec@5.0.0
9
- - @objectstack/core@5.0.0
10
-
11
- ## 4.2.0
12
-
13
- ### Patch Changes
14
-
15
- - Updated dependencies [2869891]
16
- - @objectstack/spec@4.2.0
17
- - @objectstack/core@4.2.0
18
-
19
- ## 4.1.1
20
-
21
- ### Patch Changes
22
-
23
- - @objectstack/spec@4.1.1
24
- - @objectstack/core@4.1.1
25
-
26
- ## 4.0.1
27
-
28
- ### Patch Changes
29
-
30
- - Updated dependencies [2108c30]
31
- - Updated dependencies [23db640]
32
- - @objectstack/spec@4.1.0
33
- - @objectstack/core@4.1.0
7
+ - Updated dependencies [bab2b20]
8
+ - Updated dependencies [fa011d8]
9
+ - Updated dependencies [f0f7c27]
10
+ - Updated dependencies [b806f58]
11
+ - @objectstack/platform-objects@5.2.0
12
+ - @objectstack/spec@5.2.0
13
+ - @objectstack/core@5.2.0
14
+ - @objectstack/service-cluster@5.1.1
@@ -0,0 +1,164 @@
1
+ // src/sys-webhook-delivery.object.ts
2
+ import { Field, ObjectSchema } from "@objectstack/spec/data";
3
+ var SysWebhookDelivery = ObjectSchema.create({
4
+ name: "sys_webhook_delivery",
5
+ label: "Webhook Delivery",
6
+ pluralLabel: "Webhook Deliveries",
7
+ icon: "package",
8
+ isSystem: true,
9
+ managedBy: "config",
10
+ userActions: { create: false, edit: false, delete: false, import: false },
11
+ description: "Durable outbox row for one webhook attempt. Managed by @objectstack/plugin-webhooks; do not write directly.",
12
+ displayNameField: "id",
13
+ titleFormat: "{event_type} \u2192 {url}",
14
+ compactLayout: ["event_type", "url", "status", "attempts", "next_retry_at"],
15
+ listViews: {
16
+ recent: {
17
+ type: "grid",
18
+ name: "recent",
19
+ label: "Recent",
20
+ data: { provider: "object", object: "sys_webhook_delivery" },
21
+ columns: ["event_type", "url", "status", "attempts", "response_code", "updated_at"],
22
+ sort: [{ field: "updated_at", order: "desc" }],
23
+ pagination: { pageSize: 50 }
24
+ },
25
+ failures: {
26
+ type: "grid",
27
+ name: "failures",
28
+ label: "Failures",
29
+ data: { provider: "object", object: "sys_webhook_delivery" },
30
+ columns: ["event_type", "url", "status", "attempts", "response_code", "error", "updated_at"],
31
+ filter: [{ field: "status", operator: "in", value: ["failed", "dead"] }],
32
+ sort: [{ field: "updated_at", order: "desc" }],
33
+ pagination: { pageSize: 50 }
34
+ },
35
+ in_flight: {
36
+ type: "grid",
37
+ name: "in_flight",
38
+ label: "In Flight",
39
+ data: { provider: "object", object: "sys_webhook_delivery" },
40
+ columns: ["event_type", "url", "attempts", "claimed_by", "claimed_at"],
41
+ filter: [{ field: "status", operator: "equals", value: "in_flight" }],
42
+ sort: [{ field: "claimed_at", order: "desc" }],
43
+ pagination: { pageSize: 50 }
44
+ },
45
+ pending: {
46
+ type: "grid",
47
+ name: "pending",
48
+ label: "Pending",
49
+ data: { provider: "object", object: "sys_webhook_delivery" },
50
+ columns: ["event_type", "url", "attempts", "next_retry_at", "updated_at"],
51
+ filter: [{ field: "status", operator: "equals", value: "pending" }],
52
+ sort: [{ field: "next_retry_at", order: "asc" }],
53
+ pagination: { pageSize: 50 }
54
+ },
55
+ by_status: {
56
+ type: "grid",
57
+ name: "by_status",
58
+ label: "By Status",
59
+ data: { provider: "object", object: "sys_webhook_delivery" },
60
+ columns: ["status", "event_type", "url", "attempts", "updated_at"],
61
+ sort: [{ field: "status", order: "asc" }, { field: "updated_at", order: "desc" }],
62
+ grouping: { fields: [{ field: "status", order: "asc", collapsed: false }] },
63
+ pagination: { pageSize: 100 }
64
+ },
65
+ by_webhook: {
66
+ type: "grid",
67
+ name: "by_webhook",
68
+ label: "By Webhook",
69
+ data: { provider: "object", object: "sys_webhook_delivery" },
70
+ columns: ["webhook_id", "event_type", "status", "attempts", "updated_at"],
71
+ sort: [{ field: "webhook_id", order: "asc" }, { field: "updated_at", order: "desc" }],
72
+ grouping: { fields: [{ field: "webhook_id", order: "asc", collapsed: true }] },
73
+ pagination: { pageSize: 100 }
74
+ },
75
+ all_deliveries: {
76
+ type: "grid",
77
+ name: "all_deliveries",
78
+ label: "All",
79
+ data: { provider: "object", object: "sys_webhook_delivery" },
80
+ columns: ["event_type", "url", "status", "attempts", "response_code", "updated_at"],
81
+ sort: [{ field: "updated_at", order: "desc" }],
82
+ pagination: { pageSize: 100 }
83
+ }
84
+ },
85
+ fields: {
86
+ id: Field.text({
87
+ label: "Delivery ID",
88
+ required: true,
89
+ maxLength: 64,
90
+ description: "UUID \u2014 also doubles as the receiver-side idempotency key"
91
+ }),
92
+ webhook_id: Field.text({
93
+ label: "Webhook ID",
94
+ required: true,
95
+ maxLength: 64,
96
+ description: "FK to sys_webhook.id (loosely coupled \u2014 denormalised URL/secret on row)"
97
+ }),
98
+ event_id: Field.text({
99
+ label: "Event ID",
100
+ required: true,
101
+ maxLength: 128,
102
+ description: "Source event id; UNIQUE(event_id, webhook_id) for dedup"
103
+ }),
104
+ event_type: Field.text({
105
+ label: "Event Type",
106
+ required: true,
107
+ maxLength: 128,
108
+ description: "e.g. data.record.created"
109
+ }),
110
+ url: Field.text({
111
+ label: "Target URL",
112
+ required: true,
113
+ maxLength: 2048,
114
+ description: "Snapshotted at enqueue so config edits do not rewrite live rows"
115
+ }),
116
+ method: Field.text({ label: "Method", required: false, maxLength: 10 }),
117
+ headers_json: Field.textarea({ label: "Headers JSON", required: false }),
118
+ secret: Field.text({ label: "HMAC Secret", required: false, maxLength: 256 }),
119
+ timeout_ms: Field.number({ label: "Timeout (ms)", required: false }),
120
+ payload_json: Field.textarea({ label: "Payload JSON", required: true }),
121
+ partition_key: Field.number({
122
+ label: "Partition",
123
+ required: true,
124
+ description: "hash(webhook_id) mod partitionCount \u2014 precomputed for cheap WHERE"
125
+ }),
126
+ status: Field.text({
127
+ label: "Status",
128
+ required: true,
129
+ defaultValue: "pending",
130
+ maxLength: 16,
131
+ description: "pending | in_flight | success | failed | dead"
132
+ }),
133
+ attempts: Field.number({
134
+ label: "Attempts",
135
+ required: true,
136
+ defaultValue: 0,
137
+ description: "Number of POST attempts made so far"
138
+ }),
139
+ claimed_by: Field.text({ label: "Claimed By", required: false, maxLength: 128 }),
140
+ claimed_at: Field.number({ label: "Claimed At (ms)", required: false }),
141
+ next_retry_at: Field.number({ label: "Next Retry At (ms)", required: false }),
142
+ last_attempted_at: Field.number({ label: "Last Attempted At (ms)", required: false }),
143
+ response_code: Field.number({ label: "HTTP Status", required: false }),
144
+ response_body: Field.textarea({ label: "Response Body (capped)", required: false }),
145
+ error: Field.textarea({ label: "Error", required: false }),
146
+ created_at: Field.number({ label: "Created At (ms)", required: true }),
147
+ updated_at: Field.number({ label: "Updated At (ms)", required: true })
148
+ },
149
+ indexes: [
150
+ { fields: ["event_id", "webhook_id"], unique: true },
151
+ // Hot path: claim query
152
+ { fields: ["status", "partition_key", "next_retry_at"] },
153
+ // Reaper: scan stale in_flight rows by claimed_at
154
+ { fields: ["status", "claimed_at"] },
155
+ { fields: ["webhook_id"] }
156
+ ]
157
+ });
158
+ var SYS_WEBHOOK_DELIVERY = "sys_webhook_delivery";
159
+
160
+ export {
161
+ SysWebhookDelivery,
162
+ SYS_WEBHOOK_DELIVERY
163
+ };
164
+ //# sourceMappingURL=chunk-JN76ZRWN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sys-webhook-delivery.object.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Field, ObjectSchema } from '@objectstack/spec/data';\n\n/**\n * sys_webhook_delivery — Durable outbox row for one HTTP attempt.\n *\n * Schema is owned by `@objectstack/plugin-webhooks`. Add it to your stack\n * via:\n *\n * import { SysWebhookDelivery } from '@objectstack/plugin-webhooks/schema';\n * defineStack({ objects: [SysWebhookDelivery, ...], plugins: [...] });\n *\n * Designed for the SqlWebhookOutbox claim algorithm:\n *\n * 1. Producers INSERT pending rows (dedup'd by (event_id, webhook_id)).\n * 2. The dispatcher's per-partition lock-holder runs:\n * SELECT id WHERE status='pending' AND partition_key=? AND (next_retry_at <= now OR null)\n * UPDATE SET status='in_flight' WHERE id IN (...) AND status='pending' ← atomic claim\n * POST to target URL\n * UPDATE SET status=success/pending/dead, attempts=attempts+1, ...\n *\n * `partition_key` is precomputed on enqueue (hash(webhook_id) mod N) so the\n * dispatcher can filter cheaply without DB-side hash functions.\n *\n * Indexes are tuned for the hot path: `(status, partition_key, next_retry_at)`\n * is the claim query; `(event_id, webhook_id)` is the dedup uniqueness.\n *\n * @namespace sys\n */\nexport const SysWebhookDelivery = ObjectSchema.create({\n name: 'sys_webhook_delivery',\n label: 'Webhook Delivery',\n pluralLabel: 'Webhook Deliveries',\n icon: 'package',\n isSystem: true,\n managedBy: 'config',\n userActions: { create: false, edit: false, delete: false, import: false },\n description:\n 'Durable outbox row for one webhook attempt. Managed by @objectstack/plugin-webhooks; do not write directly.',\n displayNameField: 'id',\n titleFormat: '{event_type} → {url}',\n compactLayout: ['event_type', 'url', 'status', 'attempts', 'next_retry_at'],\n\n listViews: {\n recent: {\n type: 'grid',\n name: 'recent',\n label: 'Recent',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n failures: {\n type: 'grid',\n name: 'failures',\n label: 'Failures',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'error', 'updated_at'],\n filter: [{ field: 'status', operator: 'in', value: ['failed', 'dead'] }],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n in_flight: {\n type: 'grid',\n name: 'in_flight',\n label: 'In Flight',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'claimed_by', 'claimed_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'in_flight' }],\n sort: [{ field: 'claimed_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n pending: {\n type: 'grid',\n name: 'pending',\n label: 'Pending',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'next_retry_at', 'updated_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'pending' }],\n sort: [{ field: 'next_retry_at', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_status: {\n type: 'grid',\n name: 'by_status',\n label: 'By Status',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['status', 'event_type', 'url', 'attempts', 'updated_at'],\n sort: [{ field: 'status', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'status', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n by_webhook: {\n type: 'grid',\n name: 'by_webhook',\n label: 'By Webhook',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['webhook_id', 'event_type', 'status', 'attempts', 'updated_at'],\n sort: [{ field: 'webhook_id', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'webhook_id', order: 'asc', collapsed: true }] },\n pagination: { pageSize: 100 },\n },\n all_deliveries: {\n type: 'grid',\n name: 'all_deliveries',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({\n label: 'Delivery ID',\n required: true,\n maxLength: 64,\n description: 'UUID — also doubles as the receiver-side idempotency key',\n }),\n\n webhook_id: Field.text({\n label: 'Webhook ID',\n required: true,\n maxLength: 64,\n description: 'FK to sys_webhook.id (loosely coupled — denormalised URL/secret on row)',\n }),\n\n event_id: Field.text({\n label: 'Event ID',\n required: true,\n maxLength: 128,\n description: 'Source event id; UNIQUE(event_id, webhook_id) for dedup',\n }),\n\n event_type: Field.text({\n label: 'Event Type',\n required: true,\n maxLength: 128,\n description: 'e.g. data.record.created',\n }),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'Snapshotted at enqueue so config edits do not rewrite live rows',\n }),\n\n method: Field.text({ label: 'Method', required: false, maxLength: 10 }),\n headers_json: Field.textarea({ label: 'Headers JSON', required: false }),\n secret: Field.text({ label: 'HMAC Secret', required: false, maxLength: 256 }),\n timeout_ms: Field.number({ label: 'Timeout (ms)', required: false }),\n payload_json: Field.textarea({ label: 'Payload JSON', required: true }),\n\n partition_key: Field.number({\n label: 'Partition',\n required: true,\n description: 'hash(webhook_id) mod partitionCount — precomputed for cheap WHERE',\n }),\n\n status: Field.text({\n label: 'Status',\n required: true,\n defaultValue: 'pending',\n maxLength: 16,\n description: 'pending | in_flight | success | failed | dead',\n }),\n\n attempts: Field.number({\n label: 'Attempts',\n required: true,\n defaultValue: 0,\n description: 'Number of POST attempts made so far',\n }),\n\n claimed_by: Field.text({ label: 'Claimed By', required: false, maxLength: 128 }),\n claimed_at: Field.number({ label: 'Claimed At (ms)', required: false }),\n next_retry_at: Field.number({ label: 'Next Retry At (ms)', required: false }),\n last_attempted_at: Field.number({ label: 'Last Attempted At (ms)', required: false }),\n response_code: Field.number({ label: 'HTTP Status', required: false }),\n response_body: Field.textarea({ label: 'Response Body (capped)', required: false }),\n error: Field.textarea({ label: 'Error', required: false }),\n\n created_at: Field.number({ label: 'Created At (ms)', required: true }),\n updated_at: Field.number({ label: 'Updated At (ms)', required: true }),\n },\n\n indexes: [\n { fields: ['event_id', 'webhook_id'], unique: true },\n // Hot path: claim query\n { fields: ['status', 'partition_key', 'next_retry_at'] },\n // Reaper: scan stale in_flight rows by claimed_at\n { fields: ['status', 'claimed_at'] },\n { fields: ['webhook_id'] },\n ],\n});\n\n/** Canonical object name — exported so SqlWebhookOutbox callers can override if needed. */\nexport const SYS_WEBHOOK_DELIVERY = 'sys_webhook_delivery' as const;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/partition.ts
2
+ function hashPartition(key, count) {
3
+ if (count <= 0) throw new Error("partition count must be > 0");
4
+ let h = 2166136261;
5
+ for (let i = 0; i < key.length; i++) {
6
+ h ^= key.charCodeAt(i);
7
+ h = Math.imul(h, 16777619);
8
+ }
9
+ return Math.abs(h | 0) % count;
10
+ }
11
+
12
+
13
+
14
+ exports.hashPartition = hashPartition;
15
+ //# sourceMappingURL=chunk-M4M5FWIH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-M4M5FWIH.cjs","../src/partition.ts"],"names":[],"mappings":"AAAA;ACUO,SAAS,aAAA,CAAc,GAAA,EAAa,KAAA,EAAuB;AAC9D,EAAA,GAAA,CAAI,MAAA,GAAS,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,6BAA6B,CAAA;AAC7D,EAAA,IAAI,EAAA,EAAI,UAAA;AACR,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GAAA,CAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,EAAA,GAAK,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AACrB,IAAA,EAAA,EAAI,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,QAAU,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,CAAC,EAAA,EAAI,KAAA;AAC7B;ADRA;AACA;AACE;AACF,sCAAC","file":"/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-M4M5FWIH.cjs","sourcesContent":[null,"// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Stable, framework-free partition hash. The dispatcher uses this to\n * assign webhooks to partitions; the in-memory outbox uses the same hash\n * to filter rows in `claim()`. Both call sites MUST agree, which is why\n * this is a single shared helper.\n *\n * Uses a 32-bit FNV-1a variant — fast, no allocations, deterministic.\n */\nexport function hashPartition(key: string, count: number): number {\n if (count <= 0) throw new Error('partition count must be > 0');\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return Math.abs(h | 0) % count;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ // src/partition.ts
2
+ function hashPartition(key, count) {
3
+ if (count <= 0) throw new Error("partition count must be > 0");
4
+ let h = 2166136261;
5
+ for (let i = 0; i < key.length; i++) {
6
+ h ^= key.charCodeAt(i);
7
+ h = Math.imul(h, 16777619);
8
+ }
9
+ return Math.abs(h | 0) % count;
10
+ }
11
+
12
+ export {
13
+ hashPartition
14
+ };
15
+ //# sourceMappingURL=chunk-NYSUNT6X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/partition.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Stable, framework-free partition hash. The dispatcher uses this to\n * assign webhooks to partitions; the in-memory outbox uses the same hash\n * to filter rows in `claim()`. Both call sites MUST agree, which is why\n * this is a single shared helper.\n *\n * Uses a 32-bit FNV-1a variant — fast, no allocations, deterministic.\n */\nexport function hashPartition(key: string, count: number): number {\n if (count <= 0) throw new Error('partition count must be > 0');\n let h = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n h ^= key.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return Math.abs(h | 0) % count;\n}\n"],"mappings":";AAUO,SAAS,cAAc,KAAa,OAAuB;AAC9D,MAAI,SAAS,EAAG,OAAM,IAAI,MAAM,6BAA6B;AAC7D,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,SAAK,IAAI,WAAW,CAAC;AACrB,QAAI,KAAK,KAAK,GAAG,QAAU;AAAA,EAC/B;AACA,SAAO,KAAK,IAAI,IAAI,CAAC,IAAI;AAC7B;","names":[]}
@@ -0,0 +1,164 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/sys-webhook-delivery.object.ts
2
+ var _data = require('@objectstack/spec/data');
3
+ var SysWebhookDelivery = _data.ObjectSchema.create({
4
+ name: "sys_webhook_delivery",
5
+ label: "Webhook Delivery",
6
+ pluralLabel: "Webhook Deliveries",
7
+ icon: "package",
8
+ isSystem: true,
9
+ managedBy: "config",
10
+ userActions: { create: false, edit: false, delete: false, import: false },
11
+ description: "Durable outbox row for one webhook attempt. Managed by @objectstack/plugin-webhooks; do not write directly.",
12
+ displayNameField: "id",
13
+ titleFormat: "{event_type} \u2192 {url}",
14
+ compactLayout: ["event_type", "url", "status", "attempts", "next_retry_at"],
15
+ listViews: {
16
+ recent: {
17
+ type: "grid",
18
+ name: "recent",
19
+ label: "Recent",
20
+ data: { provider: "object", object: "sys_webhook_delivery" },
21
+ columns: ["event_type", "url", "status", "attempts", "response_code", "updated_at"],
22
+ sort: [{ field: "updated_at", order: "desc" }],
23
+ pagination: { pageSize: 50 }
24
+ },
25
+ failures: {
26
+ type: "grid",
27
+ name: "failures",
28
+ label: "Failures",
29
+ data: { provider: "object", object: "sys_webhook_delivery" },
30
+ columns: ["event_type", "url", "status", "attempts", "response_code", "error", "updated_at"],
31
+ filter: [{ field: "status", operator: "in", value: ["failed", "dead"] }],
32
+ sort: [{ field: "updated_at", order: "desc" }],
33
+ pagination: { pageSize: 50 }
34
+ },
35
+ in_flight: {
36
+ type: "grid",
37
+ name: "in_flight",
38
+ label: "In Flight",
39
+ data: { provider: "object", object: "sys_webhook_delivery" },
40
+ columns: ["event_type", "url", "attempts", "claimed_by", "claimed_at"],
41
+ filter: [{ field: "status", operator: "equals", value: "in_flight" }],
42
+ sort: [{ field: "claimed_at", order: "desc" }],
43
+ pagination: { pageSize: 50 }
44
+ },
45
+ pending: {
46
+ type: "grid",
47
+ name: "pending",
48
+ label: "Pending",
49
+ data: { provider: "object", object: "sys_webhook_delivery" },
50
+ columns: ["event_type", "url", "attempts", "next_retry_at", "updated_at"],
51
+ filter: [{ field: "status", operator: "equals", value: "pending" }],
52
+ sort: [{ field: "next_retry_at", order: "asc" }],
53
+ pagination: { pageSize: 50 }
54
+ },
55
+ by_status: {
56
+ type: "grid",
57
+ name: "by_status",
58
+ label: "By Status",
59
+ data: { provider: "object", object: "sys_webhook_delivery" },
60
+ columns: ["status", "event_type", "url", "attempts", "updated_at"],
61
+ sort: [{ field: "status", order: "asc" }, { field: "updated_at", order: "desc" }],
62
+ grouping: { fields: [{ field: "status", order: "asc", collapsed: false }] },
63
+ pagination: { pageSize: 100 }
64
+ },
65
+ by_webhook: {
66
+ type: "grid",
67
+ name: "by_webhook",
68
+ label: "By Webhook",
69
+ data: { provider: "object", object: "sys_webhook_delivery" },
70
+ columns: ["webhook_id", "event_type", "status", "attempts", "updated_at"],
71
+ sort: [{ field: "webhook_id", order: "asc" }, { field: "updated_at", order: "desc" }],
72
+ grouping: { fields: [{ field: "webhook_id", order: "asc", collapsed: true }] },
73
+ pagination: { pageSize: 100 }
74
+ },
75
+ all_deliveries: {
76
+ type: "grid",
77
+ name: "all_deliveries",
78
+ label: "All",
79
+ data: { provider: "object", object: "sys_webhook_delivery" },
80
+ columns: ["event_type", "url", "status", "attempts", "response_code", "updated_at"],
81
+ sort: [{ field: "updated_at", order: "desc" }],
82
+ pagination: { pageSize: 100 }
83
+ }
84
+ },
85
+ fields: {
86
+ id: _data.Field.text({
87
+ label: "Delivery ID",
88
+ required: true,
89
+ maxLength: 64,
90
+ description: "UUID \u2014 also doubles as the receiver-side idempotency key"
91
+ }),
92
+ webhook_id: _data.Field.text({
93
+ label: "Webhook ID",
94
+ required: true,
95
+ maxLength: 64,
96
+ description: "FK to sys_webhook.id (loosely coupled \u2014 denormalised URL/secret on row)"
97
+ }),
98
+ event_id: _data.Field.text({
99
+ label: "Event ID",
100
+ required: true,
101
+ maxLength: 128,
102
+ description: "Source event id; UNIQUE(event_id, webhook_id) for dedup"
103
+ }),
104
+ event_type: _data.Field.text({
105
+ label: "Event Type",
106
+ required: true,
107
+ maxLength: 128,
108
+ description: "e.g. data.record.created"
109
+ }),
110
+ url: _data.Field.text({
111
+ label: "Target URL",
112
+ required: true,
113
+ maxLength: 2048,
114
+ description: "Snapshotted at enqueue so config edits do not rewrite live rows"
115
+ }),
116
+ method: _data.Field.text({ label: "Method", required: false, maxLength: 10 }),
117
+ headers_json: _data.Field.textarea({ label: "Headers JSON", required: false }),
118
+ secret: _data.Field.text({ label: "HMAC Secret", required: false, maxLength: 256 }),
119
+ timeout_ms: _data.Field.number({ label: "Timeout (ms)", required: false }),
120
+ payload_json: _data.Field.textarea({ label: "Payload JSON", required: true }),
121
+ partition_key: _data.Field.number({
122
+ label: "Partition",
123
+ required: true,
124
+ description: "hash(webhook_id) mod partitionCount \u2014 precomputed for cheap WHERE"
125
+ }),
126
+ status: _data.Field.text({
127
+ label: "Status",
128
+ required: true,
129
+ defaultValue: "pending",
130
+ maxLength: 16,
131
+ description: "pending | in_flight | success | failed | dead"
132
+ }),
133
+ attempts: _data.Field.number({
134
+ label: "Attempts",
135
+ required: true,
136
+ defaultValue: 0,
137
+ description: "Number of POST attempts made so far"
138
+ }),
139
+ claimed_by: _data.Field.text({ label: "Claimed By", required: false, maxLength: 128 }),
140
+ claimed_at: _data.Field.number({ label: "Claimed At (ms)", required: false }),
141
+ next_retry_at: _data.Field.number({ label: "Next Retry At (ms)", required: false }),
142
+ last_attempted_at: _data.Field.number({ label: "Last Attempted At (ms)", required: false }),
143
+ response_code: _data.Field.number({ label: "HTTP Status", required: false }),
144
+ response_body: _data.Field.textarea({ label: "Response Body (capped)", required: false }),
145
+ error: _data.Field.textarea({ label: "Error", required: false }),
146
+ created_at: _data.Field.number({ label: "Created At (ms)", required: true }),
147
+ updated_at: _data.Field.number({ label: "Updated At (ms)", required: true })
148
+ },
149
+ indexes: [
150
+ { fields: ["event_id", "webhook_id"], unique: true },
151
+ // Hot path: claim query
152
+ { fields: ["status", "partition_key", "next_retry_at"] },
153
+ // Reaper: scan stale in_flight rows by claimed_at
154
+ { fields: ["status", "claimed_at"] },
155
+ { fields: ["webhook_id"] }
156
+ ]
157
+ });
158
+ var SYS_WEBHOOK_DELIVERY = "sys_webhook_delivery";
159
+
160
+
161
+
162
+
163
+ exports.SysWebhookDelivery = SysWebhookDelivery; exports.SYS_WEBHOOK_DELIVERY = SYS_WEBHOOK_DELIVERY;
164
+ //# sourceMappingURL=chunk-OW7ESXOK.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-OW7ESXOK.cjs","../src/sys-webhook-delivery.object.ts"],"names":[],"mappings":"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","file":"/home/runner/work/framework/framework/packages/plugins/plugin-webhooks/dist/chunk-OW7ESXOK.cjs","sourcesContent":[null,"// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Field, ObjectSchema } from '@objectstack/spec/data';\n\n/**\n * sys_webhook_delivery — Durable outbox row for one HTTP attempt.\n *\n * Schema is owned by `@objectstack/plugin-webhooks`. Add it to your stack\n * via:\n *\n * import { SysWebhookDelivery } from '@objectstack/plugin-webhooks/schema';\n * defineStack({ objects: [SysWebhookDelivery, ...], plugins: [...] });\n *\n * Designed for the SqlWebhookOutbox claim algorithm:\n *\n * 1. Producers INSERT pending rows (dedup'd by (event_id, webhook_id)).\n * 2. The dispatcher's per-partition lock-holder runs:\n * SELECT id WHERE status='pending' AND partition_key=? AND (next_retry_at <= now OR null)\n * UPDATE SET status='in_flight' WHERE id IN (...) AND status='pending' ← atomic claim\n * POST to target URL\n * UPDATE SET status=success/pending/dead, attempts=attempts+1, ...\n *\n * `partition_key` is precomputed on enqueue (hash(webhook_id) mod N) so the\n * dispatcher can filter cheaply without DB-side hash functions.\n *\n * Indexes are tuned for the hot path: `(status, partition_key, next_retry_at)`\n * is the claim query; `(event_id, webhook_id)` is the dedup uniqueness.\n *\n * @namespace sys\n */\nexport const SysWebhookDelivery = ObjectSchema.create({\n name: 'sys_webhook_delivery',\n label: 'Webhook Delivery',\n pluralLabel: 'Webhook Deliveries',\n icon: 'package',\n isSystem: true,\n managedBy: 'config',\n userActions: { create: false, edit: false, delete: false, import: false },\n description:\n 'Durable outbox row for one webhook attempt. Managed by @objectstack/plugin-webhooks; do not write directly.',\n displayNameField: 'id',\n titleFormat: '{event_type} → {url}',\n compactLayout: ['event_type', 'url', 'status', 'attempts', 'next_retry_at'],\n\n listViews: {\n recent: {\n type: 'grid',\n name: 'recent',\n label: 'Recent',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n failures: {\n type: 'grid',\n name: 'failures',\n label: 'Failures',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'error', 'updated_at'],\n filter: [{ field: 'status', operator: 'in', value: ['failed', 'dead'] }],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n in_flight: {\n type: 'grid',\n name: 'in_flight',\n label: 'In Flight',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'claimed_by', 'claimed_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'in_flight' }],\n sort: [{ field: 'claimed_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n pending: {\n type: 'grid',\n name: 'pending',\n label: 'Pending',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'attempts', 'next_retry_at', 'updated_at'],\n filter: [{ field: 'status', operator: 'equals', value: 'pending' }],\n sort: [{ field: 'next_retry_at', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n by_status: {\n type: 'grid',\n name: 'by_status',\n label: 'By Status',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['status', 'event_type', 'url', 'attempts', 'updated_at'],\n sort: [{ field: 'status', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'status', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n by_webhook: {\n type: 'grid',\n name: 'by_webhook',\n label: 'By Webhook',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['webhook_id', 'event_type', 'status', 'attempts', 'updated_at'],\n sort: [{ field: 'webhook_id', order: 'asc' }, { field: 'updated_at', order: 'desc' }],\n grouping: { fields: [{ field: 'webhook_id', order: 'asc', collapsed: true }] },\n pagination: { pageSize: 100 },\n },\n all_deliveries: {\n type: 'grid',\n name: 'all_deliveries',\n label: 'All',\n data: { provider: 'object', object: 'sys_webhook_delivery' },\n columns: ['event_type', 'url', 'status', 'attempts', 'response_code', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({\n label: 'Delivery ID',\n required: true,\n maxLength: 64,\n description: 'UUID — also doubles as the receiver-side idempotency key',\n }),\n\n webhook_id: Field.text({\n label: 'Webhook ID',\n required: true,\n maxLength: 64,\n description: 'FK to sys_webhook.id (loosely coupled — denormalised URL/secret on row)',\n }),\n\n event_id: Field.text({\n label: 'Event ID',\n required: true,\n maxLength: 128,\n description: 'Source event id; UNIQUE(event_id, webhook_id) for dedup',\n }),\n\n event_type: Field.text({\n label: 'Event Type',\n required: true,\n maxLength: 128,\n description: 'e.g. data.record.created',\n }),\n\n url: Field.text({\n label: 'Target URL',\n required: true,\n maxLength: 2048,\n description: 'Snapshotted at enqueue so config edits do not rewrite live rows',\n }),\n\n method: Field.text({ label: 'Method', required: false, maxLength: 10 }),\n headers_json: Field.textarea({ label: 'Headers JSON', required: false }),\n secret: Field.text({ label: 'HMAC Secret', required: false, maxLength: 256 }),\n timeout_ms: Field.number({ label: 'Timeout (ms)', required: false }),\n payload_json: Field.textarea({ label: 'Payload JSON', required: true }),\n\n partition_key: Field.number({\n label: 'Partition',\n required: true,\n description: 'hash(webhook_id) mod partitionCount — precomputed for cheap WHERE',\n }),\n\n status: Field.text({\n label: 'Status',\n required: true,\n defaultValue: 'pending',\n maxLength: 16,\n description: 'pending | in_flight | success | failed | dead',\n }),\n\n attempts: Field.number({\n label: 'Attempts',\n required: true,\n defaultValue: 0,\n description: 'Number of POST attempts made so far',\n }),\n\n claimed_by: Field.text({ label: 'Claimed By', required: false, maxLength: 128 }),\n claimed_at: Field.number({ label: 'Claimed At (ms)', required: false }),\n next_retry_at: Field.number({ label: 'Next Retry At (ms)', required: false }),\n last_attempted_at: Field.number({ label: 'Last Attempted At (ms)', required: false }),\n response_code: Field.number({ label: 'HTTP Status', required: false }),\n response_body: Field.textarea({ label: 'Response Body (capped)', required: false }),\n error: Field.textarea({ label: 'Error', required: false }),\n\n created_at: Field.number({ label: 'Created At (ms)', required: true }),\n updated_at: Field.number({ label: 'Updated At (ms)', required: true }),\n },\n\n indexes: [\n { fields: ['event_id', 'webhook_id'], unique: true },\n // Hot path: claim query\n { fields: ['status', 'partition_key', 'next_retry_at'] },\n // Reaper: scan stale in_flight rows by claimed_at\n { fields: ['status', 'claimed_at'] },\n { fields: ['webhook_id'] },\n ],\n});\n\n/** Canonical object name — exported so SqlWebhookOutbox callers can override if needed. */\nexport const SYS_WEBHOOK_DELIVERY = 'sys_webhook_delivery' as const;\n"]}