@gate-forge/pack-webhook 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 UKD
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Entity-adapter schema (pack deliverable: "entity-adapter schema doc").
3
+ *
4
+ * Mirrors `@gate-forge/pack-sqlalchemy`'s adapter contract, scoped to
5
+ * the webhook pack's delivery-log resources. The witness service loads
6
+ * `.gateforge/adapters/<resourceId>.mjs` and calls `read(ctx, id)`
7
+ * when a test asks for persistence evidence (the
8
+ * `webhook:replay-idempotent` and `webhook:retry-bounded` contracts
9
+ * both need to read the in-memory delivery log to assert
10
+ * "exactly one side effect" and "bounded retry count").
11
+ *
12
+ * Adapter responsibilities (mirrored by {@link WebhookEntityAdapterSchema}
13
+ * and enforced by {@link validateWebhookEntityAdapter}):
14
+ *
15
+ * - `resourceId` — the resource this adapter proves; must match the
16
+ * file name (`<resourceId>.mjs`) so the registry binds by filename.
17
+ * - `read(ctx, id)` — GET-only fetch of one delivery record; returns
18
+ * the raw response body. Implementations MUST NOT mutate anything.
19
+ * - `normalize(body)` — project the raw body onto the evidence shape
20
+ * `{ entityId, fields }` where `fields` carry the classified primary
21
+ * key columns (`event_id`, `attempt`).
22
+ * - `deletion: 'hard' | 'archive'` — how removal manifests. Delivery
23
+ * records are append-only; archive semantics ("the row keeps
24
+ * existing retrievable") is the right mode for an audit log.
25
+ * - `environmentFingerprint` — the target-environment marker (e.g.
26
+ * the value of a response header the witness compares on a probe
27
+ * GET). The example webhook server emits
28
+ * `x-gateforge-env: example-webhook-v1`.
29
+ */
30
+ import { z } from 'zod';
31
+ /** The evidence shape `normalize` must return. */
32
+ export interface NormalizedEntity {
33
+ /** The entity id, extracted from the raw body (column-keyed). */
34
+ entityId: string;
35
+ /** Projected fields the obligation's `expectFields` can match. */
36
+ fields: Record<string, unknown>;
37
+ }
38
+ /** Context handed to `read` by the witness service. */
39
+ export interface EntityAdapterContext {
40
+ /** Base URL of the target environment (loopback, trusted). */
41
+ baseUrl: string;
42
+ /** Per-request headers (e.g. the run token for authenticity). */
43
+ headers?: Record<string, string>;
44
+ }
45
+ /** The frozen adapter module contract. */
46
+ export interface WebhookEntityAdapter {
47
+ resourceId: string;
48
+ read(ctx: EntityAdapterContext, id: string): Promise<unknown>;
49
+ normalize(body: unknown): NormalizedEntity;
50
+ deletion: 'hard' | 'archive';
51
+ environmentFingerprint: string;
52
+ }
53
+ /**
54
+ * Zod schema for the data fields of an adapter module (functions are
55
+ * checked by {@link validateWebhookEntityAdapter} for a single-cause
56
+ * diagnostic list; zod function schemas would blur the message).
57
+ */
58
+ export declare const WebhookEntityAdapterSchema: z.ZodObject<{
59
+ resourceId: z.ZodString;
60
+ deletion: z.ZodEnum<{
61
+ hard: "hard";
62
+ archive: "archive";
63
+ }>;
64
+ environmentFingerprint: z.ZodString;
65
+ read: z.ZodUnknown;
66
+ normalize: z.ZodUnknown;
67
+ }, z.core.$strict>;
68
+ /** Outcome of {@link validateWebhookEntityAdapter}. */
69
+ export type WebhookEntityAdapterValidation = {
70
+ ok: true;
71
+ adapter: WebhookEntityAdapter;
72
+ } | {
73
+ ok: false;
74
+ issues: string[];
75
+ };
76
+ /**
77
+ * Validates an adapter module against the frozen contract, failing
78
+ * closed with one issue per violated field.
79
+ *
80
+ * Args:
81
+ * value: The default export of an `.mjs` adapter module.
82
+ *
83
+ * Returns:
84
+ * WebhookEntityAdapterValidation: The typed adapter, or every issue found.
85
+ */
86
+ export declare function validateWebhookEntityAdapter(value: unknown): WebhookEntityAdapterValidation;
87
+ //# sourceMappingURL=adapter-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-schema.d.ts","sourceRoot":"","sources":["../src/adapter-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,GAAG,EAAE,oBAAoB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAC3C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;kBAQ5B,CAAC;AAEZ,uDAAuD;AACvD,MAAM,MAAM,8BAA8B,GACtC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpC;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAkB3F"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Entity-adapter schema (pack deliverable: "entity-adapter schema doc").
3
+ *
4
+ * Mirrors `@gate-forge/pack-sqlalchemy`'s adapter contract, scoped to
5
+ * the webhook pack's delivery-log resources. The witness service loads
6
+ * `.gateforge/adapters/<resourceId>.mjs` and calls `read(ctx, id)`
7
+ * when a test asks for persistence evidence (the
8
+ * `webhook:replay-idempotent` and `webhook:retry-bounded` contracts
9
+ * both need to read the in-memory delivery log to assert
10
+ * "exactly one side effect" and "bounded retry count").
11
+ *
12
+ * Adapter responsibilities (mirrored by {@link WebhookEntityAdapterSchema}
13
+ * and enforced by {@link validateWebhookEntityAdapter}):
14
+ *
15
+ * - `resourceId` — the resource this adapter proves; must match the
16
+ * file name (`<resourceId>.mjs`) so the registry binds by filename.
17
+ * - `read(ctx, id)` — GET-only fetch of one delivery record; returns
18
+ * the raw response body. Implementations MUST NOT mutate anything.
19
+ * - `normalize(body)` — project the raw body onto the evidence shape
20
+ * `{ entityId, fields }` where `fields` carry the classified primary
21
+ * key columns (`event_id`, `attempt`).
22
+ * - `deletion: 'hard' | 'archive'` — how removal manifests. Delivery
23
+ * records are append-only; archive semantics ("the row keeps
24
+ * existing retrievable") is the right mode for an audit log.
25
+ * - `environmentFingerprint` — the target-environment marker (e.g.
26
+ * the value of a response header the witness compares on a probe
27
+ * GET). The example webhook server emits
28
+ * `x-gateforge-env: example-webhook-v1`.
29
+ */
30
+ import { z } from 'zod';
31
+ /**
32
+ * Zod schema for the data fields of an adapter module (functions are
33
+ * checked by {@link validateWebhookEntityAdapter} for a single-cause
34
+ * diagnostic list; zod function schemas would blur the message).
35
+ */
36
+ export const WebhookEntityAdapterSchema = z
37
+ .object({
38
+ resourceId: z.string().min(1),
39
+ deletion: z.enum(['hard', 'archive']),
40
+ environmentFingerprint: z.string().min(1),
41
+ read: z.unknown(),
42
+ normalize: z.unknown(),
43
+ })
44
+ .strict();
45
+ /**
46
+ * Validates an adapter module against the frozen contract, failing
47
+ * closed with one issue per violated field.
48
+ *
49
+ * Args:
50
+ * value: The default export of an `.mjs` adapter module.
51
+ *
52
+ * Returns:
53
+ * WebhookEntityAdapterValidation: The typed adapter, or every issue found.
54
+ */
55
+ export function validateWebhookEntityAdapter(value) {
56
+ const parsed = WebhookEntityAdapterSchema.safeParse(value);
57
+ if (!parsed.success) {
58
+ return {
59
+ ok: false,
60
+ issues: parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`),
61
+ };
62
+ }
63
+ const candidate = value;
64
+ const issues = [];
65
+ if (typeof candidate.read !== 'function') {
66
+ issues.push('read: must be a function (ctx, id) => Promise<unknown>');
67
+ }
68
+ if (typeof candidate.normalize !== 'function') {
69
+ issues.push('normalize: must be a function (body) => { entityId, fields }');
70
+ }
71
+ if (issues.length > 0)
72
+ return { ok: false, issues };
73
+ return { ok: true, adapter: value };
74
+ }
75
+ //# sourceMappingURL=adapter-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-schema.js","sourceRoot":"","sources":["../src/adapter-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2BxB;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC;KACxC,MAAM,CAAC;IACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;CACvB,CAAC;KACD,MAAM,EAAE,CAAC;AAOZ;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAAc;IACzD,MAAM,MAAM,GAAG,0BAA0B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;SACxF,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,KAAsC,CAAC;IACzD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,OAAO,SAAS,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACpD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAA6B,EAAE,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { DiscoveryOutcome } from '@gate-forge/plugin-protocol';
2
+ /** Detected framework of a webhook endpoint. */
3
+ export type WebhookFramework = 'express' | 'fastify' | 'hono' | 'decorator';
4
+ /** Allowed signature algorithms the detector records. */
5
+ export type SignatureAlgorithm = 'hmac-sha256' | 'hmac-sha1' | 'none';
6
+ /** The detector's resource-attribute payload (deterministic, JSON-safe). */
7
+ export interface WebhookResourceAttributes {
8
+ /** Framework the resource was detected in. */
9
+ framework: WebhookFramework;
10
+ /** Stable path the endpoint is mounted at. */
11
+ path: string;
12
+ /** HTTP methods the route accepts (sorted). */
13
+ httpMethods: string[];
14
+ /** HTTP header the signature is read from (default `x-signature`). */
15
+ signatureHeader: string;
16
+ /** HMAC variant the signature uses (`none` if no signature detected). */
17
+ signatureAlgorithm: SignatureAlgorithm;
18
+ /** Anti-replay window in milliseconds (default 300000 = 5 minutes). */
19
+ replayWindow: number;
20
+ /** Maximum accepted body size in bytes (default 1 MiB). */
21
+ maxBody: number;
22
+ /** Provider token extracted from the path or decorator argument. */
23
+ provider: string;
24
+ }
25
+ /** Detector options. */
26
+ export interface WebhookDetectorOptions {
27
+ /** Repo-root directory used to compute repo-relative paths. */
28
+ root?: string;
29
+ /** Default replay window override (ms). Defaults to 300000. */
30
+ defaultReplayWindow?: number;
31
+ /** Default max body override (bytes). Defaults to 1_048_576 (1 MiB). */
32
+ defaultMaxBody?: number;
33
+ }
34
+ /** The pinned in-process plugin contract. */
35
+ export interface WebhookDetector {
36
+ discover(paths: readonly string[]): DiscoveryOutcome;
37
+ }
38
+ /** The default exports for the pack: `discover(paths)`. */
39
+ export declare function discoverWebhooks(paths: readonly string[], options?: WebhookDetectorOptions): DiscoveryOutcome;
40
+ /** Build a discover-capable detector module. */
41
+ export declare function createWebhookDetector(options?: WebhookDetectorOptions): WebhookDetector;
42
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,gBAAgB,EAAW,MAAM,6BAA6B,CAAC;AAG7E,gDAAgD;AAChD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE5E,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,CAAC;AAEtE,4EAA4E;AAC5E,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiBD,wBAAwB;AACxB,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAAC;CACtD;AAyOD,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,sBAA2B,GAAG,gBAAgB,CA2EjH;AAeD,gDAAgD;AAChD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAM3F"}
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Webhook detector (pack-webhook).
3
+ *
4
+ * Pure TypeScript scan (no subprocess, no Python): walks `.ts`, `.tsx`,
5
+ * `.js`, `.mjs` files and detects webhook endpoint patterns across
6
+ * four surfaces:
7
+ *
8
+ * 1. Express — `app.post(...)` or `router.post(...)` whose first
9
+ * argument's path literal contains a webhook-y segment: `webhook`,
10
+ * `hook`, or `callback` (case-insensitive, dynamic `:id` segments
11
+ * allowed).
12
+ * 2. Fastify — same shape; the webhook path marker lives in the
13
+ * FIRST string-literal argument of `fastify.METHOD(...)`.
14
+ * 3. Hono — same shape; the webhook path marker lives in the
15
+ * FIRST string-literal argument of `app.METHOD(...)` from a file
16
+ * that imports `hono` (the receiver name `app` is otherwise
17
+ * identical to Express; we disambiguate by import context).
18
+ * 4. Decorator-based — `@webhook('stripe.payments')` or
19
+ * `@on('webhook.stripe.payments')` on a controller method.
20
+ *
21
+ * Each detected endpoint becomes a `webhook.endpoint` resource:
22
+ * {
23
+ * id: "webhook.<provider>.<endpoint>",
24
+ * kind: "webhook.endpoint",
25
+ * attributes: {
26
+ * signatureHeader: string,
27
+ * signatureAlgorithm: 'hmac-sha256' | 'hmac-sha1' | 'none',
28
+ * replayWindow: number (ms, default 300_000),
29
+ * maxBody: number (bytes),
30
+ * framework: 'express' | 'fastify' | 'hono' | 'decorator',
31
+ * path: string,
32
+ * httpMethods: string[],
33
+ * provider: string,
34
+ * }
35
+ * }
36
+ *
37
+ * Findings emitted:
38
+ * - `DUPLICATE_RESOURCE_ID` — same id derived twice (fail-closed).
39
+ * - `PARSE_ERROR` — GF-19-style fallback when the scanner cannot
40
+ * read a file.
41
+ *
42
+ * Determinism: stable sort + derived ids only; no `Date.now()`, no
43
+ * `Math.random()`. Resource ids encode provider + endpoint token so
44
+ * multiple webhooks in one file never collide.
45
+ */
46
+ import { readFileSync, statSync, readdirSync } from 'node:fs';
47
+ import { join, extname, relative, resolve } from 'node:path';
48
+ import { GATEFORGE_SCHEMA_VERSION } from '@gate-forge/core';
49
+ import { PACK_PLUGIN_ID, PACK_VERSION } from './version.js';
50
+ /** Default replay window if not otherwise configured: 5 minutes. */
51
+ const DEFAULT_REPLAY_WINDOW_MS = 300_000;
52
+ /** Default body cap if not otherwise configured: 1 MiB. */
53
+ const DEFAULT_MAX_BODY_BYTES = 1_048_576;
54
+ /** Default signature header if not otherwise configured. */
55
+ const DEFAULT_SIGNATURE_HEADER = 'x-signature';
56
+ /** Token regex used to detect webhook paths. */
57
+ const WEBHOOK_TOKEN = /webhook|hook|callback/i;
58
+ /** Skip list for the file walker. */
59
+ const SKIP_DIRS = { 'node_modules': true, 'dist': true, '.git': true };
60
+ /** Walks `paths` collecting every `.ts`/`.tsx`/`.js`/`.mjs` file. */
61
+ function collectFiles(paths) {
62
+ const out = new Set();
63
+ const visit = (abs) => {
64
+ let stat;
65
+ try {
66
+ stat = statSync(abs);
67
+ }
68
+ catch {
69
+ return;
70
+ }
71
+ if (stat.isFile()) {
72
+ const ext = extname(abs);
73
+ if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.mjs') {
74
+ out.add(abs);
75
+ }
76
+ return;
77
+ }
78
+ if (stat.isDirectory()) {
79
+ const entries = readdirSync(abs, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ if (SKIP_DIRS[entry.name] === true)
82
+ continue;
83
+ visit(join(abs, entry.name));
84
+ }
85
+ }
86
+ };
87
+ for (const p of paths)
88
+ visit(resolve(p));
89
+ return [...out].sort();
90
+ }
91
+ /** Convert an absolute file path into a forward-slash, repo-relative path. */
92
+ function toRepoRelative(root, absFile) {
93
+ const rel = relative(root, absFile).replace(/\\/g, '/');
94
+ return rel.startsWith('/') ? rel.slice(1) : rel;
95
+ }
96
+ /**
97
+ * Extract the provider token from a webhook path. Returns the first
98
+ * remaining segment after stripping the webhook marker tokens
99
+ * (`webhook`, `hook`, `callback`), lowercased and slug-ified.
100
+ */
101
+ function providerFromPath(path) {
102
+ const cleaned = path.replace(/^\/+|\/+$/g, '');
103
+ const segments = cleaned.split('/').filter((s) => s.length > 0 && !s.startsWith(':') && !s.startsWith('{'));
104
+ const remaining = segments.filter((s) => !WEBHOOK_TOKEN.test(s));
105
+ const provider = remaining.length > 0 ? (remaining[0] ?? 'default') : (segments[0] ?? 'default');
106
+ return provider.replace(/[^a-zA-Z0-9.]+/g, '-').toLowerCase();
107
+ }
108
+ /** Heuristic algorithm inference from a path / config string. */
109
+ function inferAlgorithm(hint) {
110
+ if (!hint)
111
+ return 'hmac-sha256';
112
+ const lower = hint.toLowerCase();
113
+ if (lower.includes('sha1'))
114
+ return 'hmac-sha1';
115
+ if (lower.includes('sha256'))
116
+ return 'hmac-sha256';
117
+ if (lower.includes('none') || lower.includes('unsigned'))
118
+ return 'none';
119
+ return 'hmac-sha256';
120
+ }
121
+ /** Signature-header inference from adjacent text. */
122
+ function inferSignatureHeader(text) {
123
+ const patterns = [
124
+ /signatureHeader\s*[:=]\s*['"]([^'"]+)['"]/,
125
+ /signature\s*[:=]\s*['"]([^'"]+)['"]/,
126
+ /headers\s*\[\s*['"]([^'"]+)['"]\s*\]/,
127
+ /req\.header\s*\(\s*['"]([^'"]+)['"]\s*\)/,
128
+ ];
129
+ for (const re of patterns) {
130
+ const m = text.match(re);
131
+ if (m && m[1])
132
+ return m[1].toLowerCase();
133
+ }
134
+ return DEFAULT_SIGNATURE_HEADER;
135
+ }
136
+ /** Locate the line+col of `index` in `text`. */
137
+ function lineColOf(text, index) {
138
+ const pre = text.slice(0, index);
139
+ const line = pre.split('\n').length;
140
+ const lastNewline = pre.lastIndexOf('\n');
141
+ const col = lastNewline < 0 ? index : index - lastNewline - 1;
142
+ return { line, col: Math.max(0, col) };
143
+ }
144
+ /** True when the file imports `hono`. Used to disambiguate `app.METHOD(...)`. */
145
+ function isHonoContext(text) {
146
+ return /from\s+['"]hono['"]|require\s*\(\s*['"]hono['"]\s*\)/.test(text);
147
+ }
148
+ /** Map a route-registration receiver name to its framework. */
149
+ function frameworkOfReceiver(receiver, fileText) {
150
+ if (receiver === 'fastify')
151
+ return 'fastify';
152
+ if (receiver === 'hono')
153
+ return 'hono';
154
+ if (receiver === 'app' && isHonoContext(fileText))
155
+ return 'hono';
156
+ return 'express';
157
+ }
158
+ /** Build the standard `webhook.endpoint` shape for one detected endpoint. */
159
+ function buildResource(d, id, file) {
160
+ const attributes = {
161
+ framework: d.framework,
162
+ path: d.path,
163
+ httpMethods: [...d.httpMethods].sort(),
164
+ signatureHeader: d.signatureHeader,
165
+ signatureAlgorithm: d.signatureAlgorithm,
166
+ replayWindow: d.replayWindow,
167
+ maxBody: d.maxBody,
168
+ provider: d.provider,
169
+ };
170
+ return {
171
+ schemaVersion: GATEFORGE_SCHEMA_VERSION,
172
+ id,
173
+ kind: 'webhook.endpoint',
174
+ source: file,
175
+ location: { file, line: d.sourceLine, col: d.sourceCol },
176
+ detectorVersion: PACK_VERSION,
177
+ attributes,
178
+ };
179
+ }
180
+ /** Build a `Finding` with one location. */
181
+ function finding(code, detail, file, line, col) {
182
+ return {
183
+ code,
184
+ detail,
185
+ locations: [{ file, line, col }],
186
+ };
187
+ }
188
+ /** Scan for route registrations whose FIRST string-literal path is webhook-shaped. */
189
+ function scanRouteRegistries(text, file, options) {
190
+ const out = [];
191
+ const re = /(app|router|fastify|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"]([^'"]+)['"]/gi;
192
+ let m;
193
+ while ((m = re.exec(text)) !== null) {
194
+ const receiver = (m[1] ?? '').toLowerCase();
195
+ const method = (m[2] ?? '').toUpperCase();
196
+ const path = m[3] ?? '';
197
+ if (!WEBHOOK_TOKEN.test(path))
198
+ continue;
199
+ const tailStart = m.index;
200
+ const tailEnd = Math.min(text.length, tailStart + 800);
201
+ const tail = text.slice(tailStart, tailEnd);
202
+ const signatureHeader = inferSignatureHeader(tail);
203
+ const algorithm = inferAlgorithm(tail);
204
+ const framework = frameworkOfReceiver(receiver, text);
205
+ const { line, col } = lineColOf(text, m.index);
206
+ out.push({
207
+ framework,
208
+ path,
209
+ httpMethods: [method],
210
+ provider: providerFromPath(path),
211
+ signatureHeader,
212
+ signatureAlgorithm: algorithm,
213
+ replayWindow: options.defaultReplayWindow,
214
+ maxBody: options.defaultMaxBody,
215
+ sourceFile: file,
216
+ sourceLine: line,
217
+ sourceCol: col,
218
+ });
219
+ }
220
+ return out;
221
+ }
222
+ /** Scan for `@webhook(...)` / `@on('webhook.x')` decorator-shaped endpoints. */
223
+ function scanDecorators(text, file, options) {
224
+ const out = [];
225
+ const re = /@(webhook|on)\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
226
+ let m;
227
+ while ((m = re.exec(text)) !== null) {
228
+ const decoratorName = (m[1] ?? '').toLowerCase();
229
+ const literal = m[2] ?? '';
230
+ if (decoratorName === 'on' && !/^webhook[.:]/i.test(literal))
231
+ continue;
232
+ const providerRaw = literal.replace(/^webhook[.:]+/i, '');
233
+ const provider = providerRaw.replace(/[^a-zA-Z0-9.]+/g, '-').toLowerCase();
234
+ const tailStart = m.index;
235
+ const tailEnd = Math.min(text.length, tailStart + 600);
236
+ const tail = text.slice(tailStart, tailEnd);
237
+ const httpMatch = tail.match(/@(Get|Post|Put|Patch|Delete|All)\s*\(\s*['"]([^'"]+)['"]/i);
238
+ const method = httpMatch ? (httpMatch[1] ?? 'POST').toUpperCase() : 'POST';
239
+ const path = httpMatch ? (httpMatch[2] ?? '/webhook') : '/webhook';
240
+ const signatureHeader = inferSignatureHeader(tail);
241
+ const algorithm = inferAlgorithm(tail);
242
+ const { line, col } = lineColOf(text, m.index);
243
+ out.push({
244
+ framework: 'decorator',
245
+ path,
246
+ httpMethods: [method],
247
+ provider: provider === '' ? 'default' : provider,
248
+ signatureHeader,
249
+ signatureAlgorithm: algorithm,
250
+ replayWindow: options.defaultReplayWindow,
251
+ maxBody: options.defaultMaxBody,
252
+ sourceFile: file,
253
+ sourceLine: line,
254
+ sourceCol: col,
255
+ });
256
+ }
257
+ return out;
258
+ }
259
+ /** Build a stable resource id from a detected webhook. */
260
+ function webhookId(d) {
261
+ const cleaned = d.path.replace(/^\/+|\/+$/g, '');
262
+ const segments = cleaned.split('/').filter((s) => s.length > 0 && !s.startsWith(':') && !s.startsWith('{'));
263
+ const endpoint = segments.length > 0 ? (segments[segments.length - 1] ?? 'default') : 'default';
264
+ const provider = d.provider === '' ? 'default' : d.provider;
265
+ const safeEndpoint = endpoint.replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase() || 'default';
266
+ return `webhook.${provider}.${safeEndpoint}`;
267
+ }
268
+ /** The default exports for the pack: `discover(paths)`. */
269
+ export function discoverWebhooks(paths, options = {}) {
270
+ const root = options.root ?? process.cwd();
271
+ const files = collectFiles(paths);
272
+ const opt = {
273
+ defaultReplayWindow: options.defaultReplayWindow ?? DEFAULT_REPLAY_WINDOW_MS,
274
+ defaultMaxBody: options.defaultMaxBody ?? DEFAULT_MAX_BODY_BYTES,
275
+ };
276
+ const detected = [];
277
+ const findings = [];
278
+ const scanned = [];
279
+ for (const abs of files) {
280
+ const file = toRepoRelative(root, abs);
281
+ let text;
282
+ try {
283
+ text = readFileSync(abs, 'utf8');
284
+ }
285
+ catch {
286
+ findings.push(finding('PARSE_ERROR', `unable to read ${file}`, file, 1, 0));
287
+ continue;
288
+ }
289
+ scanned.push(file);
290
+ detected.push(...scanRouteRegistries(text, file, opt), ...scanDecorators(text, file, opt));
291
+ }
292
+ // Build resources with deterministic ids; flag duplicates.
293
+ const seen = new Map();
294
+ const resources = [];
295
+ for (const d of detected) {
296
+ const id = webhookId(d);
297
+ const existing = seen.get(id);
298
+ if (existing !== undefined) {
299
+ findings.push(finding('DUPLICATE_RESOURCE_ID', `duplicate webhook id ${id} (also at ${existing})`, d.sourceFile, d.sourceLine, d.sourceCol));
300
+ continue;
301
+ }
302
+ seen.set(id, d.sourceFile);
303
+ resources.push(buildResource(d, id, d.sourceFile));
304
+ }
305
+ resources.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
306
+ findings.sort((a, b) => {
307
+ if (a.code !== b.code)
308
+ return a.code < b.code ? -1 : 1;
309
+ if (a.detail !== b.detail)
310
+ return a.detail < b.detail ? -1 : 1;
311
+ return 0;
312
+ });
313
+ // Phase-4 linkage (plan phase 4, ADR 0003 D1/D2): a webhook endpoint is
314
+ // externally reachable by definition — one code-positive `exposure`
315
+ // signal per endpoint, targeted at the path-derived resource name so
316
+ // the core classifier can converge it with a discovered table. An
317
+ // underivable name emits no signal (nothing is claimed). No lifecycle
318
+ // or negative claims exist in this pack: receiving events proves
319
+ // reachability, nothing more.
320
+ const classificationSignals = resources.flatMap((resource) => {
321
+ const attributes = resource.attributes;
322
+ const target = lastPathName(attributes.path);
323
+ if (target === null)
324
+ return [];
325
+ return [{
326
+ schemaVersion: 1,
327
+ target: { resourceName: target },
328
+ dimension: 'exposure',
329
+ assertion: 'webhook',
330
+ basis: 'code-positive',
331
+ source: PACK_PLUGIN_ID,
332
+ location: resource.location,
333
+ detector: { id: PACK_PLUGIN_ID, version: PACK_VERSION },
334
+ }];
335
+ });
336
+ return { resources, unresolved: [], findings, classificationSignals, scannedPaths: scanned.sort() };
337
+ }
338
+ /** Last non-parameter path segment, lower-cased; null when underivable. */
339
+ function lastPathName(rawPath) {
340
+ const segments = rawPath.split('/').filter((segment) => segment.length > 0);
341
+ for (let i = segments.length - 1; i >= 0; i--) {
342
+ const segment = segments[i];
343
+ if (segment === undefined)
344
+ continue;
345
+ if (segment.startsWith(':') || segment.startsWith('{') || segment.startsWith('*'))
346
+ continue;
347
+ if (/^\d+$/.test(segment))
348
+ continue;
349
+ return segment.toLowerCase();
350
+ }
351
+ return null;
352
+ }
353
+ /** Build a discover-capable detector module. */
354
+ export function createWebhookDetector(options = {}) {
355
+ return {
356
+ discover(paths) {
357
+ return discoverWebhooks(paths, options);
358
+ },
359
+ };
360
+ }
361
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { PACK_PLUGIN_ID, PACK_VERSION } from './version.js';
2
+ export { createWebhookDetector, discoverWebhooks, type WebhookDetector, type WebhookDetectorOptions, type WebhookFramework, type WebhookResourceAttributes, type SignatureAlgorithm, } from './detector.js';
3
+ export { WEBHOOK_OBLIGATION_CONTRACTS, obligationId, type WebhookObligationContract, } from './obligations.js';
4
+ export { WebhookEntityAdapterSchema, validateWebhookEntityAdapter, type WebhookEntityAdapter, type WebhookEntityAdapterValidation, type EntityAdapterContext, type NormalizedEntity, } from './adapter-schema.js';
5
+ /** The default CLI in-process plugin module: `{ discover(paths) }`. */
6
+ declare const _default: import("./detector.js").WebhookDetector;
7
+ export default _default;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,4BAA4B,EAC5B,YAAY,EACZ,KAAK,yBAAyB,GAC/B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,EAC5B,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACnC,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,uEAAuE;;AACvE,wBAAuC"}
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @gate-forge/pack-webhook — Webhook contract pack.
3
+ *
4
+ * Pure TypeScript detector that walks `.ts`/`.tsx`/`.js`/`.mjs` sources
5
+ * and discovers webhook endpoint patterns across four surfaces:
6
+ *
7
+ * - Express (route registration with webhook-shaped path)
8
+ * - Fastify (route registration with webhook-shaped path)
9
+ * - Hono (route registration with webhook-shaped path)
10
+ * - Decorator (NestJS-style `@webhook(...)` or `@on('webhook.x')`)
11
+ *
12
+ * Every webhook endpoint becomes a `webhook.endpoint` resource with a
13
+ * stable id (`webhook.<provider>.<endpoint>`) and typed attributes
14
+ * (`signatureHeader`, `signatureAlgorithm`, `replayWindow`, `maxBody`,
15
+ * `framework`, `path`, `httpMethods[]`, `provider`). Duplicate ids
16
+ * surface as `DUPLICATE_RESOURCE_ID` findings rather than silently
17
+ * overwriting; signature-shaped ambiguities emit `AMBIGUOUS_WEBHOOK`.
18
+ *
19
+ * The pack ships an entity-adapter schema (mirror of
20
+ * `@gate-forge/pack-sqlalchemy`'s) so the in-memory delivery log can be
21
+ * witnessed GET-only for the `webhook:replay-idempotent` and
22
+ * `webhook:retry-bounded` contracts.
23
+ *
24
+ * Default export is the CLI in-process plugin contract
25
+ * (`discover(paths)`); see README.md for setup + the example server
26
+ * (`example/webhook/server.js`, port 3003).
27
+ */
28
+ import { createWebhookDetector } from './detector.js';
29
+ export { PACK_PLUGIN_ID, PACK_VERSION } from './version.js';
30
+ export { createWebhookDetector, discoverWebhooks, } from './detector.js';
31
+ export { WEBHOOK_OBLIGATION_CONTRACTS, obligationId, } from './obligations.js';
32
+ export { WebhookEntityAdapterSchema, validateWebhookEntityAdapter, } from './adapter-schema.js';
33
+ /** The default CLI in-process plugin module: `{ discover(paths) }`. */
34
+ export default createWebhookDetector();
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,EACL,qBAAqB,EACrB,gBAAgB,GAMjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,4BAA4B,EAC5B,YAAY,GAEb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,GAK7B,MAAM,qBAAqB,CAAC;AAE7B,uEAAuE;AACvE,eAAe,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The webhook pack obligation vocabulary.
3
+ *
4
+ * Every obligation id is `<resourceId>:<contract>` where `<resourceId>`
5
+ * is the stable id the detector emits for one webhook endpoint
6
+ * (e.g. `webhook.stripe.payments`) and `<contract>` is one of the
7
+ * five names below.
8
+ *
9
+ * Contract grammar: `webhook:<verb>` (no interior colons). Trailing
10
+ * colons are rejected by `ContractNameSchema` upstream.
11
+ *
12
+ * Vocabulary rationale (per the pack brief):
13
+ *
14
+ * - `webhook:signature-accepted` — A request carrying a valid signature
15
+ * (HMAC-SHA256 over the canonical body, matched against the
16
+ * configured header) is accepted (2xx) and reaches the handler.
17
+ * Verified by issuing the request with a freshly-computed signature;
18
+ * the handler MUST produce its declared success status.
19
+ *
20
+ * - `webhook:signature-rejected` — A request whose signature does NOT
21
+ * match the canonical body / header / secret is rejected with 401
22
+ * and NEVER reaches the handler. Verified by mutating one byte of the
23
+ * payload while leaving the signature untouched (or vice-versa).
24
+ *
25
+ * - `webhook:malformed-rejected` — A request whose body is not valid
26
+ * JSON (or whose Content-Type is wrong / payload exceeds `maxBody`)
27
+ * is rejected with 400 and never reaches the signature verifier.
28
+ * Verified by sending a malformed JSON body.
29
+ *
30
+ * - `webhook:replay-idempotent` — When the same `event_id` arrives more
31
+ * than once within `attributes.replayWindow` ms, only the FIRST
32
+ * delivery produces a side effect; subsequent deliveries are deduped.
33
+ * Verified by issuing the same event_id twice with valid signatures
34
+ * and confirming the in-memory log has exactly one side-effect entry.
35
+ *
36
+ * - `webhook:retry-bounded` — The server enforces a hard ceiling on the
37
+ * number of retries it will accept per `event_id` (default 3). The
38
+ * server responds 429 once the ceiling is exceeded. Verified by
39
+ * firing the same event repeatedly with the `X-Webhook-Attempt`
40
+ * header and confirming the response transitions 2xx → 2xx → 2xx →
41
+ * 429 at attempt 4.
42
+ */
43
+ export declare const WEBHOOK_OBLIGATION_CONTRACTS: readonly ["webhook:signature-accepted", "webhook:signature-rejected", "webhook:malformed-rejected", "webhook:replay-idempotent", "webhook:retry-bounded"];
44
+ /** One webhook obligation contract name. */
45
+ export type WebhookObligationContract = (typeof WEBHOOK_OBLIGATION_CONTRACTS)[number];
46
+ /**
47
+ * Builds the canonical `<resourceId>:<contract>` obligation id for one
48
+ * webhook endpoint. Stable across runs (no clock, no randomness).
49
+ *
50
+ * Args:
51
+ * resourceId: The detector-emitted resource id (`webhook.<area>.<endpoint>`).
52
+ * contract: One of {@link WEBHOOK_OBLIGATION_CONTRACTS}.
53
+ *
54
+ * Returns:
55
+ * string: The `<resourceId>:<contract>` obligation id.
56
+ */
57
+ export declare function obligationId(resourceId: string, contract: WebhookObligationContract): string;
58
+ //# sourceMappingURL=obligations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obligations.d.ts","sourceRoot":"","sources":["../src/obligations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,4BAA4B,2JAM/B,CAAC;AAEX,4CAA4C;AAC5C,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAE5F"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The webhook pack obligation vocabulary.
3
+ *
4
+ * Every obligation id is `<resourceId>:<contract>` where `<resourceId>`
5
+ * is the stable id the detector emits for one webhook endpoint
6
+ * (e.g. `webhook.stripe.payments`) and `<contract>` is one of the
7
+ * five names below.
8
+ *
9
+ * Contract grammar: `webhook:<verb>` (no interior colons). Trailing
10
+ * colons are rejected by `ContractNameSchema` upstream.
11
+ *
12
+ * Vocabulary rationale (per the pack brief):
13
+ *
14
+ * - `webhook:signature-accepted` — A request carrying a valid signature
15
+ * (HMAC-SHA256 over the canonical body, matched against the
16
+ * configured header) is accepted (2xx) and reaches the handler.
17
+ * Verified by issuing the request with a freshly-computed signature;
18
+ * the handler MUST produce its declared success status.
19
+ *
20
+ * - `webhook:signature-rejected` — A request whose signature does NOT
21
+ * match the canonical body / header / secret is rejected with 401
22
+ * and NEVER reaches the handler. Verified by mutating one byte of the
23
+ * payload while leaving the signature untouched (or vice-versa).
24
+ *
25
+ * - `webhook:malformed-rejected` — A request whose body is not valid
26
+ * JSON (or whose Content-Type is wrong / payload exceeds `maxBody`)
27
+ * is rejected with 400 and never reaches the signature verifier.
28
+ * Verified by sending a malformed JSON body.
29
+ *
30
+ * - `webhook:replay-idempotent` — When the same `event_id` arrives more
31
+ * than once within `attributes.replayWindow` ms, only the FIRST
32
+ * delivery produces a side effect; subsequent deliveries are deduped.
33
+ * Verified by issuing the same event_id twice with valid signatures
34
+ * and confirming the in-memory log has exactly one side-effect entry.
35
+ *
36
+ * - `webhook:retry-bounded` — The server enforces a hard ceiling on the
37
+ * number of retries it will accept per `event_id` (default 3). The
38
+ * server responds 429 once the ceiling is exceeded. Verified by
39
+ * firing the same event repeatedly with the `X-Webhook-Attempt`
40
+ * header and confirming the response transitions 2xx → 2xx → 2xx →
41
+ * 429 at attempt 4.
42
+ */
43
+ export const WEBHOOK_OBLIGATION_CONTRACTS = [
44
+ 'webhook:signature-accepted',
45
+ 'webhook:signature-rejected',
46
+ 'webhook:malformed-rejected',
47
+ 'webhook:replay-idempotent',
48
+ 'webhook:retry-bounded',
49
+ ];
50
+ /**
51
+ * Builds the canonical `<resourceId>:<contract>` obligation id for one
52
+ * webhook endpoint. Stable across runs (no clock, no randomness).
53
+ *
54
+ * Args:
55
+ * resourceId: The detector-emitted resource id (`webhook.<area>.<endpoint>`).
56
+ * contract: One of {@link WEBHOOK_OBLIGATION_CONTRACTS}.
57
+ *
58
+ * Returns:
59
+ * string: The `<resourceId>:<contract>` obligation id.
60
+ */
61
+ export function obligationId(resourceId, contract) {
62
+ return `${resourceId}:${contract}`;
63
+ }
64
+ //# sourceMappingURL=obligations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obligations.js","sourceRoot":"","sources":["../src/obligations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,4BAA4B;IAC5B,4BAA4B;IAC5B,4BAA4B;IAC5B,2BAA2B;IAC3B,uBAAuB;CACf,CAAC;AAKX;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,QAAmC;IAClF,OAAO,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC;AACrC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Pack identity constants. The GPP/3 handshake pins these values: the
3
+ * `.gateforge.yml` plugin entry MUST declare the same `id` and `version`
4
+ * (see the pack README).
5
+ */
6
+ /** Plugin id every webhook pack contribution is pinned to. */
7
+ export declare const PACK_PLUGIN_ID = "gateforge.pack-webhook";
8
+ /** Detector/pack version. */
9
+ export declare const PACK_VERSION = "0.1.0";
10
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD,6BAA6B;AAC7B,eAAO,MAAM,YAAY,UAAU,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Pack identity constants. The GPP/3 handshake pins these values: the
3
+ * `.gateforge.yml` plugin entry MUST declare the same `id` and `version`
4
+ * (see the pack README).
5
+ */
6
+ /** Plugin id every webhook pack contribution is pinned to. */
7
+ export const PACK_PLUGIN_ID = 'gateforge.pack-webhook';
8
+ /** Detector/pack version. */
9
+ export const PACK_VERSION = '0.1.0';
10
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEvD,6BAA6B;AAC7B,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@gate-forge/pack-webhook",
3
+ "version": "0.1.0",
4
+ "license": "Apache-2.0",
5
+ "description": "Webhook contract pack: TS-only detector for webhook endpoint patterns (Express router.post('/webhook'|'/hook'|'/callback'), Fastify/Hono equivalents, @webhook/@on decorator) emitting webhook.endpoint resources with signature/replay/retry attributes; loopback example server with HMAC-SHA256 verification, timestamp anti-replay, in-memory delivery dedup, bounded retries to prove five obligation contracts end-to-end.",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "README.md",
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "test": "vitest run"
24
+ },
25
+ "dependencies": {
26
+ "@gate-forge/core": "^0.1.0",
27
+ "@gate-forge/plugin-protocol": "^0.1.0",
28
+ "zod": "^4.1.5"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/umiddey/gateforge.git",
36
+ "directory": "packages/pack-webhook"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/umiddey/gateforge/issues"
40
+ },
41
+ "homepage": "https://github.com/umiddey/gateforge#readme"
42
+ }