@skillsmith/core 0.5.3 → 0.5.4
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 +10 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/activation/ActivationManager.d.ts +7 -0
- package/dist/src/activation/ActivationManager.d.ts.map +1 -1
- package/dist/src/activation/ActivationManager.js +13 -4
- package/dist/src/activation/ActivationManager.js.map +1 -1
- package/dist/src/analysis/adapters/python.d.ts +16 -11
- package/dist/src/analysis/adapters/python.d.ts.map +1 -1
- package/dist/src/analysis/adapters/python.js +46 -61
- package/dist/src/analysis/adapters/python.js.map +1 -1
- package/dist/src/analysis/router.test.d.ts +2 -0
- package/dist/src/analysis/router.test.d.ts.map +1 -0
- package/dist/src/analysis/router.test.js +411 -0
- package/dist/src/analysis/router.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/manager.js +12 -5
- package/dist/src/analysis/tree-sitter/manager.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts +45 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js +264 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts +12 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js +74 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +93 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts +22 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +229 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +287 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts +17 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js +142 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts +43 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.js +88 -0
- package/dist/src/analysis/tree-sitter/queries/python.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts +13 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +174 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts +11 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.js +43 -0
- package/dist/src/analytics/ROIDashboardService.csv.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.d.ts +64 -3
- package/dist/src/analytics/ROIDashboardService.d.ts.map +1 -1
- package/dist/src/analytics/ROIDashboardService.js +116 -45
- package/dist/src/analytics/ROIDashboardService.js.map +1 -1
- package/dist/src/api/schemas.d.ts +70 -319
- package/dist/src/api/schemas.d.ts.map +1 -1
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts +18 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts.map +1 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js +121 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts +2 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.js +405 -0
- package/dist/src/billing/GDPRComplianceService.test.js.map +1 -0
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexer/SkillParser.coverage.test.d.ts +10 -0
- package/dist/src/indexer/SkillParser.coverage.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.coverage.test.js +76 -0
- package/dist/src/indexer/SkillParser.coverage.test.js.map +1 -0
- package/dist/src/indexer/SkillParser.test.d.ts +2 -0
- package/dist/src/indexer/SkillParser.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.test.js +375 -0
- package/dist/src/indexer/SkillParser.test.js.map +1 -0
- package/dist/src/scripts/validation/types.d.ts +14 -24
- package/dist/src/scripts/validation/types.d.ts.map +1 -1
- package/dist/src/services/skill-config-schema.d.ts +4 -36
- package/dist/src/services/skill-config-schema.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.d.ts +104 -10
- package/dist/src/sources/LocalFilesystemAdapter.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts +92 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js +157 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.js +218 -159
- package/dist/src/sources/LocalFilesystemAdapter.js.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts +78 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js +118 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js.map +1 -0
- package/dist/src/sources/index.d.ts +1 -1
- package/dist/src/sources/index.d.ts.map +1 -1
- package/dist/src/sources/index.js.map +1 -1
- package/dist/src/sources/types.d.ts +28 -0
- package/dist/src/sources/types.d.ts.map +1 -1
- package/dist/src/telemetry/tracer-imports.d.ts +13 -0
- package/dist/src/telemetry/tracer-imports.d.ts.map +1 -0
- package/dist/src/telemetry/tracer-imports.js +26 -0
- package/dist/src/telemetry/tracer-imports.js.map +1 -0
- package/dist/src/telemetry/tracer.d.ts.map +1 -1
- package/dist/src/telemetry/tracer.js +18 -21
- package/dist/src/telemetry/tracer.js.map +1 -1
- package/dist/src/utils/rate-limit.d.ts +39 -0
- package/dist/src/utils/rate-limit.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.js +48 -0
- package/dist/src/utils/rate-limit.js.map +1 -0
- package/dist/src/utils/rate-limit.test.d.ts +11 -0
- package/dist/src/utils/rate-limit.test.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.test.js +86 -0
- package/dist/src/utils/rate-limit.test.js.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts +178 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js +196 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js.map +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts.map +1 -1
- package/dist/src/webhooks/WebhookQueue.js +19 -0
- package/dist/src/webhooks/WebhookQueue.js.map +1 -1
- package/dist/src/webhooks/WebhookQueue.types.d.ts +11 -0
- package/dist/src/webhooks/WebhookQueue.types.d.ts.map +1 -1
- package/dist/src/webhooks/index.d.ts +1 -0
- package/dist/src/webhooks/index.d.ts.map +1 -1
- package/dist/src/webhooks/index.js +2 -0
- package/dist/src/webhooks/index.js.map +1 -1
- package/dist/src/webhooks/webhook-schemas.d.ts +117 -1212
- package/dist/src/webhooks/webhook-schemas.d.ts.map +1 -1
- package/dist/tests/ActivationManager.test.d.ts +13 -0
- package/dist/tests/ActivationManager.test.d.ts.map +1 -0
- package/dist/tests/ActivationManager.test.js +218 -0
- package/dist/tests/ActivationManager.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts +13 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js +314 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts +18 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js +344 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts +12 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.js +301 -0
- package/dist/tests/LocalFilesystemAdapter.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts +9 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.js +118 -0
- package/dist/tests/ROIDashboardService.coverage.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.test.js +87 -0
- package/dist/tests/ROIDashboardService.test.js.map +1 -1
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts +14 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts.map +1 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js +169 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js.map +1 -0
- package/dist/tests/ScraperAdapters.test.d.ts +5 -1
- package/dist/tests/ScraperAdapters.test.d.ts.map +1 -1
- package/dist/tests/ScraperAdapters.test.js +6 -336
- package/dist/tests/ScraperAdapters.test.js.map +1 -1
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts +2 -0
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js +333 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js.map +1 -0
- package/dist/tests/WebhookHandler.test.js +93 -1
- package/dist/tests/WebhookHandler.test.js.map +1 -1
- package/dist/tests/WebhookQueue.coverage.test.d.ts +19 -0
- package/dist/tests/WebhookQueue.coverage.test.d.ts.map +1 -0
- package/dist/tests/WebhookQueue.coverage.test.js +190 -0
- package/dist/tests/WebhookQueue.coverage.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +2 -2
- package/dist/tests/billing/GDPRCompliance.test.js +221 -36
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -1
- package/dist/tests/telemetry.test.js +126 -0
- package/dist/tests/telemetry.test.js.map +1 -1
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts +10 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js +109 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js.map +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-4291 / SMI-4308: WebhookDeadLetterRepository
|
|
3
|
+
*
|
|
4
|
+
* Thin persistence layer over the `webhook_dead_letters` table
|
|
5
|
+
* (migrations 070 + 077). Used by the WebhookQueue deadLetterSink wiring
|
|
6
|
+
* and by the `webhook-dlq` edge function.
|
|
7
|
+
*
|
|
8
|
+
* Plans:
|
|
9
|
+
* - docs/internal/implementation/github-wave-4-webhook-dlq.md
|
|
10
|
+
* - docs/internal/implementation/smi-4308-webhook-dlq-retry-real-enqueue.md
|
|
11
|
+
*
|
|
12
|
+
* Current operations (three terminal states: never-touched / retried / resolved):
|
|
13
|
+
* - insertDeadLetter(item, reason, teamId) — sink path
|
|
14
|
+
* - listOpen(teamId) — edge fn GET + repo consumers
|
|
15
|
+
* - listUnretried(teamId) — DEPRECATED alias for listOpen
|
|
16
|
+
* (kept for SMI-4322's delivery worker)
|
|
17
|
+
* - markRetried(id, success) — reserved for SMI-4322 delivery worker
|
|
18
|
+
* - markResolved(id, resolvedBy?) — operator acknowledgement (edge fn)
|
|
19
|
+
*
|
|
20
|
+
* No `audit_logs` emission is performed by this repository (plan-review finding
|
|
21
|
+
* C2: cross-tenant exposure risk on the global-read RLS of `audit_logs`). The
|
|
22
|
+
* edge function emits `webhook:dlq_resolved` for operator actions — see
|
|
23
|
+
* plan-review finding C-03 — because those writes are team-scoped.
|
|
24
|
+
*/
|
|
25
|
+
import type { WebhookQueueItem } from './WebhookQueue.types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Minimal Supabase-client shape this repository depends on. We avoid
|
|
28
|
+
* importing `@supabase/supabase-js` directly so the module stays usable
|
|
29
|
+
* from Deno edge functions, Node services, and tests with a fake client.
|
|
30
|
+
*/
|
|
31
|
+
export interface SupabaseLikeClient {
|
|
32
|
+
from(table: string): {
|
|
33
|
+
insert(row: Record<string, unknown>): Promise<{
|
|
34
|
+
error: {
|
|
35
|
+
message: string;
|
|
36
|
+
} | null;
|
|
37
|
+
}>;
|
|
38
|
+
select(columns: string): {
|
|
39
|
+
eq(column: string, value: string): {
|
|
40
|
+
order(column: string, opts?: {
|
|
41
|
+
ascending?: boolean;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
data: DeadLetterRow[] | null;
|
|
44
|
+
error: {
|
|
45
|
+
message: string;
|
|
46
|
+
} | null;
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
49
|
+
single?: () => Promise<{
|
|
50
|
+
data: DeadLetterRow | null;
|
|
51
|
+
error: {
|
|
52
|
+
message: string;
|
|
53
|
+
} | null;
|
|
54
|
+
}>;
|
|
55
|
+
};
|
|
56
|
+
update(row: Record<string, unknown>): {
|
|
57
|
+
eq(column: string, value: string): Promise<{
|
|
58
|
+
data: DeadLetterRow | null;
|
|
59
|
+
error: {
|
|
60
|
+
message: string;
|
|
61
|
+
} | null;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface DeadLetterRow {
|
|
67
|
+
id: string;
|
|
68
|
+
original_event_id: string;
|
|
69
|
+
endpoint_url: string;
|
|
70
|
+
payload: Record<string, unknown>;
|
|
71
|
+
failure_reason: string;
|
|
72
|
+
attempt_count: number;
|
|
73
|
+
first_failed_at: string;
|
|
74
|
+
last_failed_at: string;
|
|
75
|
+
retried_at: string | null;
|
|
76
|
+
retry_success: boolean | null;
|
|
77
|
+
resolved_at: string | null;
|
|
78
|
+
resolved_by: string | null;
|
|
79
|
+
team_id: string;
|
|
80
|
+
created_at: string;
|
|
81
|
+
}
|
|
82
|
+
export interface InsertDeadLetterInput {
|
|
83
|
+
originalEventId: string;
|
|
84
|
+
endpointUrl: string;
|
|
85
|
+
payload: Record<string, unknown>;
|
|
86
|
+
failureReason: string;
|
|
87
|
+
attemptCount: number;
|
|
88
|
+
firstFailedAt: Date | string;
|
|
89
|
+
lastFailedAt?: Date | string;
|
|
90
|
+
teamId: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* WebhookDeadLetterRepository — single-table CRUD wrapper.
|
|
94
|
+
*
|
|
95
|
+
* Instances are stateless beyond the client reference; construct freely per
|
|
96
|
+
* request in edge functions and once per process in long-lived services.
|
|
97
|
+
*/
|
|
98
|
+
export declare class WebhookDeadLetterRepository {
|
|
99
|
+
private readonly client;
|
|
100
|
+
constructor(client: SupabaseLikeClient);
|
|
101
|
+
/**
|
|
102
|
+
* Persist a dead-letter row for an exhausted queue item.
|
|
103
|
+
*
|
|
104
|
+
* Validates `endpoint_url` length at the application layer so the DB
|
|
105
|
+
* CHECK constraint is never hit in practice; a row that would violate the
|
|
106
|
+
* check throws a sanitized error without the payload.
|
|
107
|
+
*
|
|
108
|
+
* SMI-4307 F-03/F-02: When `input.teamId` is null or empty, the caller is an
|
|
109
|
+
* Individual-tier user (or a user whose team has not been provisioned yet).
|
|
110
|
+
* `webhook_dead_letters.team_id` is NOT NULL and RLS is team-scoped, so an
|
|
111
|
+
* insert would fail the FK/CHECK or land unreachable for its owner.
|
|
112
|
+
* Short-circuit instead: emit a console warning for ops visibility and
|
|
113
|
+
* return. A follow-on issue can promote this to an `audit_logs` write if
|
|
114
|
+
* orphan-DLQ observability becomes operationally valuable.
|
|
115
|
+
*
|
|
116
|
+
* F-02 overload note: the guard uses `input.teamId` (already resolved by the
|
|
117
|
+
* caller). Do NOT call `user_team_ids()` from here — the SMI-4309 overload
|
|
118
|
+
* collision between migrations 070 and 071 makes any such call brittle.
|
|
119
|
+
*/
|
|
120
|
+
insertDeadLetter(input: InsertDeadLetterInput): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* List open DLQ rows for a team — rows that are neither retried nor
|
|
123
|
+
* operator-resolved. Ordered by `last_failed_at` descending so fresh
|
|
124
|
+
* failures surface first.
|
|
125
|
+
*
|
|
126
|
+
* RLS guarantees cross-team isolation; the `team_id` filter below is a
|
|
127
|
+
* belt-and-braces server-side refinement for service-role callers.
|
|
128
|
+
*
|
|
129
|
+
* SMI-4308 H-03: the in-process filter now checks both terminal columns.
|
|
130
|
+
* Prior to this change the filter only checked `retried_at === null`, so
|
|
131
|
+
* resolved rows would have leaked into `listUnretried` results once the
|
|
132
|
+
* /resolve path landed.
|
|
133
|
+
*/
|
|
134
|
+
listOpen(teamId: string): Promise<DeadLetterRow[]>;
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated SMI-4308 — use {@link listOpen}. Retained because the
|
|
137
|
+
* dormant WebhookQueue sink wiring (SMI-4322) will call this through an
|
|
138
|
+
* older API contract. Removed once SMI-4322 lands.
|
|
139
|
+
*/
|
|
140
|
+
listUnretried(teamId: string): Promise<DeadLetterRow[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Mark a DLQ row as retried. Three-state `retry_success` semantics:
|
|
143
|
+
* - true — retry succeeded (item re-entered the queue and drained)
|
|
144
|
+
* - false — retry failed (item re-entered the queue and was re-deadlettered)
|
|
145
|
+
*
|
|
146
|
+
* NULL is reserved for rows that have never been retried; this method
|
|
147
|
+
* never writes NULL.
|
|
148
|
+
*
|
|
149
|
+
* SMI-4308 status: dormant in production until SMI-4322 ships the
|
|
150
|
+
* outbound delivery worker. The shipped edge function uses
|
|
151
|
+
* {@link markResolved} instead.
|
|
152
|
+
*/
|
|
153
|
+
markRetried(id: string, success: boolean): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Mark a DLQ row as operator-resolved (SMI-4308). The row is
|
|
156
|
+
* acknowledged out-of-band; no automated re-delivery took place. `retried_at`
|
|
157
|
+
* and `retry_success` are left NULL so the three-state retry semantic stays
|
|
158
|
+
* clean for the future delivery worker (SMI-4322).
|
|
159
|
+
*
|
|
160
|
+
* `resolvedBy` is typically the auth.users.id of the operator; when the
|
|
161
|
+
* edge function cannot extract the `sub` claim from the caller JWT, it
|
|
162
|
+
* passes `undefined` and the column is set to NULL (the RLS policy has
|
|
163
|
+
* already scoped the row to the caller's team).
|
|
164
|
+
*/
|
|
165
|
+
markResolved(id: string, resolvedBy?: string): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Build a sink function compatible with `WebhookQueueOptions.deadLetterSink`.
|
|
168
|
+
*
|
|
169
|
+
* The sink captures `teamId` and a payload-extraction strategy so the
|
|
170
|
+
* WebhookQueue stays decoupled from Supabase.
|
|
171
|
+
*/
|
|
172
|
+
makeSink(opts: {
|
|
173
|
+
teamId: string;
|
|
174
|
+
extractEndpointUrl: (item: WebhookQueueItem) => string;
|
|
175
|
+
extractPayload?: (item: WebhookQueueItem) => Record<string, unknown>;
|
|
176
|
+
}): (item: WebhookQueueItem, reason: string) => Promise<void>;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=WebhookDeadLetterRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookDeadLetterRepository.d.ts","sourceRoot":"","sources":["../../../src/webhooks/WebhookDeadLetterRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-4291 / SMI-4308: WebhookDeadLetterRepository
|
|
3
|
+
*
|
|
4
|
+
* Thin persistence layer over the `webhook_dead_letters` table
|
|
5
|
+
* (migrations 070 + 077). Used by the WebhookQueue deadLetterSink wiring
|
|
6
|
+
* and by the `webhook-dlq` edge function.
|
|
7
|
+
*
|
|
8
|
+
* Plans:
|
|
9
|
+
* - docs/internal/implementation/github-wave-4-webhook-dlq.md
|
|
10
|
+
* - docs/internal/implementation/smi-4308-webhook-dlq-retry-real-enqueue.md
|
|
11
|
+
*
|
|
12
|
+
* Current operations (three terminal states: never-touched / retried / resolved):
|
|
13
|
+
* - insertDeadLetter(item, reason, teamId) — sink path
|
|
14
|
+
* - listOpen(teamId) — edge fn GET + repo consumers
|
|
15
|
+
* - listUnretried(teamId) — DEPRECATED alias for listOpen
|
|
16
|
+
* (kept for SMI-4322's delivery worker)
|
|
17
|
+
* - markRetried(id, success) — reserved for SMI-4322 delivery worker
|
|
18
|
+
* - markResolved(id, resolvedBy?) — operator acknowledgement (edge fn)
|
|
19
|
+
*
|
|
20
|
+
* No `audit_logs` emission is performed by this repository (plan-review finding
|
|
21
|
+
* C2: cross-tenant exposure risk on the global-read RLS of `audit_logs`). The
|
|
22
|
+
* edge function emits `webhook:dlq_resolved` for operator actions — see
|
|
23
|
+
* plan-review finding C-03 — because those writes are team-scoped.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Coerce a Date|string to an ISO string suitable for TIMESTAMPTZ columns.
|
|
27
|
+
*/
|
|
28
|
+
function toIso(value) {
|
|
29
|
+
if (!value)
|
|
30
|
+
return new Date().toISOString();
|
|
31
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* WebhookDeadLetterRepository — single-table CRUD wrapper.
|
|
35
|
+
*
|
|
36
|
+
* Instances are stateless beyond the client reference; construct freely per
|
|
37
|
+
* request in edge functions and once per process in long-lived services.
|
|
38
|
+
*/
|
|
39
|
+
export class WebhookDeadLetterRepository {
|
|
40
|
+
client;
|
|
41
|
+
constructor(client) {
|
|
42
|
+
this.client = client;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Persist a dead-letter row for an exhausted queue item.
|
|
46
|
+
*
|
|
47
|
+
* Validates `endpoint_url` length at the application layer so the DB
|
|
48
|
+
* CHECK constraint is never hit in practice; a row that would violate the
|
|
49
|
+
* check throws a sanitized error without the payload.
|
|
50
|
+
*
|
|
51
|
+
* SMI-4307 F-03/F-02: When `input.teamId` is null or empty, the caller is an
|
|
52
|
+
* Individual-tier user (or a user whose team has not been provisioned yet).
|
|
53
|
+
* `webhook_dead_letters.team_id` is NOT NULL and RLS is team-scoped, so an
|
|
54
|
+
* insert would fail the FK/CHECK or land unreachable for its owner.
|
|
55
|
+
* Short-circuit instead: emit a console warning for ops visibility and
|
|
56
|
+
* return. A follow-on issue can promote this to an `audit_logs` write if
|
|
57
|
+
* orphan-DLQ observability becomes operationally valuable.
|
|
58
|
+
*
|
|
59
|
+
* F-02 overload note: the guard uses `input.teamId` (already resolved by the
|
|
60
|
+
* caller). Do NOT call `user_team_ids()` from here — the SMI-4309 overload
|
|
61
|
+
* collision between migrations 070 and 071 makes any such call brittle.
|
|
62
|
+
*/
|
|
63
|
+
async insertDeadLetter(input) {
|
|
64
|
+
if (!input.teamId || input.teamId.length === 0) {
|
|
65
|
+
console.warn('webhook-dlq: skipping DLQ insert — no teamId (orphan event)', {
|
|
66
|
+
originalEventId: input.originalEventId,
|
|
67
|
+
failureReason: input.failureReason,
|
|
68
|
+
attemptCount: input.attemptCount,
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const endpointUrl = input.endpointUrl ?? '';
|
|
73
|
+
if (endpointUrl.length === 0 || endpointUrl.length > 2048) {
|
|
74
|
+
throw new Error(`webhook-dlq: endpoint_url length ${endpointUrl.length} outside [1, 2048]`);
|
|
75
|
+
}
|
|
76
|
+
if (input.attemptCount < 1 || !Number.isInteger(input.attemptCount)) {
|
|
77
|
+
throw new Error(`webhook-dlq: attempt_count must be a positive integer (got ${input.attemptCount})`);
|
|
78
|
+
}
|
|
79
|
+
const row = {
|
|
80
|
+
original_event_id: input.originalEventId,
|
|
81
|
+
endpoint_url: endpointUrl,
|
|
82
|
+
payload: input.payload,
|
|
83
|
+
failure_reason: input.failureReason,
|
|
84
|
+
attempt_count: input.attemptCount,
|
|
85
|
+
first_failed_at: toIso(input.firstFailedAt),
|
|
86
|
+
last_failed_at: toIso(input.lastFailedAt),
|
|
87
|
+
team_id: input.teamId,
|
|
88
|
+
};
|
|
89
|
+
const { error } = await this.client.from('webhook_dead_letters').insert(row);
|
|
90
|
+
if (error) {
|
|
91
|
+
throw new Error(`webhook-dlq: insert failed: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* List open DLQ rows for a team — rows that are neither retried nor
|
|
96
|
+
* operator-resolved. Ordered by `last_failed_at` descending so fresh
|
|
97
|
+
* failures surface first.
|
|
98
|
+
*
|
|
99
|
+
* RLS guarantees cross-team isolation; the `team_id` filter below is a
|
|
100
|
+
* belt-and-braces server-side refinement for service-role callers.
|
|
101
|
+
*
|
|
102
|
+
* SMI-4308 H-03: the in-process filter now checks both terminal columns.
|
|
103
|
+
* Prior to this change the filter only checked `retried_at === null`, so
|
|
104
|
+
* resolved rows would have leaked into `listUnretried` results once the
|
|
105
|
+
* /resolve path landed.
|
|
106
|
+
*/
|
|
107
|
+
async listOpen(teamId) {
|
|
108
|
+
const { data, error } = await this.client
|
|
109
|
+
.from('webhook_dead_letters')
|
|
110
|
+
.select('*')
|
|
111
|
+
.eq('team_id', teamId)
|
|
112
|
+
.order('last_failed_at', { ascending: false });
|
|
113
|
+
if (error) {
|
|
114
|
+
throw new Error(`webhook-dlq: list failed: ${error.message}`);
|
|
115
|
+
}
|
|
116
|
+
return (data ?? []).filter((row) => row.retried_at === null && row.resolved_at === null);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @deprecated SMI-4308 — use {@link listOpen}. Retained because the
|
|
120
|
+
* dormant WebhookQueue sink wiring (SMI-4322) will call this through an
|
|
121
|
+
* older API contract. Removed once SMI-4322 lands.
|
|
122
|
+
*/
|
|
123
|
+
async listUnretried(teamId) {
|
|
124
|
+
return this.listOpen(teamId);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Mark a DLQ row as retried. Three-state `retry_success` semantics:
|
|
128
|
+
* - true — retry succeeded (item re-entered the queue and drained)
|
|
129
|
+
* - false — retry failed (item re-entered the queue and was re-deadlettered)
|
|
130
|
+
*
|
|
131
|
+
* NULL is reserved for rows that have never been retried; this method
|
|
132
|
+
* never writes NULL.
|
|
133
|
+
*
|
|
134
|
+
* SMI-4308 status: dormant in production until SMI-4322 ships the
|
|
135
|
+
* outbound delivery worker. The shipped edge function uses
|
|
136
|
+
* {@link markResolved} instead.
|
|
137
|
+
*/
|
|
138
|
+
async markRetried(id, success) {
|
|
139
|
+
const { error } = await this.client
|
|
140
|
+
.from('webhook_dead_letters')
|
|
141
|
+
.update({
|
|
142
|
+
retried_at: new Date().toISOString(),
|
|
143
|
+
retry_success: success,
|
|
144
|
+
})
|
|
145
|
+
.eq('id', id);
|
|
146
|
+
if (error) {
|
|
147
|
+
throw new Error(`webhook-dlq: markRetried failed: ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Mark a DLQ row as operator-resolved (SMI-4308). The row is
|
|
152
|
+
* acknowledged out-of-band; no automated re-delivery took place. `retried_at`
|
|
153
|
+
* and `retry_success` are left NULL so the three-state retry semantic stays
|
|
154
|
+
* clean for the future delivery worker (SMI-4322).
|
|
155
|
+
*
|
|
156
|
+
* `resolvedBy` is typically the auth.users.id of the operator; when the
|
|
157
|
+
* edge function cannot extract the `sub` claim from the caller JWT, it
|
|
158
|
+
* passes `undefined` and the column is set to NULL (the RLS policy has
|
|
159
|
+
* already scoped the row to the caller's team).
|
|
160
|
+
*/
|
|
161
|
+
async markResolved(id, resolvedBy) {
|
|
162
|
+
const { error } = await this.client
|
|
163
|
+
.from('webhook_dead_letters')
|
|
164
|
+
.update({
|
|
165
|
+
resolved_at: new Date().toISOString(),
|
|
166
|
+
resolved_by: resolvedBy ?? null,
|
|
167
|
+
})
|
|
168
|
+
.eq('id', id);
|
|
169
|
+
if (error) {
|
|
170
|
+
throw new Error(`webhook-dlq: markResolved failed: ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build a sink function compatible with `WebhookQueueOptions.deadLetterSink`.
|
|
175
|
+
*
|
|
176
|
+
* The sink captures `teamId` and a payload-extraction strategy so the
|
|
177
|
+
* WebhookQueue stays decoupled from Supabase.
|
|
178
|
+
*/
|
|
179
|
+
makeSink(opts) {
|
|
180
|
+
return async (item, reason) => {
|
|
181
|
+
await this.insertDeadLetter({
|
|
182
|
+
originalEventId: item.id,
|
|
183
|
+
endpointUrl: opts.extractEndpointUrl(item),
|
|
184
|
+
payload: opts.extractPayload
|
|
185
|
+
? opts.extractPayload(item)
|
|
186
|
+
: { repoFullName: item.repoFullName, filePath: item.filePath, commitSha: item.commitSha },
|
|
187
|
+
failureReason: reason,
|
|
188
|
+
attemptCount: Math.max(1, (item.retries ?? 0) + 1),
|
|
189
|
+
firstFailedAt: new Date(item.timestamp),
|
|
190
|
+
lastFailedAt: new Date(),
|
|
191
|
+
teamId: opts.teamId,
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=WebhookDeadLetterRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookDeadLetterRepository.js","sourceRoot":"","sources":["../../../src/webhooks/WebhookDeadLetterRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookQueue.d.ts","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAGhC,OAAO,KAAK,EACV,gBAAgB,EAEhB,UAAU,EACV,mBAAmB,EACpB,MAAM,yBAAyB,CAAA;AAIhC;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,iBAAiB,CAA6B;IAEtD,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,SAAS,CAAC,CAA2C;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAsC;IAC1D,OAAO,CAAC,GAAG,CAA6E;
|
|
1
|
+
{"version":3,"file":"WebhookQueue.d.ts","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAGhC,OAAO,KAAK,EACV,gBAAgB,EAEhB,UAAU,EACV,mBAAmB,EACpB,MAAM,yBAAyB,CAAA;AAIhC;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,iBAAiB,CAA6B;IAEtD,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,SAAS,CAAC,CAA2C;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAsC;IAC1D,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,cAAc,CAAC,CAA2D;gBAEtE,OAAO,GAAE,mBAAwB;IAY7C;;;OAGG;IACG,GAAG,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IA2CnD;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO;IAuB7C;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAiCnB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;OAEG;YACW,YAAY;IAmC1B;;OAEG;YACW,WAAW;IAsFzB;;OAEG;IACH,QAAQ,IAAI,UAAU;IAuBtB;;OAEG;IACH,QAAQ,IAAI,gBAAgB,EAAE;IAI9B;;OAEG;IACH,KAAK,IAAI,IAAI;IAiBb;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAWxC;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIxE;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,kBAAkB,IAAI,MAAM;CAG7B;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -25,6 +25,7 @@ export class WebhookQueue {
|
|
|
25
25
|
processor;
|
|
26
26
|
onProcessed;
|
|
27
27
|
log;
|
|
28
|
+
deadLetterSink;
|
|
28
29
|
constructor(options = {}) {
|
|
29
30
|
this.concurrency = options.concurrency ?? 2;
|
|
30
31
|
this.debounceMs = options.debounceMs ?? 5000; // 5 seconds default
|
|
@@ -34,6 +35,7 @@ export class WebhookQueue {
|
|
|
34
35
|
this.processor = options.processor;
|
|
35
36
|
this.onProcessed = options.onProcessed;
|
|
36
37
|
this.log = options.onLog ?? (() => { });
|
|
38
|
+
this.deadLetterSink = options.deadLetterSink;
|
|
37
39
|
}
|
|
38
40
|
/**
|
|
39
41
|
* Add an item to the queue
|
|
@@ -214,6 +216,23 @@ export class WebhookQueue {
|
|
|
214
216
|
if (item.retries >= this.maxRetries) {
|
|
215
217
|
// Max retries exceeded - remove from queue
|
|
216
218
|
this.items.delete(item.id);
|
|
219
|
+
// SMI-4291: dead-letter sink (plan-review finding C3).
|
|
220
|
+
// Catch + console.error; never rethrow. Local queue state is already
|
|
221
|
+
// cleared, so a sink failure must not cascade or re-queue the item.
|
|
222
|
+
if (this.deadLetterSink) {
|
|
223
|
+
try {
|
|
224
|
+
await this.deadLetterSink(item, errorMessage);
|
|
225
|
+
}
|
|
226
|
+
catch (sinkError) {
|
|
227
|
+
const sinkMessage = sinkError instanceof Error ? sinkError.message : String(sinkError);
|
|
228
|
+
// Operator-visibility fallback: finding C3 requires no rethrow.
|
|
229
|
+
console.error('[webhook-dlq] Sink failed; event dropped locally', {
|
|
230
|
+
event_id: item.id,
|
|
231
|
+
reason: errorMessage,
|
|
232
|
+
sink_error: sinkMessage,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
217
236
|
const result = {
|
|
218
237
|
item,
|
|
219
238
|
success: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookQueue.js","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAEnG;;GAEG;AACH,MAAM,OAAO,YAAY;IACf,KAAK,GAAkC,IAAI,GAAG,EAAE,CAAA;IAChD,UAAU,GAAgB,IAAI,GAAG,EAAE,CAAA;IACnC,cAAc,GAAgC,IAAI,GAAG,EAAE,CAAA;IACvD,YAAY,GAAG,KAAK,CAAA;IACpB,iBAAiB,GAAyB,IAAI,CAAA;IAE9C,WAAW,CAAQ;IACnB,UAAU,CAAQ;IAClB,UAAU,CAAQ;IAClB,YAAY,CAAQ;IACpB,OAAO,CAAQ;IACf,SAAS,CAA4C;IACrD,WAAW,CAAuC;IAClD,GAAG,CAA6E;
|
|
1
|
+
{"version":3,"file":"WebhookQueue.js","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.ts"],"names":[],"mappings":"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"}
|
|
@@ -145,5 +145,16 @@ export interface WebhookQueueOptions {
|
|
|
145
145
|
* Callback for logging
|
|
146
146
|
*/
|
|
147
147
|
onLog?: (level: 'info' | 'warn' | 'error', message: string, data?: unknown) => void;
|
|
148
|
+
/**
|
|
149
|
+
* SMI-4291: Dead-letter sink invoked when an item exhausts maxRetries.
|
|
150
|
+
*
|
|
151
|
+
* Contract (plan-review finding C3):
|
|
152
|
+
* - `WebhookQueue` wraps the call in try/catch and logs sink errors via
|
|
153
|
+
* `console.error`. Sink implementations therefore MUST NOT rely on the
|
|
154
|
+
* queue re-throwing — local in-memory state has already been cleared.
|
|
155
|
+
* - The sink should be idempotent: a failure during persistence will not
|
|
156
|
+
* cause the queue to retry the write.
|
|
157
|
+
*/
|
|
158
|
+
deadLetterSink?: (item: WebhookQueueItem, reason: string) => Promise<void>;
|
|
148
159
|
}
|
|
149
160
|
//# sourceMappingURL=WebhookQueue.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookQueue.types.d.ts","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,CAAA;AAExF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IAEV;;OAEG;IACH,IAAI,EAAE,aAAa,CAAA;IAEnB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAA;IAEvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAA;IAEtB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IAEzC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IAErC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAErD;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAElD;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"WebhookQueue.types.d.ts","sourceRoot":"","sources":["../../../src/webhooks/WebhookQueue.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,CAAA;AAExF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IAEV;;OAEG;IACH,IAAI,EAAE,aAAa,CAAA;IAEnB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAA;IAEvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAA;IAEtB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IAEzC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IAErC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAErD;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAElD;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAEnF;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3E"}
|
|
@@ -9,4 +9,5 @@
|
|
|
9
9
|
export { type WebhookEventType, type RepositoryAction, type GitUser, type PushCommit, type RepositoryOwner, type WebhookRepository, type WebhookSender, type PushEventPayload, type RepositoryEventPayload, type PingEventPayload, type WebhookPayload, type ParsedWebhookEvent, type SignatureVerificationResult, type SkillFileChange, isSkillFile, extractSkillChanges, parseWebhookPayload, } from './WebhookPayload.js';
|
|
10
10
|
export { WebhookHandler, type WebhookHandlerOptions, type WebhookHandleResult, type DeliveryStats, } from './WebhookHandler.js';
|
|
11
11
|
export { WebhookQueue, type QueueItemType, type QueuePriority, type WebhookQueueItem, type QueueProcessResult, type QueueStats, type WebhookQueueOptions, } from './WebhookQueue.js';
|
|
12
|
+
export { WebhookDeadLetterRepository, type DeadLetterRow, type InsertDeadLetterInput, type SupabaseLikeClient, } from './WebhookDeadLetterRepository.js';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EACL,2BAA2B,EAC3B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACxB,MAAM,kCAAkC,CAAA"}
|
|
@@ -12,4 +12,6 @@ export { isSkillFile, extractSkillChanges, parseWebhookPayload, } from './Webhoo
|
|
|
12
12
|
export { WebhookHandler, } from './WebhookHandler.js';
|
|
13
13
|
// WebhookQueue - Event queue
|
|
14
14
|
export { WebhookQueue, } from './WebhookQueue.js';
|
|
15
|
+
// SMI-4291: WebhookDeadLetterRepository - DLQ persistence
|
|
16
|
+
export { WebhookDeadLetterRepository, } from './WebhookDeadLetterRepository.js';
|
|
15
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uCAAuC;AACvC,OAAO,EAeL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAE5B,oCAAoC;AACpC,OAAO,EACL,cAAc,GAIf,MAAM,qBAAqB,CAAA;AAE5B,6BAA6B;AAC7B,OAAO,EACL,YAAY,GAOb,MAAM,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uCAAuC;AACvC,OAAO,EAeL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAE5B,oCAAoC;AACpC,OAAO,EACL,cAAc,GAIf,MAAM,qBAAqB,CAAA;AAE5B,6BAA6B;AAC7B,OAAO,EACL,YAAY,GAOb,MAAM,mBAAmB,CAAA;AAE1B,0DAA0D;AAC1D,OAAO,EACL,2BAA2B,GAI5B,MAAM,kCAAkC,CAAA"}
|