@ontrails/core 1.0.0-beta.15 → 1.0.0-beta.16
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 +63 -0
- package/README.md +61 -16
- package/package.json +9 -8
- package/src/activation-provenance.ts +111 -0
- package/src/activation-source-compatibility.ts +430 -0
- package/src/activation-source-projection.ts +221 -0
- package/src/activation-source.ts +91 -0
- package/src/blob-ref.ts +51 -0
- package/src/context.ts +4 -0
- package/src/{internal/cross-batch.ts → cross-batch.ts} +3 -11
- package/src/detours.ts +8 -0
- package/src/draft.ts +4 -0
- package/src/error-projection.ts +51 -0
- package/src/errors.ts +192 -39
- package/src/execute.ts +443 -44
- package/src/fire.ts +965 -91
- package/src/index.ts +285 -38
- package/src/layer-projection.ts +192 -0
- package/src/layer.ts +32 -1
- package/src/observe.ts +361 -0
- package/src/permits.ts +12 -0
- package/src/redaction/patterns.ts +6 -3
- package/src/resilience.ts +8 -8
- package/src/resource-config.ts +477 -44
- package/src/resource.ts +21 -1
- package/src/result.ts +18 -0
- package/src/run.ts +2 -2
- package/src/schedule-runtime.ts +689 -0
- package/src/schedule.ts +324 -0
- package/src/serialization.ts +90 -57
- package/src/signal-diagnostics.ts +633 -0
- package/src/{internal/signal-ref.ts → signal-ref.ts} +1 -1
- package/src/signal.ts +37 -0
- package/src/store/accessor-protocol.ts +5 -5
- package/src/structured-examples.ts +245 -0
- package/src/surface-derivation.ts +91 -0
- package/src/surface-filter.ts +7 -13
- package/src/topo.ts +386 -6
- package/src/tracing.ts +345 -0
- package/src/trail.ts +168 -22
- package/src/trails/derive-trail.ts +1 -1
- package/src/trails/ingest.ts +7 -15
- package/src/{internal/trails-db.ts → trails-db.ts} +42 -21
- package/src/transport-error-map.ts +146 -28
- package/src/types.ts +82 -28
- package/src/validate-established-topo.ts +6 -0
- package/src/validate-topo.ts +212 -5
- package/src/validation.ts +58 -1
- package/src/webhook.ts +276 -0
- package/src/{internal/zod-wrappers.ts → zod-wrappers.ts} +0 -6
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/blob-ref.d.ts +0 -20
- package/dist/blob-ref.d.ts.map +0 -1
- package/dist/blob-ref.js +0 -22
- package/dist/blob-ref.js.map +0 -1
- package/dist/branded.d.ts +0 -36
- package/dist/branded.d.ts.map +0 -1
- package/dist/branded.js +0 -89
- package/dist/branded.js.map +0 -1
- package/dist/collections.d.ts +0 -31
- package/dist/collections.d.ts.map +0 -1
- package/dist/collections.js +0 -60
- package/dist/collections.js.map +0 -1
- package/dist/context.d.ts +0 -22
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -37
- package/dist/context.js.map +0 -1
- package/dist/contour.d.ts +0 -81
- package/dist/contour.d.ts.map +0 -1
- package/dist/contour.js +0 -176
- package/dist/contour.js.map +0 -1
- package/dist/cross-schema.d.ts +0 -18
- package/dist/cross-schema.d.ts.map +0 -1
- package/dist/cross-schema.js +0 -29
- package/dist/cross-schema.js.map +0 -1
- package/dist/derive.d.ts +0 -44
- package/dist/derive.d.ts.map +0 -1
- package/dist/derive.js +0 -149
- package/dist/derive.js.map +0 -1
- package/dist/draft.d.ts +0 -28
- package/dist/draft.d.ts.map +0 -1
- package/dist/draft.js +0 -157
- package/dist/draft.js.map +0 -1
- package/dist/errors.d.ts +0 -141
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -185
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts +0 -52
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js +0 -511
- package/dist/execute.js.map +0 -1
- package/dist/fetch.d.ts +0 -15
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js +0 -102
- package/dist/fetch.js.map +0 -1
- package/dist/fire.d.ts +0 -43
- package/dist/fire.d.ts.map +0 -1
- package/dist/fire.js +0 -185
- package/dist/fire.js.map +0 -1
- package/dist/guards.d.ts +0 -17
- package/dist/guards.d.ts.map +0 -1
- package/dist/guards.js +0 -25
- package/dist/guards.js.map +0 -1
- package/dist/index.d.ts +0 -59
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -61
- package/dist/index.js.map +0 -1
- package/dist/internal/cross-batch.d.ts +0 -44
- package/dist/internal/cross-batch.d.ts.map +0 -1
- package/dist/internal/cross-batch.js +0 -58
- package/dist/internal/cross-batch.js.map +0 -1
- package/dist/internal/fork-ctx.d.ts +0 -41
- package/dist/internal/fork-ctx.d.ts.map +0 -1
- package/dist/internal/fork-ctx.js +0 -47
- package/dist/internal/fork-ctx.js.map +0 -1
- package/dist/internal/signal-ref.d.ts +0 -15
- package/dist/internal/signal-ref.d.ts.map +0 -1
- package/dist/internal/signal-ref.js +0 -41
- package/dist/internal/signal-ref.js.map +0 -1
- package/dist/internal/topo-saves.d.ts +0 -53
- package/dist/internal/topo-saves.d.ts.map +0 -1
- package/dist/internal/topo-saves.js +0 -449
- package/dist/internal/topo-saves.js.map +0 -1
- package/dist/internal/topo-snapshots.d.ts +0 -57
- package/dist/internal/topo-snapshots.d.ts.map +0 -1
- package/dist/internal/topo-snapshots.js +0 -372
- package/dist/internal/topo-snapshots.js.map +0 -1
- package/dist/internal/topo-store-read.d.ts +0 -70
- package/dist/internal/topo-store-read.d.ts.map +0 -1
- package/dist/internal/topo-store-read.js +0 -221
- package/dist/internal/topo-store-read.js.map +0 -1
- package/dist/internal/topo-store.d.ts +0 -26
- package/dist/internal/topo-store.d.ts.map +0 -1
- package/dist/internal/topo-store.js +0 -621
- package/dist/internal/topo-store.js.map +0 -1
- package/dist/internal/tracing.d.ts +0 -98
- package/dist/internal/tracing.d.ts.map +0 -1
- package/dist/internal/tracing.js +0 -102
- package/dist/internal/tracing.js.map +0 -1
- package/dist/internal/trails-db.d.ts +0 -16
- package/dist/internal/trails-db.d.ts.map +0 -1
- package/dist/internal/trails-db.js +0 -118
- package/dist/internal/trails-db.js.map +0 -1
- package/dist/internal/zod-wrappers.d.ts +0 -40
- package/dist/internal/zod-wrappers.d.ts.map +0 -1
- package/dist/internal/zod-wrappers.js +0 -59
- package/dist/internal/zod-wrappers.js.map +0 -1
- package/dist/layer.d.ts +0 -23
- package/dist/layer.d.ts.map +0 -1
- package/dist/layer.js +0 -21
- package/dist/layer.js.map +0 -1
- package/dist/path-security.d.ts +0 -28
- package/dist/path-security.d.ts.map +0 -1
- package/dist/path-security.js +0 -63
- package/dist/path-security.js.map +0 -1
- package/dist/patterns/bulk.d.ts +0 -15
- package/dist/patterns/bulk.d.ts.map +0 -1
- package/dist/patterns/bulk.js +0 -14
- package/dist/patterns/bulk.js.map +0 -1
- package/dist/patterns/change.d.ts +0 -10
- package/dist/patterns/change.d.ts.map +0 -1
- package/dist/patterns/change.js +0 -10
- package/dist/patterns/change.js.map +0 -1
- package/dist/patterns/date-range.d.ts +0 -10
- package/dist/patterns/date-range.d.ts.map +0 -1
- package/dist/patterns/date-range.js +0 -10
- package/dist/patterns/date-range.js.map +0 -1
- package/dist/patterns/index.d.ts +0 -9
- package/dist/patterns/index.d.ts.map +0 -1
- package/dist/patterns/index.js +0 -9
- package/dist/patterns/index.js.map +0 -1
- package/dist/patterns/pagination.d.ts +0 -18
- package/dist/patterns/pagination.d.ts.map +0 -1
- package/dist/patterns/pagination.js +0 -18
- package/dist/patterns/pagination.js.map +0 -1
- package/dist/patterns/progress.d.ts +0 -11
- package/dist/patterns/progress.d.ts.map +0 -1
- package/dist/patterns/progress.js +0 -11
- package/dist/patterns/progress.js.map +0 -1
- package/dist/patterns/sorting.d.ts +0 -13
- package/dist/patterns/sorting.d.ts.map +0 -1
- package/dist/patterns/sorting.js +0 -10
- package/dist/patterns/sorting.js.map +0 -1
- package/dist/patterns/status.d.ts +0 -15
- package/dist/patterns/status.d.ts.map +0 -1
- package/dist/patterns/status.js +0 -9
- package/dist/patterns/status.js.map +0 -1
- package/dist/patterns/timestamps.d.ts +0 -10
- package/dist/patterns/timestamps.d.ts.map +0 -1
- package/dist/patterns/timestamps.js +0 -10
- package/dist/patterns/timestamps.js.map +0 -1
- package/dist/redaction/index.d.ts +0 -4
- package/dist/redaction/index.d.ts.map +0 -1
- package/dist/redaction/index.js +0 -3
- package/dist/redaction/index.js.map +0 -1
- package/dist/redaction/patterns.d.ts +0 -9
- package/dist/redaction/patterns.d.ts.map +0 -1
- package/dist/redaction/patterns.js +0 -39
- package/dist/redaction/patterns.js.map +0 -1
- package/dist/redaction/redactor.d.ts +0 -27
- package/dist/redaction/redactor.d.ts.map +0 -1
- package/dist/redaction/redactor.js +0 -89
- package/dist/redaction/redactor.js.map +0 -1
- package/dist/resilience.d.ts +0 -34
- package/dist/resilience.d.ts.map +0 -1
- package/dist/resilience.js +0 -164
- package/dist/resilience.js.map +0 -1
- package/dist/resource-config.d.ts +0 -22
- package/dist/resource-config.d.ts.map +0 -1
- package/dist/resource-config.js +0 -210
- package/dist/resource-config.js.map +0 -1
- package/dist/resource.d.ts +0 -74
- package/dist/resource.d.ts.map +0 -1
- package/dist/resource.js +0 -61
- package/dist/resource.js.map +0 -1
- package/dist/result.d.ts +0 -57
- package/dist/result.d.ts.map +0 -1
- package/dist/result.js +0 -156
- package/dist/result.js.map +0 -1
- package/dist/run.d.ts +0 -28
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -35
- package/dist/run.js.map +0 -1
- package/dist/serialization.d.ts +0 -27
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -154
- package/dist/serialization.js.map +0 -1
- package/dist/signal.d.ts +0 -33
- package/dist/signal.d.ts.map +0 -1
- package/dist/signal.js +0 -17
- package/dist/signal.js.map +0 -1
- package/dist/store/accessor-protocol.d.ts +0 -51
- package/dist/store/accessor-protocol.d.ts.map +0 -1
- package/dist/store/accessor-protocol.js +0 -2
- package/dist/store/accessor-protocol.js.map +0 -1
- package/dist/store/index.d.ts +0 -2
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/index.js +0 -2
- package/dist/store/index.js.map +0 -1
- package/dist/surface-filter.d.ts +0 -13
- package/dist/surface-filter.d.ts.map +0 -1
- package/dist/surface-filter.js +0 -76
- package/dist/surface-filter.js.map +0 -1
- package/dist/topo-store.d.ts +0 -62
- package/dist/topo-store.d.ts.map +0 -1
- package/dist/topo-store.js +0 -319
- package/dist/topo-store.js.map +0 -1
- package/dist/topo.d.ts +0 -39
- package/dist/topo.d.ts.map +0 -1
- package/dist/topo.js +0 -241
- package/dist/topo.js.map +0 -1
- package/dist/trail.d.ts +0 -163
- package/dist/trail.d.ts.map +0 -1
- package/dist/trail.js +0 -75
- package/dist/trail.js.map +0 -1
- package/dist/trails/derive-trail.d.ts +0 -66
- package/dist/trails/derive-trail.d.ts.map +0 -1
- package/dist/trails/derive-trail.js +0 -378
- package/dist/trails/derive-trail.js.map +0 -1
- package/dist/trails/index.d.ts +0 -5
- package/dist/trails/index.d.ts.map +0 -1
- package/dist/trails/index.js +0 -3
- package/dist/trails/index.js.map +0 -1
- package/dist/trails/ingest.d.ts +0 -24
- package/dist/trails/ingest.d.ts.map +0 -1
- package/dist/trails/ingest.js +0 -63
- package/dist/trails/ingest.js.map +0 -1
- package/dist/transport-error-map.d.ts +0 -106
- package/dist/transport-error-map.d.ts.map +0 -1
- package/dist/transport-error-map.js +0 -24
- package/dist/transport-error-map.js.map +0 -1
- package/dist/type-checks.test-d.d.ts +0 -71
- package/dist/type-checks.test-d.d.ts.map +0 -1
- package/dist/type-checks.test-d.js +0 -12
- package/dist/type-checks.test-d.js.map +0 -1
- package/dist/type-utils.d.ts +0 -33
- package/dist/type-utils.d.ts.map +0 -1
- package/dist/type-utils.js +0 -12
- package/dist/type-utils.js.map +0 -1
- package/dist/types.d.ts +0 -177
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/validate-established-topo.d.ts +0 -76
- package/dist/validate-established-topo.d.ts.map +0 -1
- package/dist/validate-established-topo.js +0 -43
- package/dist/validate-established-topo.js.map +0 -1
- package/dist/validate-topo.d.ts +0 -24
- package/dist/validate-topo.d.ts.map +0 -1
- package/dist/validate-topo.js +0 -193
- package/dist/validate-topo.js.map +0 -1
- package/dist/validation.d.ts +0 -27
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -165
- package/dist/validation.js.map +0 -1
- package/dist/workspace.d.ts +0 -25
- package/dist/workspace.d.ts.map +0 -1
- package/dist/workspace.js +0 -57
- package/dist/workspace.js.map +0 -1
- package/src/__tests__/blob-ref.test.ts +0 -103
- package/src/__tests__/branded.test.ts +0 -148
- package/src/__tests__/collections.test.ts +0 -126
- package/src/__tests__/context.test.ts +0 -77
- package/src/__tests__/contour.test.ts +0 -263
- package/src/__tests__/derive-trail.test.ts +0 -668
- package/src/__tests__/derive.test.ts +0 -260
- package/src/__tests__/errors.test.ts +0 -427
- package/src/__tests__/execute.test.ts +0 -1563
- package/src/__tests__/fetch.test.ts +0 -217
- package/src/__tests__/fire.test.ts +0 -1056
- package/src/__tests__/fork-ctx.test.ts +0 -83
- package/src/__tests__/guards.test.ts +0 -102
- package/src/__tests__/ingest.test.ts +0 -191
- package/src/__tests__/layer.test.ts +0 -125
- package/src/__tests__/path-security.test.ts +0 -114
- package/src/__tests__/patterns.test.ts +0 -273
- package/src/__tests__/redaction.test.ts +0 -244
- package/src/__tests__/resilience.test.ts +0 -246
- package/src/__tests__/resource.test.ts +0 -204
- package/src/__tests__/result.test.ts +0 -155
- package/src/__tests__/run.test.ts +0 -181
- package/src/__tests__/serialization.test.ts +0 -401
- package/src/__tests__/service-config.test.ts +0 -224
- package/src/__tests__/signal.test.ts +0 -82
- package/src/__tests__/surface-filter.test.ts +0 -196
- package/src/__tests__/topo-store-read.test.ts +0 -256
- package/src/__tests__/topo-store.test.ts +0 -869
- package/src/__tests__/topo.test.ts +0 -573
- package/src/__tests__/trail-permit.test.ts +0 -60
- package/src/__tests__/trail.test.ts +0 -426
- package/src/__tests__/trails-db.test.ts +0 -198
- package/src/__tests__/transport-error-map.test.ts +0 -82
- package/src/__tests__/type-utils.test.ts +0 -118
- package/src/__tests__/validate-topo.test.ts +0 -611
- package/src/__tests__/validation.test.ts +0 -336
- package/src/__tests__/workspace.test.ts +0 -183
- package/src/__tests__/zod-wrappers.test.ts +0 -62
- package/src/internal/topo-snapshots.ts +0 -517
- package/src/internal/topo-store-read.ts +0 -481
- package/src/internal/topo-store.ts +0 -1192
- package/src/internal/tracing.ts +0 -187
- package/src/topo-store.ts +0 -510
- package/src/type-checks.test-d.ts +0 -106
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/src/validate-topo.ts
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Structural validation for a Topo graph.
|
|
3
3
|
*
|
|
4
|
-
* Checks trail crossing references, example input validity,
|
|
5
|
-
* references, and output schema completeness. Returns
|
|
6
|
-
* issues collected into a single ValidationError.
|
|
4
|
+
* Checks trail crossing references, example input validity, signal origin
|
|
5
|
+
* references, activation source kinds, and output schema completeness. Returns
|
|
6
|
+
* a Result with all issues collected into a single ValidationError.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { ActivationSchemaIssue } from './activation-source-compatibility.js';
|
|
10
|
+
import { getActivationSourceInputCompatibilityIssues } from './activation-source-compatibility.js';
|
|
11
|
+
import {
|
|
12
|
+
activationSourceDeclarationSignature,
|
|
13
|
+
activationSourceKey,
|
|
14
|
+
} from './activation-source-projection.js';
|
|
9
15
|
import type { AnyContour } from './contour.js';
|
|
10
16
|
import { getContourReferences } from './contour.js';
|
|
11
17
|
import { ValidationError } from './errors.js';
|
|
18
|
+
import type { ActivationEntry } from './activation-source.js';
|
|
19
|
+
import { isKnownActivationSourceKind } from './activation-source.js';
|
|
12
20
|
import { isDraftId } from './draft.js';
|
|
13
21
|
import type { AnySignal } from './signal.js';
|
|
22
|
+
import { validateScheduleSource } from './schedule.js';
|
|
14
23
|
import { Result } from './result.js';
|
|
15
24
|
import type { Topo } from './topo.js';
|
|
16
25
|
import type { AnyTrail } from './trail.js';
|
|
17
26
|
import { validateInput } from './validation.js';
|
|
27
|
+
import { validateWebhookSource } from './webhook.js';
|
|
18
28
|
|
|
19
29
|
// ---------------------------------------------------------------------------
|
|
20
30
|
// Issue shape
|
|
@@ -24,8 +34,14 @@ export interface TopoIssue {
|
|
|
24
34
|
readonly trailId: string;
|
|
25
35
|
readonly rule: string;
|
|
26
36
|
readonly message: string;
|
|
37
|
+
readonly inputPath?: readonly (string | number)[];
|
|
38
|
+
readonly schemaIssues?: readonly TopoSchemaIssue[];
|
|
39
|
+
readonly sourceId?: string;
|
|
40
|
+
readonly sourceKind?: string;
|
|
27
41
|
}
|
|
28
42
|
|
|
43
|
+
export type TopoSchemaIssue = ActivationSchemaIssue;
|
|
44
|
+
|
|
29
45
|
// ---------------------------------------------------------------------------
|
|
30
46
|
// Validators
|
|
31
47
|
// ---------------------------------------------------------------------------
|
|
@@ -205,6 +221,193 @@ const checkSignalOrigins = (
|
|
|
205
221
|
return issues;
|
|
206
222
|
};
|
|
207
223
|
|
|
224
|
+
const checkSignalReferences = (
|
|
225
|
+
trails: ReadonlyMap<string, AnyTrail>,
|
|
226
|
+
signals: ReadonlyMap<string, AnySignal>
|
|
227
|
+
): TopoIssue[] => {
|
|
228
|
+
const issues: TopoIssue[] = [];
|
|
229
|
+
|
|
230
|
+
for (const [id, trail] of trails) {
|
|
231
|
+
for (const signalId of trail.fires ?? []) {
|
|
232
|
+
if (!signals.has(signalId) && !isDraftId(signalId)) {
|
|
233
|
+
issues.push({
|
|
234
|
+
message: `Trail fires signal "${signalId}" which is not in the topo`,
|
|
235
|
+
rule: 'signal-fire-exists',
|
|
236
|
+
trailId: id,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const signalId of trail.on ?? []) {
|
|
242
|
+
if (!signals.has(signalId) && !isDraftId(signalId)) {
|
|
243
|
+
issues.push({
|
|
244
|
+
message: `Trail declares on signal "${signalId}" which is not in the topo`,
|
|
245
|
+
rule: 'signal-on-exists',
|
|
246
|
+
trailId: id,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return issues;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const checkActivationSources = (
|
|
256
|
+
trails: ReadonlyMap<string, AnyTrail>
|
|
257
|
+
): TopoIssue[] => {
|
|
258
|
+
const issues: TopoIssue[] = [];
|
|
259
|
+
const sourceDeclarations = new Map<
|
|
260
|
+
string,
|
|
261
|
+
{
|
|
262
|
+
readonly signature: string;
|
|
263
|
+
readonly trailId: string;
|
|
264
|
+
}
|
|
265
|
+
>();
|
|
266
|
+
const trailSourceEdges = new Set<string>();
|
|
267
|
+
|
|
268
|
+
for (const [id, trail] of trails) {
|
|
269
|
+
for (const activation of trail.activationSources ?? []) {
|
|
270
|
+
if (!isKnownActivationSourceKind(activation.source.kind)) {
|
|
271
|
+
issues.push({
|
|
272
|
+
message: `Trail declares on source "${activation.source.id}" with unsupported source kind "${activation.source.kind}"`,
|
|
273
|
+
rule: 'activation-source-kind-known',
|
|
274
|
+
trailId: id,
|
|
275
|
+
});
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const sourceKey = activationSourceKey(activation.source);
|
|
280
|
+
const edgeKey = `${id}\0${sourceKey}`;
|
|
281
|
+
if (trailSourceEdges.has(edgeKey)) {
|
|
282
|
+
issues.push({
|
|
283
|
+
message: `Trail declares activation source "${activation.source.id}" (${activation.source.kind}) more than once. Keep one source-to-trail activation edge, or split distinct activation behavior into distinct source ids.`,
|
|
284
|
+
rule: 'activation-source-edge-unique',
|
|
285
|
+
sourceId: activation.source.id,
|
|
286
|
+
sourceKind: activation.source.kind,
|
|
287
|
+
trailId: id,
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
trailSourceEdges.add(edgeKey);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!isDraftId(activation.source.id)) {
|
|
294
|
+
const signature = activationSourceDeclarationSignature(
|
|
295
|
+
activation.source
|
|
296
|
+
);
|
|
297
|
+
const previous = sourceDeclarations.get(sourceKey);
|
|
298
|
+
if (previous === undefined) {
|
|
299
|
+
sourceDeclarations.set(sourceKey, { signature, trailId: id });
|
|
300
|
+
} else if (previous.signature !== signature) {
|
|
301
|
+
issues.push({
|
|
302
|
+
message: `Activation source "${activation.source.id}" (${activation.source.kind}) is declared with conflicting source options by trails "${previous.trailId}" and "${id}". Use one canonical source declaration per source id, or give distinct source configurations distinct ids.`,
|
|
303
|
+
rule: 'activation-source-definition-unique',
|
|
304
|
+
sourceId: activation.source.id,
|
|
305
|
+
sourceKind: activation.source.kind,
|
|
306
|
+
trailId: id,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const scheduleIssues = validateScheduleSource(activation.source);
|
|
312
|
+
for (const issue of scheduleIssues) {
|
|
313
|
+
issues.push({
|
|
314
|
+
inputPath: [issue.field],
|
|
315
|
+
message: `Trail declares schedule source "${activation.source.id}" with invalid ${issue.field}: ${issue.message}`,
|
|
316
|
+
rule: 'activation-schedule-valid',
|
|
317
|
+
schemaIssues: [
|
|
318
|
+
{ code: issue.field, message: issue.message, path: [issue.field] },
|
|
319
|
+
],
|
|
320
|
+
sourceId: activation.source.id,
|
|
321
|
+
sourceKind: activation.source.kind,
|
|
322
|
+
trailId: id,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const webhookIssues = validateWebhookSource(activation.source);
|
|
327
|
+
for (const issue of webhookIssues) {
|
|
328
|
+
issues.push({
|
|
329
|
+
inputPath: [issue.field],
|
|
330
|
+
message: `Trail declares webhook source "${activation.source.id}" with invalid ${issue.field}: ${issue.message}`,
|
|
331
|
+
rule: 'activation-webhook-valid',
|
|
332
|
+
schemaIssues: [
|
|
333
|
+
{ code: issue.field, message: issue.message, path: [issue.field] },
|
|
334
|
+
],
|
|
335
|
+
sourceId: activation.source.id,
|
|
336
|
+
sourceKind: activation.source.kind,
|
|
337
|
+
trailId: id,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return issues;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const issuePathText = (path: readonly (string | number)[]): string =>
|
|
347
|
+
path.length > 0 ? path.join('.') : '<root>';
|
|
348
|
+
|
|
349
|
+
const createSourceCompatibilityIssue = (
|
|
350
|
+
trailId: string,
|
|
351
|
+
activation: ActivationEntry,
|
|
352
|
+
schemaIssues: readonly TopoSchemaIssue[]
|
|
353
|
+
): TopoIssue => {
|
|
354
|
+
const [firstIssue] = schemaIssues;
|
|
355
|
+
const inputPath = firstIssue?.path ?? Object.freeze([]);
|
|
356
|
+
return {
|
|
357
|
+
inputPath,
|
|
358
|
+
message: `Activation source "${activation.source.id}" (${activation.source.kind}) does not satisfy trail input at ${issuePathText(inputPath)}: ${firstIssue?.message ?? 'source payload is incompatible with trail input'}`,
|
|
359
|
+
rule: 'activation-source-input-compatible',
|
|
360
|
+
schemaIssues,
|
|
361
|
+
sourceId: activation.source.id,
|
|
362
|
+
sourceKind: activation.source.kind,
|
|
363
|
+
trailId,
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const checkSourcePayloadCompatibility = (
|
|
368
|
+
trail: AnyTrail,
|
|
369
|
+
activation: ActivationEntry,
|
|
370
|
+
signals: ReadonlyMap<string, AnySignal>
|
|
371
|
+
): TopoIssue | undefined => {
|
|
372
|
+
if (
|
|
373
|
+
!isKnownActivationSourceKind(activation.source.kind) ||
|
|
374
|
+
isDraftId(activation.source.id)
|
|
375
|
+
) {
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const schemaIssues = getActivationSourceInputCompatibilityIssues(
|
|
380
|
+
trail.input,
|
|
381
|
+
activation.source,
|
|
382
|
+
signals
|
|
383
|
+
);
|
|
384
|
+
if (!schemaIssues) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return schemaIssues.length > 0
|
|
389
|
+
? createSourceCompatibilityIssue(trail.id, activation, schemaIssues)
|
|
390
|
+
: undefined;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const checkActivationSourceInputCompatibility = (
|
|
394
|
+
trails: ReadonlyMap<string, AnyTrail>,
|
|
395
|
+
signals: ReadonlyMap<string, AnySignal>
|
|
396
|
+
): TopoIssue[] => {
|
|
397
|
+
const issues: TopoIssue[] = [];
|
|
398
|
+
|
|
399
|
+
for (const trail of trails.values()) {
|
|
400
|
+
for (const activation of trail.activationSources ?? []) {
|
|
401
|
+
const issue = checkSourcePayloadCompatibility(trail, activation, signals);
|
|
402
|
+
if (issue) {
|
|
403
|
+
issues.push(issue);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return issues;
|
|
409
|
+
};
|
|
410
|
+
|
|
208
411
|
const checkContourReferences = (
|
|
209
412
|
contours: ReadonlyMap<string, AnyContour>,
|
|
210
413
|
topo: Topo
|
|
@@ -233,8 +436,9 @@ const checkContourReferences = (
|
|
|
233
436
|
/**
|
|
234
437
|
* Validate the structural integrity of a Topo graph.
|
|
235
438
|
*
|
|
236
|
-
* Checks crossing references, example inputs,
|
|
237
|
-
* schema presence. Returns `Result.ok()` when no
|
|
439
|
+
* Checks crossing references, example inputs, signal origins, activation
|
|
440
|
+
* source kinds, and output schema presence. Returns `Result.ok()` when no
|
|
441
|
+
* issues are found, or
|
|
238
442
|
* `Result.err(ValidationError)` with all issues in the error context.
|
|
239
443
|
*/
|
|
240
444
|
export const validateTopo = (topo: Topo): Result<void, ValidationError> => {
|
|
@@ -244,6 +448,9 @@ export const validateTopo = (topo: Topo): Result<void, ValidationError> => {
|
|
|
244
448
|
...checkContourReferences(topo.contours, topo),
|
|
245
449
|
...checkExamples(topo.trails),
|
|
246
450
|
...checkSignalOrigins(topo.signals, topo),
|
|
451
|
+
...checkSignalReferences(topo.trails, topo.signals),
|
|
452
|
+
...checkActivationSources(topo.trails),
|
|
453
|
+
...checkActivationSourceInputCompatibility(topo.trails, topo.signals),
|
|
247
454
|
];
|
|
248
455
|
|
|
249
456
|
if (issues.length === 0) {
|
package/src/validation.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { z } from 'zod';
|
|
9
9
|
|
|
10
|
+
import { BLOB_REF_SCHEMA_META_KEY, blobRefJsonSchema } from './blob-ref.js';
|
|
10
11
|
import { ValidationError } from './errors.js';
|
|
11
12
|
import { Result } from './result.js';
|
|
12
13
|
|
|
@@ -31,10 +32,57 @@ type JsonSchemaConverter = (schema: z.ZodType) => JsonSchema;
|
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
32
33
|
|
|
33
34
|
const isOptionalLike = (s: ZodInternals): boolean => {
|
|
34
|
-
|
|
35
|
+
let current = s;
|
|
36
|
+
const seen = new Set<ZodInternals>();
|
|
37
|
+
while (
|
|
38
|
+
current._zod.def['type'] === 'readonly' &&
|
|
39
|
+
!seen.has(current) &&
|
|
40
|
+
current._zod.def['innerType'] !== undefined
|
|
41
|
+
) {
|
|
42
|
+
seen.add(current);
|
|
43
|
+
current = current._zod.def['innerType'] as ZodInternals;
|
|
44
|
+
}
|
|
45
|
+
const defType = current._zod.def['type'] as string;
|
|
35
46
|
return defType === 'optional' || defType === 'default';
|
|
36
47
|
};
|
|
37
48
|
|
|
49
|
+
const getSchemaMeta = (
|
|
50
|
+
schema: z.ZodType
|
|
51
|
+
): Readonly<Record<string, unknown>> | undefined => {
|
|
52
|
+
const maybeMeta = (schema as unknown as { meta?: () => unknown }).meta;
|
|
53
|
+
if (typeof maybeMeta !== 'function') {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const meta = maybeMeta.call(schema);
|
|
57
|
+
return typeof meta === 'object' && meta !== null
|
|
58
|
+
? (meta as Readonly<Record<string, unknown>>)
|
|
59
|
+
: undefined;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getSchemaJsonSchemaOverride = (
|
|
63
|
+
schema: z.ZodType
|
|
64
|
+
): JsonSchema | undefined => {
|
|
65
|
+
const meta = getSchemaMeta(schema);
|
|
66
|
+
if (meta?.[BLOB_REF_SCHEMA_META_KEY] === true) {
|
|
67
|
+
const override: JsonSchema = {
|
|
68
|
+
properties: Object.fromEntries(
|
|
69
|
+
Object.entries(blobRefJsonSchema.properties).map(([key, value]) => [
|
|
70
|
+
key,
|
|
71
|
+
{ ...value },
|
|
72
|
+
])
|
|
73
|
+
),
|
|
74
|
+
required: [...blobRefJsonSchema.required],
|
|
75
|
+
type: blobRefJsonSchema.type,
|
|
76
|
+
};
|
|
77
|
+
const { description } = schema as unknown as ZodInternals;
|
|
78
|
+
if (description) {
|
|
79
|
+
override['description'] = description;
|
|
80
|
+
}
|
|
81
|
+
return override;
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
|
|
38
86
|
// ---------------------------------------------------------------------------
|
|
39
87
|
// Issue formatting
|
|
40
88
|
// ---------------------------------------------------------------------------
|
|
@@ -132,6 +180,11 @@ const resolveDefault = (def: Record<string, unknown>): unknown => {
|
|
|
132
180
|
export const zodToJsonSchema: JsonSchemaConverter = (
|
|
133
181
|
schema: z.ZodType
|
|
134
182
|
): JsonSchema => {
|
|
183
|
+
const jsonSchemaOverride = getSchemaJsonSchemaOverride(schema);
|
|
184
|
+
if (jsonSchemaOverride !== undefined) {
|
|
185
|
+
return jsonSchemaOverride;
|
|
186
|
+
}
|
|
187
|
+
|
|
135
188
|
const s = schema as unknown as ZodInternals;
|
|
136
189
|
|
|
137
190
|
const collectObjectFields = (shape: Record<string, ZodInternals>) => {
|
|
@@ -197,6 +250,10 @@ export const zodToJsonSchema: JsonSchemaConverter = (
|
|
|
197
250
|
const inner = value._zod.def['innerType'] as unknown as z.ZodType;
|
|
198
251
|
return zodToJsonSchema(inner);
|
|
199
252
|
},
|
|
253
|
+
readonly: (value) => {
|
|
254
|
+
const inner = value._zod.def['innerType'] as unknown as z.ZodType;
|
|
255
|
+
return zodToJsonSchema(inner);
|
|
256
|
+
},
|
|
200
257
|
string: () => ({ type: 'string' }),
|
|
201
258
|
union: (value) => {
|
|
202
259
|
const options = value._zod.def['options'] as unknown as z.ZodType[];
|
package/src/webhook.ts
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActivationSource,
|
|
3
|
+
ActivationSourceMeta,
|
|
4
|
+
ActivationSourceParse,
|
|
5
|
+
} from './activation-source.js';
|
|
6
|
+
import { InternalError, ValidationError } from './errors.js';
|
|
7
|
+
import { Result } from './result.js';
|
|
8
|
+
|
|
9
|
+
export const webhookMethods = Object.freeze([
|
|
10
|
+
'DELETE',
|
|
11
|
+
'GET',
|
|
12
|
+
'PATCH',
|
|
13
|
+
'POST',
|
|
14
|
+
'PUT',
|
|
15
|
+
] as const);
|
|
16
|
+
|
|
17
|
+
export type WebhookMethod = (typeof webhookMethods)[number];
|
|
18
|
+
export type WebhookMethodInput = WebhookMethod | Lowercase<WebhookMethod>;
|
|
19
|
+
|
|
20
|
+
export type WebhookVerifyHeaders = Readonly<
|
|
21
|
+
Record<string, readonly string[] | string | undefined>
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
export interface WebhookVerifyRequest {
|
|
25
|
+
readonly body: ArrayBuffer | Uint8Array | string;
|
|
26
|
+
readonly headers: WebhookVerifyHeaders;
|
|
27
|
+
readonly method: string;
|
|
28
|
+
readonly path: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type WebhookVerify = (
|
|
32
|
+
request: WebhookVerifyRequest
|
|
33
|
+
) => Promise<Result<void, Error>> | Result<void, Error>;
|
|
34
|
+
|
|
35
|
+
export interface WebhookSpec<TOutput = unknown> {
|
|
36
|
+
readonly meta?: ActivationSourceMeta | undefined;
|
|
37
|
+
readonly method?: WebhookMethodInput | undefined;
|
|
38
|
+
readonly parse: ActivationSourceParse<TOutput>;
|
|
39
|
+
readonly path: string;
|
|
40
|
+
readonly payload?: ActivationSource['payload'] | undefined;
|
|
41
|
+
readonly verify?: WebhookVerify | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface WebhookSource<TOutput = unknown> extends ActivationSource {
|
|
45
|
+
readonly kind: 'webhook';
|
|
46
|
+
readonly meta?: ActivationSourceMeta | undefined;
|
|
47
|
+
readonly method: WebhookMethod;
|
|
48
|
+
readonly parse: ActivationSourceParse<TOutput>;
|
|
49
|
+
readonly path: string;
|
|
50
|
+
readonly payload?: ActivationSource['payload'] | undefined;
|
|
51
|
+
readonly verify?: WebhookVerify | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface WebhookValidationIssue {
|
|
55
|
+
readonly field: 'method' | 'parse' | 'path' | 'verify';
|
|
56
|
+
readonly message: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_WEBHOOK_METHOD = 'POST' as const;
|
|
60
|
+
|
|
61
|
+
const normalizeMethod = (
|
|
62
|
+
method: WebhookMethodInput | string | undefined
|
|
63
|
+
): string => (method ?? DEFAULT_WEBHOOK_METHOD).trim().toUpperCase();
|
|
64
|
+
|
|
65
|
+
const normalizePath = (path: string): string => path.trim();
|
|
66
|
+
|
|
67
|
+
const isWebhookMethod = (method: string): method is WebhookMethod =>
|
|
68
|
+
(webhookMethods as readonly string[]).includes(method);
|
|
69
|
+
|
|
70
|
+
const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
|
|
71
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
72
|
+
|
|
73
|
+
const isZodSchema = (value: unknown): boolean =>
|
|
74
|
+
isObjectRecord(value) && typeof value['safeParse'] === 'function';
|
|
75
|
+
|
|
76
|
+
const validateMethod = (
|
|
77
|
+
method: WebhookMethodInput | string | undefined
|
|
78
|
+
): WebhookValidationIssue[] => {
|
|
79
|
+
const normalized = normalizeMethod(method);
|
|
80
|
+
return isWebhookMethod(normalized)
|
|
81
|
+
? []
|
|
82
|
+
: [
|
|
83
|
+
{
|
|
84
|
+
field: 'method',
|
|
85
|
+
message: `Webhook method must be one of ${webhookMethods.join(', ')}`,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const validatePath = (path: unknown): WebhookValidationIssue[] => {
|
|
91
|
+
if (typeof path !== 'string' || path.trim().length === 0) {
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
field: 'path',
|
|
95
|
+
message: 'Webhook path must be a non-empty absolute path',
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const normalized = normalizePath(path);
|
|
101
|
+
return normalized.startsWith('/')
|
|
102
|
+
? []
|
|
103
|
+
: [
|
|
104
|
+
{
|
|
105
|
+
field: 'path',
|
|
106
|
+
message: 'Webhook path must start with "/"',
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const validateRequiredParse = (parse: unknown): WebhookValidationIssue[] =>
|
|
112
|
+
parse === undefined
|
|
113
|
+
? [
|
|
114
|
+
{
|
|
115
|
+
field: 'parse',
|
|
116
|
+
message: 'Webhook sources must define parse',
|
|
117
|
+
},
|
|
118
|
+
]
|
|
119
|
+
: [];
|
|
120
|
+
|
|
121
|
+
const validateParseShape = (parse: unknown): WebhookValidationIssue[] => {
|
|
122
|
+
if (parse === undefined || isZodSchema(parse)) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (isObjectRecord(parse) && isZodSchema(parse['output'])) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
field: 'parse',
|
|
131
|
+
message: 'Webhook parse must be a Zod schema or define parse.output',
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const validateVerify = (verify: unknown): WebhookValidationIssue[] =>
|
|
137
|
+
verify === undefined || typeof verify === 'function'
|
|
138
|
+
? []
|
|
139
|
+
: [
|
|
140
|
+
{
|
|
141
|
+
field: 'verify',
|
|
142
|
+
message: 'Webhook verify must be a function when provided',
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
const webhookIssuesMessage = (
|
|
147
|
+
id: string,
|
|
148
|
+
issues: readonly WebhookValidationIssue[]
|
|
149
|
+
): string =>
|
|
150
|
+
`webhook("${id}") is invalid: ${issues.map((issue) => `${issue.field}: ${issue.message}`).join('; ')}`;
|
|
151
|
+
|
|
152
|
+
const assertWebhookSpec = <TOutput>(
|
|
153
|
+
id: string,
|
|
154
|
+
spec: WebhookSpec<TOutput>
|
|
155
|
+
): {
|
|
156
|
+
readonly method: WebhookMethod;
|
|
157
|
+
readonly path: string;
|
|
158
|
+
} => {
|
|
159
|
+
const issues = [
|
|
160
|
+
...validateMethod(spec.method),
|
|
161
|
+
...validatePath(spec.path),
|
|
162
|
+
...validateRequiredParse(spec.parse),
|
|
163
|
+
...validateParseShape(spec.parse),
|
|
164
|
+
...validateVerify(spec.verify),
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
if (issues.length > 0) {
|
|
168
|
+
throw new ValidationError(webhookIssuesMessage(id, issues), {
|
|
169
|
+
context: { issues },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
method: normalizeMethod(spec.method) as WebhookMethod,
|
|
175
|
+
path: normalizePath(spec.path),
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const validateWebhookSource = (
|
|
180
|
+
source: ActivationSource
|
|
181
|
+
): readonly WebhookValidationIssue[] => {
|
|
182
|
+
if (source.kind !== 'webhook') {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return [
|
|
187
|
+
...validateMethod(source.method),
|
|
188
|
+
...validatePath(source.path),
|
|
189
|
+
...validateRequiredParse(source.parse),
|
|
190
|
+
...validateParseShape(source.parse),
|
|
191
|
+
...validateVerify(source.verify),
|
|
192
|
+
];
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const errorFromUnknown = (error: unknown): Error =>
|
|
196
|
+
error instanceof Error ? error : new Error(String(error));
|
|
197
|
+
|
|
198
|
+
export const getWebhookHeaders = (
|
|
199
|
+
request: Pick<WebhookVerifyRequest, 'headers'>,
|
|
200
|
+
name: string
|
|
201
|
+
): readonly string[] => {
|
|
202
|
+
const normalized = name.toLowerCase();
|
|
203
|
+
const matches: string[] = [];
|
|
204
|
+
for (const [headerName, value] of Object.entries(request.headers)) {
|
|
205
|
+
if (headerName.toLowerCase() !== normalized) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (value === undefined) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === 'string') {
|
|
212
|
+
matches.push(value);
|
|
213
|
+
} else {
|
|
214
|
+
matches.push(...value);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return matches;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const getWebhookHeader = (
|
|
221
|
+
request: Pick<WebhookVerifyRequest, 'headers'>,
|
|
222
|
+
name: string
|
|
223
|
+
): string | undefined => {
|
|
224
|
+
const [first] = getWebhookHeaders(request, name);
|
|
225
|
+
return first;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export const verifyWebhookRequest = async (
|
|
229
|
+
source: Pick<WebhookSource, 'id' | 'verify'>,
|
|
230
|
+
request: WebhookVerifyRequest
|
|
231
|
+
): Promise<Result<void, Error>> => {
|
|
232
|
+
if (source.verify === undefined) {
|
|
233
|
+
return Result.ok();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
return await source.verify(request);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
const cause = errorFromUnknown(error);
|
|
240
|
+
return Result.err(
|
|
241
|
+
new InternalError(`Webhook source "${source.id}" verification threw`, {
|
|
242
|
+
cause,
|
|
243
|
+
})
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export function webhook<TOutput>(
|
|
249
|
+
id: string,
|
|
250
|
+
spec: WebhookSpec<TOutput>
|
|
251
|
+
): WebhookSource<TOutput>;
|
|
252
|
+
export function webhook<TOutput>(
|
|
253
|
+
spec: WebhookSpec<TOutput> & { readonly id: string }
|
|
254
|
+
): WebhookSource<TOutput>;
|
|
255
|
+
export function webhook<TOutput>(
|
|
256
|
+
idOrSpec: string | (WebhookSpec<TOutput> & { readonly id: string }),
|
|
257
|
+
maybeSpec?: WebhookSpec<TOutput>
|
|
258
|
+
): WebhookSource<TOutput> {
|
|
259
|
+
const id = typeof idOrSpec === 'string' ? idOrSpec : idOrSpec.id;
|
|
260
|
+
// oxlint-disable-next-line no-non-null-assertion -- overload guarantees maybeSpec when idOrSpec is string
|
|
261
|
+
const spec = typeof idOrSpec === 'string' ? maybeSpec! : idOrSpec;
|
|
262
|
+
const normalized = assertWebhookSpec(id, spec);
|
|
263
|
+
|
|
264
|
+
return Object.freeze({
|
|
265
|
+
id,
|
|
266
|
+
kind: 'webhook' as const,
|
|
267
|
+
method: normalized.method,
|
|
268
|
+
parse: spec.parse,
|
|
269
|
+
path: normalized.path,
|
|
270
|
+
...(spec.meta === undefined
|
|
271
|
+
? {}
|
|
272
|
+
: { meta: Object.freeze({ ...spec.meta }) }),
|
|
273
|
+
...(spec.payload === undefined ? {} : { payload: spec.payload }),
|
|
274
|
+
...(spec.verify === undefined ? {} : { verify: spec.verify }),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
* would be redundant at best and would produce an `OptionalOptional<T>` shape
|
|
16
16
|
* in edge cases at worst. `nullable` wrappers are preserved because nullability
|
|
17
17
|
* is a semantic constraint that `.partial()` does not reintroduce.
|
|
18
|
-
*
|
|
19
|
-
* @internal
|
|
20
18
|
*/
|
|
21
19
|
|
|
22
20
|
import type { z } from 'zod';
|
|
@@ -35,8 +33,6 @@ const readInnerType = (schema: z.ZodType): z.ZodType =>
|
|
|
35
33
|
* (`default`, `optional`, `nullable`), drops defaults, drops `optional` (the
|
|
36
34
|
* downstream `.partial()` reintroduces it across the whole shape), and
|
|
37
35
|
* preserves `nullable` so explicit nullability survives.
|
|
38
|
-
*
|
|
39
|
-
* @internal
|
|
40
36
|
*/
|
|
41
37
|
export const stripDefaultWrappers = (schema: z.ZodType): z.ZodType => {
|
|
42
38
|
let current = schema;
|
|
@@ -61,8 +57,6 @@ const asObjectSchema = (schema: z.ZodType): AnyObjectSchema =>
|
|
|
61
57
|
* Apply {@link stripDefaultWrappers} to every field in a Zod object shape and
|
|
62
58
|
* return the resulting shape record. Callers typically feed the result to
|
|
63
59
|
* `.extend()` + `.partial()`.
|
|
64
|
-
*
|
|
65
|
-
* @internal
|
|
66
60
|
*/
|
|
67
61
|
export const stripDefaultsFromShape = (
|
|
68
62
|
schema: z.ZodType
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc -b
|
package/.turbo/turbo-lint.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc --noEmit
|
package/dist/blob-ref.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BlobRef — a frozen reference to binary data for @ontrails/core.
|
|
3
|
-
*/
|
|
4
|
-
/** Immutable reference to a blob of binary data. */
|
|
5
|
-
export interface BlobRef {
|
|
6
|
-
readonly name: string;
|
|
7
|
-
readonly mimeType: string;
|
|
8
|
-
readonly size: number;
|
|
9
|
-
readonly data: Uint8Array | ReadableStream<Uint8Array>;
|
|
10
|
-
}
|
|
11
|
-
/** Creates a frozen BlobRef. */
|
|
12
|
-
export declare const createBlobRef: (options: {
|
|
13
|
-
name: string;
|
|
14
|
-
mimeType: string;
|
|
15
|
-
size: number;
|
|
16
|
-
data: Uint8Array | ReadableStream<Uint8Array>;
|
|
17
|
-
}) => BlobRef;
|
|
18
|
-
/** Type guard for BlobRef-shaped values. */
|
|
19
|
-
export declare const isBlobRef: (value: unknown) => value is BlobRef;
|
|
20
|
-
//# sourceMappingURL=blob-ref.d.ts.map
|
package/dist/blob-ref.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"blob-ref.d.ts","sourceRoot":"","sources":["../src/blob-ref.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oDAAoD;AACpD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;CACxD;AAED,gCAAgC;AAChC,eAAO,MAAM,aAAa,GAAI,SAAS;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;CAC/C,KAAG,OAMA,CAAC;AAEL,4CAA4C;AAC5C,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAWnD,CAAC"}
|