@voyantjs/distribution 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/channel-push/admin-routes.d.ts +31 -0
  2. package/dist/channel-push/admin-routes.d.ts.map +1 -0
  3. package/dist/channel-push/admin-routes.js +165 -0
  4. package/dist/channel-push/availability-push.d.ts +76 -0
  5. package/dist/channel-push/availability-push.d.ts.map +1 -0
  6. package/dist/channel-push/availability-push.js +238 -0
  7. package/dist/channel-push/booking-push.d.ts +114 -0
  8. package/dist/channel-push/booking-push.d.ts.map +1 -0
  9. package/dist/channel-push/booking-push.js +503 -0
  10. package/dist/channel-push/content-push.d.ts +60 -0
  11. package/dist/channel-push/content-push.d.ts.map +1 -0
  12. package/dist/channel-push/content-push.js +256 -0
  13. package/dist/channel-push/index.d.ts +15 -0
  14. package/dist/channel-push/index.d.ts.map +1 -0
  15. package/dist/channel-push/index.js +18 -0
  16. package/dist/channel-push/plugin.d.ts +18 -0
  17. package/dist/channel-push/plugin.d.ts.map +1 -0
  18. package/dist/channel-push/plugin.js +21 -0
  19. package/dist/channel-push/reconciler.d.ts +85 -0
  20. package/dist/channel-push/reconciler.d.ts.map +1 -0
  21. package/dist/channel-push/reconciler.js +175 -0
  22. package/dist/channel-push/subscriber.d.ts +40 -0
  23. package/dist/channel-push/subscriber.d.ts.map +1 -0
  24. package/dist/channel-push/subscriber.js +174 -0
  25. package/dist/channel-push/types.d.ts +43 -0
  26. package/dist/channel-push/types.d.ts.map +1 -0
  27. package/dist/channel-push/types.js +32 -0
  28. package/dist/channel-push/workflows.d.ts +56 -0
  29. package/dist/channel-push/workflows.d.ts.map +1 -0
  30. package/dist/channel-push/workflows.js +100 -0
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -0
  34. package/dist/rate-limit.d.ts +69 -0
  35. package/dist/rate-limit.d.ts.map +1 -0
  36. package/dist/rate-limit.js +135 -0
  37. package/dist/routes.d.ts +170 -10
  38. package/dist/routes.d.ts.map +1 -1
  39. package/dist/schema-core.d.ts +417 -1
  40. package/dist/schema-core.d.ts.map +1 -1
  41. package/dist/schema-core.js +98 -1
  42. package/dist/schema-push-intents.d.ts +387 -0
  43. package/dist/schema-push-intents.d.ts.map +1 -0
  44. package/dist/schema-push-intents.js +77 -0
  45. package/dist/schema.d.ts +1 -0
  46. package/dist/schema.d.ts.map +1 -1
  47. package/dist/schema.js +1 -0
  48. package/dist/service.d.ts +103 -7
  49. package/dist/service.d.ts.map +1 -1
  50. package/dist/validation.d.ts +5 -5
  51. package/dist/webhook-deliveries.d.ts +86 -0
  52. package/dist/webhook-deliveries.d.ts.map +1 -0
  53. package/dist/webhook-deliveries.js +293 -0
  54. package/package.json +16 -8
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Channel-push EventBus subscribers.
3
+ *
4
+ * The `booking.confirmed` subscriber writes durable intent rows to
5
+ * `channel_booking_links` and returns immediately — per §4.5 the
6
+ * subscriber MUST NOT do HTTP work because the EventBus is in-process
7
+ * and sequential. The intent rows are drained by `processBookingPush`
8
+ * (called inline in v1, or by the durable workflow in production).
9
+ *
10
+ * Per docs/architecture/channel-push-architecture.md §4.1.
11
+ */
12
+ import { processAvailabilityPushIntents, resolveAllotmentTargetsForSlot, upsertAvailabilityIntent, } from "./availability-push.js";
13
+ import { processBookingPush, resolveBookingPushTargets, upsertPendingBookingLinks, } from "./booking-push.js";
14
+ import { processContentPushIntents, resolveContentPushTargets, upsertContentIntent, } from "./content-push.js";
15
+ import { defaultLogger, getChannelPushDeps, getChannelPushDepsOrThrow, } from "./types.js";
16
+ function coerceBookingConfirmed(envelope) {
17
+ const data = envelope.data;
18
+ if (data == null || typeof data !== "object")
19
+ return null;
20
+ const maybe = data;
21
+ if (typeof maybe.bookingId !== "string")
22
+ return null;
23
+ return maybe;
24
+ }
25
+ function coerceSlotChanged(envelope) {
26
+ const data = envelope.data;
27
+ if (data == null || typeof data !== "object")
28
+ return null;
29
+ const maybe = data;
30
+ if (typeof maybe.slotId !== "string" || typeof maybe.productId !== "string")
31
+ return null;
32
+ return maybe;
33
+ }
34
+ function coerceContentChanged(envelope) {
35
+ const data = envelope.data;
36
+ if (data == null || typeof data !== "object")
37
+ return null;
38
+ const maybe = data;
39
+ if (typeof maybe.id !== "string")
40
+ return null;
41
+ return maybe;
42
+ }
43
+ /**
44
+ * Construct the channel-push subscriber bundle. Templates pass these to
45
+ * `registerPlugins({ subscribers })` or attach them to an EventBus
46
+ * directly via `eventBus.subscribe`.
47
+ */
48
+ export function createChannelPushSubscribers(options = {}) {
49
+ const drainInline = options.drainInline ?? true;
50
+ const handler = async (envelope) => {
51
+ const payload = coerceBookingConfirmed(envelope);
52
+ if (!payload)
53
+ return;
54
+ const deps = options.deps ?? getChannelPushDeps();
55
+ if (!deps) {
56
+ // Templates haven't wired channel-push — silent no-op so the rest
57
+ // of the booking flow isn't impacted by missing wiring.
58
+ return;
59
+ }
60
+ const logger = deps.logger ?? defaultLogger;
61
+ try {
62
+ const targets = await resolveBookingPushTargets(deps.db, payload.bookingId);
63
+ if (targets.length === 0)
64
+ return;
65
+ await upsertPendingBookingLinks(deps.db, payload.bookingId, targets);
66
+ if (drainInline) {
67
+ // Inline drain — simple deployments without the workflow runtime
68
+ // wired (dev templates, demo mode). Production uses the workflow
69
+ // and skips this branch.
70
+ await processBookingPush({ bookingId: payload.bookingId }, deps);
71
+ }
72
+ }
73
+ catch (err) {
74
+ logger.error("[channel-push] booking.confirmed subscriber failed", {
75
+ bookingId: payload.bookingId,
76
+ error: err instanceof Error ? err.message : String(err),
77
+ });
78
+ }
79
+ };
80
+ const slotHandler = async (envelope) => {
81
+ const payload = coerceSlotChanged(envelope);
82
+ if (!payload)
83
+ return;
84
+ const deps = options.deps ?? getChannelPushDeps();
85
+ if (!deps)
86
+ return;
87
+ const logger = deps.logger ?? defaultLogger;
88
+ try {
89
+ const targets = await resolveAllotmentTargetsForSlot(deps.db, {
90
+ slotId: payload.slotId,
91
+ productId: payload.productId,
92
+ optionId: payload.optionId,
93
+ });
94
+ if (targets.length === 0)
95
+ return;
96
+ const startsAt = payload.startsAt instanceof Date ? payload.startsAt : new Date(payload.startsAt);
97
+ // One intent per (channel, slot) pair — supersession collapses
98
+ // concurrent events.
99
+ for (const target of targets) {
100
+ await upsertAvailabilityIntent(deps.db, {
101
+ channelId: target.channelId,
102
+ sourceConnectionId: target.sourceConnectionId,
103
+ slotId: payload.slotId,
104
+ productId: payload.productId,
105
+ optionId: payload.optionId,
106
+ startsAt,
107
+ });
108
+ }
109
+ if (drainInline) {
110
+ // Drain only this channel's intents to keep latency bounded.
111
+ // (v1 dev behavior; production runs the scheduled workflow.)
112
+ for (const target of targets) {
113
+ await processAvailabilityPushIntents({ channelId: target.channelId, limit: 50 }, deps);
114
+ }
115
+ }
116
+ }
117
+ catch (err) {
118
+ logger.error("[channel-push] availability.slot.changed subscriber failed", {
119
+ slotId: payload.slotId,
120
+ error: err instanceof Error ? err.message : String(err),
121
+ });
122
+ }
123
+ };
124
+ const contentHandler = async (envelope) => {
125
+ const payload = coerceContentChanged(envelope);
126
+ if (!payload)
127
+ return;
128
+ const deps = options.deps ?? getChannelPushDeps();
129
+ if (!deps)
130
+ return;
131
+ const logger = deps.logger ?? defaultLogger;
132
+ try {
133
+ const targets = await resolveContentPushTargets(deps.db, payload.id);
134
+ if (targets.length === 0)
135
+ return;
136
+ for (const target of targets) {
137
+ await upsertContentIntent(deps.db, {
138
+ channelId: target.channelId,
139
+ sourceConnectionId: target.sourceConnectionId,
140
+ productId: payload.id,
141
+ });
142
+ }
143
+ if (drainInline) {
144
+ for (const target of targets) {
145
+ await processContentPushIntents({ channelId: target.channelId, limit: 50 }, deps);
146
+ }
147
+ }
148
+ }
149
+ catch (err) {
150
+ logger.error("[channel-push] product.content.changed subscriber failed", {
151
+ productId: payload.id,
152
+ error: err instanceof Error ? err.message : String(err),
153
+ });
154
+ }
155
+ };
156
+ return [
157
+ { event: "booking.confirmed", handler },
158
+ { event: "availability.slot.changed", handler: slotHandler },
159
+ { event: "product.content.changed", handler: contentHandler },
160
+ ];
161
+ }
162
+ /**
163
+ * Trigger the booking-push pipeline for an arbitrary booking id —
164
+ * useful for the operator dashboard's "retry sync" button and for the
165
+ * reconciler (Phase G).
166
+ */
167
+ export async function triggerBookingPushForBooking(bookingId) {
168
+ const deps = getChannelPushDepsOrThrow();
169
+ const targets = await resolveBookingPushTargets(deps.db, bookingId);
170
+ if (targets.length === 0)
171
+ return;
172
+ await upsertPendingBookingLinks(deps.db, bookingId, targets);
173
+ await processBookingPush({ bookingId }, deps);
174
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared types for the channel-push pipeline.
3
+ *
4
+ * Per docs/architecture/channel-push-architecture.md §4-§6.
5
+ */
6
+ import type { SourceAdapterRegistry } from "@voyantjs/catalog/booking-engine";
7
+ import type { AnyDrizzleDb } from "@voyantjs/db";
8
+ /**
9
+ * Process-local dependencies the channel-push workers and subscribers
10
+ * need. Templates wire this once at startup via `setChannelPushDeps`;
11
+ * callers retrieve via `getChannelPushDeps`.
12
+ *
13
+ * The runtime indirection lets workflows (which can't take closures in
14
+ * their durable input) and EventBus subscribers (which run in the
15
+ * emitter's process) share the same wiring without each consumer
16
+ * re-resolving services. Per §4.5 — subscribers stay write-only and
17
+ * delegate HTTP work to the workflow, which reaches into these deps.
18
+ */
19
+ export interface ChannelPushDeps {
20
+ db: AnyDrizzleDb;
21
+ registry: SourceAdapterRegistry;
22
+ /**
23
+ * Optional logger. Defaults to a console fallback. Subscribers and
24
+ * workers log to this when they swallow errors per the EventBus
25
+ * fire-and-forget contract.
26
+ */
27
+ logger?: ChannelPushLogger;
28
+ }
29
+ export interface ChannelPushLogger {
30
+ info?: (message: string, meta?: Record<string, unknown>) => void;
31
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
32
+ error: (message: string, meta?: Record<string, unknown>) => void;
33
+ }
34
+ /** Wire the channel-push pipeline at process start. Idempotent. */
35
+ export declare function setChannelPushDeps(deps: ChannelPushDeps): void;
36
+ /** Retrieve the wired deps, throwing if templates haven't wired them. */
37
+ export declare function getChannelPushDepsOrThrow(): ChannelPushDeps;
38
+ /** Retrieve the wired deps, returning undefined when none are wired. */
39
+ export declare function getChannelPushDeps(): ChannelPushDeps | undefined;
40
+ /** Reset (test helper). */
41
+ export declare function clearChannelPushDeps(): void;
42
+ export declare const defaultLogger: ChannelPushLogger;
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/channel-push/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,YAAY,CAAA;IAChB,QAAQ,EAAE,qBAAqB,CAAA;IAC/B;;;;OAIG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAChE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACjE;AAUD,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAE9D;AAED,yEAAyE;AACzE,wBAAgB,yBAAyB,IAAI,eAAe,CAQ3D;AAED,wEAAwE;AACxE,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAEhE;AAED,2BAA2B;AAC3B,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED,eAAO,MAAM,aAAa,EAAE,iBAI3B,CAAA"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shared types for the channel-push pipeline.
3
+ *
4
+ * Per docs/architecture/channel-push-architecture.md §4-§6.
5
+ */
6
+ const DEPS_KEY = Symbol.for("voyant.distribution.channel-push.deps");
7
+ const globalRef = globalThis;
8
+ /** Wire the channel-push pipeline at process start. Idempotent. */
9
+ export function setChannelPushDeps(deps) {
10
+ globalRef[DEPS_KEY] = deps;
11
+ }
12
+ /** Retrieve the wired deps, throwing if templates haven't wired them. */
13
+ export function getChannelPushDepsOrThrow() {
14
+ const deps = globalRef[DEPS_KEY];
15
+ if (!deps) {
16
+ throw new Error("channel-push deps not wired — call setChannelPushDeps({ db, registry }) at process start");
17
+ }
18
+ return deps;
19
+ }
20
+ /** Retrieve the wired deps, returning undefined when none are wired. */
21
+ export function getChannelPushDeps() {
22
+ return globalRef[DEPS_KEY];
23
+ }
24
+ /** Reset (test helper). */
25
+ export function clearChannelPushDeps() {
26
+ delete globalRef[DEPS_KEY];
27
+ }
28
+ export const defaultLogger = {
29
+ info: (message, meta) => console.log(`[channel-push] ${message}`, meta ?? ""),
30
+ warn: (message, meta) => console.warn(`[channel-push] ${message}`, meta ?? ""),
31
+ error: (message, meta) => console.error(`[channel-push] ${message}`, meta ?? ""),
32
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Durable channel-push workflows.
3
+ *
4
+ * Wraps the inline-callable processors (`processBookingPush`,
5
+ * `processAvailabilityPushIntents`, `processContentPushIntents`) in
6
+ * `@voyantjs/workflows` definitions so retries, sleeps, and resumption
7
+ * survive worker restarts. Importing this module registers the
8
+ * workflows in the global registry — Voyant Cloud orchestrator picks
9
+ * them up automatically.
10
+ *
11
+ * The workflow bodies look up `ChannelPushDeps` from the process-local
12
+ * holder set via `setChannelPushDeps`. Hosts wire deps at bootstrap;
13
+ * the orchestrator's Node container reads from the same global since
14
+ * it runs in the same isolate that loaded the user bundle.
15
+ *
16
+ * Dev / single-process deployments (e.g. the operator template's
17
+ * inline drain) don't need to register these — the subscriber calls the
18
+ * processors directly. Production deployments with the Voyant Cloud
19
+ * orchestrator wired import this module to opt into durability.
20
+ *
21
+ * Per docs/architecture/channel-push-architecture.md §4.2 + §12.
22
+ */
23
+ import { type ProcessAvailabilityPushInput, type ProcessAvailabilityPushResult } from "./availability-push.js";
24
+ import { type ProcessBookingPushInput, type ProcessBookingPushResult } from "./booking-push.js";
25
+ import { type ProcessContentPushInput, type ProcessContentPushResult } from "./content-push.js";
26
+ /**
27
+ * Per-booking saga workflow with compensation support.
28
+ *
29
+ * Concurrency is keyed by `bookingId` so two confirms of the same
30
+ * booking serialize (which can't actually happen given the booking
31
+ * state machine, but the perKey lock is cheap insurance). Retries on
32
+ * exponential backoff up to 5 attempts; the per-link compensation pass
33
+ * inside `processBookingPush` handles strict-atomic policy when
34
+ * `channel_contracts.policy.compensation = "strict-atomic"`.
35
+ *
36
+ * Per §4.2 + §12.1.
37
+ */
38
+ export declare const channelBookingPushWorkflow: import("@voyantjs/workflows").WorkflowDefinition<ProcessBookingPushInput, ProcessBookingPushResult>;
39
+ /**
40
+ * Scheduled batch worker for availability push. Runs every 30 seconds
41
+ * (tunable per channel via policy in a future iteration); each tick
42
+ * drains up to 100 pending intents, capped at one concurrent run per
43
+ * channel. Idempotency is upstream-side via `(slot_id, remaining_pax)`.
44
+ *
45
+ * Per §5.3 + §12.2.
46
+ */
47
+ export declare const channelAvailabilityPushWorkflow: import("@voyantjs/workflows").WorkflowDefinition<ProcessAvailabilityPushInput, ProcessAvailabilityPushResult>;
48
+ /**
49
+ * Scheduled batch worker for content push. Longer cadence (5m) since
50
+ * content drift is rarely time-critical; idempotency via the upstream's
51
+ * acknowledged-hash skip in `processContentPushIntents`.
52
+ *
53
+ * Per §6 + §12.3.
54
+ */
55
+ export declare const channelContentPushWorkflow: import("@voyantjs/workflows").WorkflowDefinition<ProcessContentPushInput, ProcessContentPushResult>;
56
+ //# sourceMappingURL=workflows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/channel-push/workflows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAEnC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAE9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAE9B,MAAM,mBAAmB,CAAA;AAE1B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,qGAiBrC,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,+GAkB1C,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,qGAkBrC,CAAA"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Durable channel-push workflows.
3
+ *
4
+ * Wraps the inline-callable processors (`processBookingPush`,
5
+ * `processAvailabilityPushIntents`, `processContentPushIntents`) in
6
+ * `@voyantjs/workflows` definitions so retries, sleeps, and resumption
7
+ * survive worker restarts. Importing this module registers the
8
+ * workflows in the global registry — Voyant Cloud orchestrator picks
9
+ * them up automatically.
10
+ *
11
+ * The workflow bodies look up `ChannelPushDeps` from the process-local
12
+ * holder set via `setChannelPushDeps`. Hosts wire deps at bootstrap;
13
+ * the orchestrator's Node container reads from the same global since
14
+ * it runs in the same isolate that loaded the user bundle.
15
+ *
16
+ * Dev / single-process deployments (e.g. the operator template's
17
+ * inline drain) don't need to register these — the subscriber calls the
18
+ * processors directly. Production deployments with the Voyant Cloud
19
+ * orchestrator wired import this module to opt into durability.
20
+ *
21
+ * Per docs/architecture/channel-push-architecture.md §4.2 + §12.
22
+ */
23
+ import { workflow } from "@voyantjs/workflows";
24
+ import { CHANNEL_AVAILABILITY_PUSH_WORKFLOW_ID, processAvailabilityPushIntents, } from "./availability-push.js";
25
+ import { CHANNEL_BOOKING_PUSH_WORKFLOW_ID, processBookingPush, } from "./booking-push.js";
26
+ import { CHANNEL_CONTENT_PUSH_WORKFLOW_ID, processContentPushIntents, } from "./content-push.js";
27
+ /**
28
+ * Per-booking saga workflow with compensation support.
29
+ *
30
+ * Concurrency is keyed by `bookingId` so two confirms of the same
31
+ * booking serialize (which can't actually happen given the booking
32
+ * state machine, but the perKey lock is cheap insurance). Retries on
33
+ * exponential backoff up to 5 attempts; the per-link compensation pass
34
+ * inside `processBookingPush` handles strict-atomic policy when
35
+ * `channel_contracts.policy.compensation = "strict-atomic"`.
36
+ *
37
+ * Per §4.2 + §12.1.
38
+ */
39
+ export const channelBookingPushWorkflow = workflow({
40
+ id: CHANNEL_BOOKING_PUSH_WORKFLOW_ID,
41
+ description: "Drain pending channel_booking_links and push to upstream channels",
42
+ retry: { backoff: "exponential", max: 5, initial: "5s", maxDelay: "5m" },
43
+ timeout: "1h",
44
+ concurrency: {
45
+ key: (input) => input.bookingId,
46
+ limit: 1,
47
+ strategy: "queue",
48
+ },
49
+ tags: ["channel-push", "booking"],
50
+ async run(input, ctx) {
51
+ return await ctx.step("process-booking-push", () => processBookingPush(input));
52
+ },
53
+ });
54
+ /**
55
+ * Scheduled batch worker for availability push. Runs every 30 seconds
56
+ * (tunable per channel via policy in a future iteration); each tick
57
+ * drains up to 100 pending intents, capped at one concurrent run per
58
+ * channel. Idempotency is upstream-side via `(slot_id, remaining_pax)`.
59
+ *
60
+ * Per §5.3 + §12.2.
61
+ */
62
+ export const channelAvailabilityPushWorkflow = workflow({
63
+ id: CHANNEL_AVAILABILITY_PUSH_WORKFLOW_ID,
64
+ description: "Drain channel_availability_push_intents per channel",
65
+ schedule: { every: "30s" },
66
+ retry: { backoff: "exponential", max: 3, initial: "10s" },
67
+ timeout: "5m",
68
+ concurrency: {
69
+ key: (input) => input.channelId ?? "all",
70
+ limit: 1,
71
+ strategy: "queue",
72
+ },
73
+ tags: ["channel-push", "availability"],
74
+ async run(input, ctx) {
75
+ return await ctx.step("process-availability-push", () => processAvailabilityPushIntents(input));
76
+ },
77
+ });
78
+ /**
79
+ * Scheduled batch worker for content push. Longer cadence (5m) since
80
+ * content drift is rarely time-critical; idempotency via the upstream's
81
+ * acknowledged-hash skip in `processContentPushIntents`.
82
+ *
83
+ * Per §6 + §12.3.
84
+ */
85
+ export const channelContentPushWorkflow = workflow({
86
+ id: CHANNEL_CONTENT_PUSH_WORKFLOW_ID,
87
+ description: "Drain channel_content_push_intents per channel",
88
+ schedule: { every: "5m" },
89
+ retry: { backoff: "exponential", max: 3, initial: "30s" },
90
+ timeout: "5m",
91
+ concurrency: {
92
+ key: (input) => input.channelId ?? "all",
93
+ limit: 1,
94
+ strategy: "queue",
95
+ },
96
+ tags: ["channel-push", "content"],
97
+ async run(input, ctx) {
98
+ return await ctx.step("process-content-push", () => processContentPushIntents(input));
99
+ },
100
+ });
package/dist/index.d.ts CHANGED
@@ -5,8 +5,12 @@ export type { DistributionRoutes } from "./routes.js";
5
5
  export declare const distributionModule: Module;
6
6
  export declare const distributionHonoModule: HonoModule;
7
7
  export { distributionBookingExtension } from "./booking-extension.js";
8
+ export { type AcquireTokenAcquired, type AcquireTokenDenied, type AcquireTokenResult, acquireToken, type ChannelPushPriority, channelScopeKey, DEFAULT_PRIORITY_GATES, drainBucket, type RateLimitConfig, } from "./rate-limit.js";
8
9
  export type { Channel, ChannelBookingLink, ChannelCommissionRule, ChannelContactProjection, ChannelContract, ChannelInventoryAllotment, ChannelInventoryAllotmentTarget, ChannelInventoryReleaseExecution, ChannelInventoryReleaseRule, ChannelProductMapping, ChannelReconciliationItem, ChannelReconciliationPolicy, ChannelReconciliationRun, ChannelReleaseSchedule, ChannelRemittanceException, ChannelSettlementApproval, ChannelSettlementItem, ChannelSettlementPolicy, ChannelSettlementRun, ChannelWebhookEvent, NewChannel, NewChannelBookingLink, NewChannelCommissionRule, NewChannelContactProjection, NewChannelContract, NewChannelInventoryAllotment, NewChannelInventoryAllotmentTarget, NewChannelInventoryReleaseExecution, NewChannelInventoryReleaseRule, NewChannelProductMapping, NewChannelReconciliationItem, NewChannelReconciliationPolicy, NewChannelReconciliationRun, NewChannelReleaseSchedule, NewChannelRemittanceException, NewChannelSettlementApproval, NewChannelSettlementItem, NewChannelSettlementPolicy, NewChannelSettlementRun, NewChannelWebhookEvent, } from "./schema.js";
9
10
  export { channelBookingLinks, channelCommissionRules, channelContactProjections, channelContracts, channelInventoryAllotments, channelInventoryAllotmentTargets, channelInventoryReleaseExecutions, channelInventoryReleaseRules, channelProductMappings, channelReconciliationItems, channelReconciliationPolicies, channelReconciliationPolicyFrequencyEnum, channelReconciliationRuns, channelReleaseScheduleKindEnum, channelReleaseSchedules, channelRemittanceExceptionStatusEnum, channelRemittanceExceptions, channelSettlementApprovalStatusEnum, channelSettlementApprovals, channelSettlementItems, channelSettlementPolicies, channelSettlementPolicyFrequencyEnum, channelSettlementRuns, channels, channelWebhookEvents, } from "./schema.js";
11
+ export type { ChannelAvailabilityPushIntent, ChannelContentPushIntent, NewChannelAvailabilityPushIntent, NewChannelContentPushIntent, } from "./schema-push-intents.js";
12
+ export { channelAvailabilityPushIntents, channelContentPushIntents, } from "./schema-push-intents.js";
10
13
  export { channelBookingLinkListQuerySchema, channelCommissionRuleListQuerySchema, channelContractListQuerySchema, channelInventoryAllotmentListQuerySchema, channelInventoryAllotmentTargetListQuerySchema, channelInventoryReleaseExecutionListQuerySchema, channelInventoryReleaseRuleListQuerySchema, channelListQuerySchema, channelProductMappingListQuerySchema, channelReconciliationItemListQuerySchema, channelReconciliationPolicyFrequencySchema, channelReconciliationPolicyListQuerySchema, channelReconciliationRunListQuerySchema, channelReleaseScheduleKindSchema, channelReleaseScheduleListQuerySchema, channelRemittanceExceptionListQuerySchema, channelRemittanceExceptionStatusSchema, channelSettlementApprovalListQuerySchema, channelSettlementApprovalStatusSchema, channelSettlementItemListQuerySchema, channelSettlementPolicyFrequencySchema, channelSettlementPolicyListQuerySchema, channelSettlementRunListQuerySchema, channelWebhookEventListQuerySchema, insertChannelBookingLinkSchema, insertChannelCommissionRuleSchema, insertChannelContractSchema, insertChannelInventoryAllotmentSchema, insertChannelInventoryAllotmentTargetSchema, insertChannelInventoryReleaseExecutionSchema, insertChannelInventoryReleaseRuleSchema, insertChannelProductMappingSchema, insertChannelReconciliationItemSchema, insertChannelReconciliationPolicySchema, insertChannelReconciliationRunSchema, insertChannelReleaseScheduleSchema, insertChannelRemittanceExceptionSchema, insertChannelSchema, insertChannelSettlementApprovalSchema, insertChannelSettlementItemSchema, insertChannelSettlementPolicySchema, insertChannelSettlementRunSchema, insertChannelWebhookEventSchema, updateChannelBookingLinkSchema, updateChannelCommissionRuleSchema, updateChannelContractSchema, updateChannelInventoryAllotmentSchema, updateChannelInventoryAllotmentTargetSchema, updateChannelInventoryReleaseExecutionSchema, updateChannelInventoryReleaseRuleSchema, updateChannelProductMappingSchema, updateChannelReconciliationItemSchema, updateChannelReconciliationPolicySchema, updateChannelReconciliationRunSchema, updateChannelReleaseScheduleSchema, updateChannelRemittanceExceptionSchema, updateChannelSchema, updateChannelSettlementApprovalSchema, updateChannelSettlementItemSchema, updateChannelSettlementPolicySchema, updateChannelSettlementRunSchema, updateChannelWebhookEventSchema, } from "./validation.js";
14
+ export { type OutboundEnvelopeInput, type OutboundEnvelopeResultInput, type PreparedEnvelope, prepareOutboundEnvelope, redactBodyPii, redactHeaders, redactStringPii, } from "./webhook-deliveries.js";
11
15
  export { distributionService };
12
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -8,6 +8,9 @@ export const distributionHonoModule = {
8
8
  routes: distributionRoutes,
9
9
  };
10
10
  export { distributionBookingExtension } from "./booking-extension.js";
11
+ export { acquireToken, channelScopeKey, DEFAULT_PRIORITY_GATES, drainBucket, } from "./rate-limit.js";
11
12
  export { channelBookingLinks, channelCommissionRules, channelContactProjections, channelContracts, channelInventoryAllotments, channelInventoryAllotmentTargets, channelInventoryReleaseExecutions, channelInventoryReleaseRules, channelProductMappings, channelReconciliationItems, channelReconciliationPolicies, channelReconciliationPolicyFrequencyEnum, channelReconciliationRuns, channelReleaseScheduleKindEnum, channelReleaseSchedules, channelRemittanceExceptionStatusEnum, channelRemittanceExceptions, channelSettlementApprovalStatusEnum, channelSettlementApprovals, channelSettlementItems, channelSettlementPolicies, channelSettlementPolicyFrequencyEnum, channelSettlementRuns, channels, channelWebhookEvents, } from "./schema.js";
13
+ export { channelAvailabilityPushIntents, channelContentPushIntents, } from "./schema-push-intents.js";
12
14
  export { channelBookingLinkListQuerySchema, channelCommissionRuleListQuerySchema, channelContractListQuerySchema, channelInventoryAllotmentListQuerySchema, channelInventoryAllotmentTargetListQuerySchema, channelInventoryReleaseExecutionListQuerySchema, channelInventoryReleaseRuleListQuerySchema, channelListQuerySchema, channelProductMappingListQuerySchema, channelReconciliationItemListQuerySchema, channelReconciliationPolicyFrequencySchema, channelReconciliationPolicyListQuerySchema, channelReconciliationRunListQuerySchema, channelReleaseScheduleKindSchema, channelReleaseScheduleListQuerySchema, channelRemittanceExceptionListQuerySchema, channelRemittanceExceptionStatusSchema, channelSettlementApprovalListQuerySchema, channelSettlementApprovalStatusSchema, channelSettlementItemListQuerySchema, channelSettlementPolicyFrequencySchema, channelSettlementPolicyListQuerySchema, channelSettlementRunListQuerySchema, channelWebhookEventListQuerySchema, insertChannelBookingLinkSchema, insertChannelCommissionRuleSchema, insertChannelContractSchema, insertChannelInventoryAllotmentSchema, insertChannelInventoryAllotmentTargetSchema, insertChannelInventoryReleaseExecutionSchema, insertChannelInventoryReleaseRuleSchema, insertChannelProductMappingSchema, insertChannelReconciliationItemSchema, insertChannelReconciliationPolicySchema, insertChannelReconciliationRunSchema, insertChannelReleaseScheduleSchema, insertChannelRemittanceExceptionSchema, insertChannelSchema, insertChannelSettlementApprovalSchema, insertChannelSettlementItemSchema, insertChannelSettlementPolicySchema, insertChannelSettlementRunSchema, insertChannelWebhookEventSchema, updateChannelBookingLinkSchema, updateChannelCommissionRuleSchema, updateChannelContractSchema, updateChannelInventoryAllotmentSchema, updateChannelInventoryAllotmentTargetSchema, updateChannelInventoryReleaseExecutionSchema, updateChannelInventoryReleaseRuleSchema, updateChannelProductMappingSchema, updateChannelReconciliationItemSchema, updateChannelReconciliationPolicySchema, updateChannelReconciliationRunSchema, updateChannelReleaseScheduleSchema, updateChannelRemittanceExceptionSchema, updateChannelSchema, updateChannelSettlementApprovalSchema, updateChannelSettlementItemSchema, updateChannelSettlementPolicySchema, updateChannelSettlementRunSchema, updateChannelWebhookEventSchema, } from "./validation.js";
15
+ export { prepareOutboundEnvelope, redactBodyPii, redactHeaders, redactStringPii, } from "./webhook-deliveries.js";
13
16
  export { distributionService };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Per-channel rate limiting (token bucket on Postgres).
3
+ *
4
+ * `acquireToken` is the canonical channel-push wrapper around the
5
+ * generic `infra.rate_limit_buckets` primitive. Each call:
6
+ *
7
+ * 1. Atomically refills the bucket based on `(now - last_refill_at) *
8
+ * refill_rate`, capped at capacity.
9
+ * 2. Checks the priority gate: tokens_available >= gate * capacity
10
+ * AND tokens_available >= 1.
11
+ * 3. On success, decrements by 1 and returns `{ acquired: true }`.
12
+ * 4. On denial, returns `{ acquired: false, retryAfterMs }` computed
13
+ * from how long until enough tokens refill to clear the gate.
14
+ *
15
+ * Whole thing is one round-trip (an UPSERT with conditional UPDATE).
16
+ *
17
+ * Per docs/architecture/channel-push-architecture.md §14.2 and §14.3.
18
+ */
19
+ import type { AnyDrizzleDb } from "@voyantjs/db";
20
+ export type ChannelPushPriority = "booking" | "availability" | "content";
21
+ export interface RateLimitConfig {
22
+ /** Sustained refill rate (tokens per second). */
23
+ rps: number;
24
+ /** Burst capacity (max tokens in the bucket). */
25
+ burst: number;
26
+ /**
27
+ * Per-priority reserve thresholds. Defaults to:
28
+ * { booking: 0, availability: 0.3, content: 0.7 }
29
+ * Read as: bookings dispatch with any tokens; availability when
30
+ * bucket ≥ 30% full; content when ≥ 70% full.
31
+ */
32
+ priorityGates?: Partial<Record<ChannelPushPriority, number>>;
33
+ }
34
+ export declare const DEFAULT_PRIORITY_GATES: Record<ChannelPushPriority, number>;
35
+ export interface AcquireTokenAcquired {
36
+ acquired: true;
37
+ /** Tokens left in the bucket after this call. */
38
+ tokensRemaining: number;
39
+ }
40
+ export interface AcquireTokenDenied {
41
+ acquired: false;
42
+ /** Suggested wait time in milliseconds before retrying. */
43
+ retryAfterMs: number;
44
+ /** Tokens currently in the bucket (post-refill). */
45
+ tokensAvailable: number;
46
+ }
47
+ export type AcquireTokenResult = AcquireTokenAcquired | AcquireTokenDenied;
48
+ /**
49
+ * Build the channel-push scope key from a (channel, connection) pair.
50
+ * Same shape used by the workflow + reconciler so all paths address the
51
+ * same bucket.
52
+ */
53
+ export declare function channelScopeKey(channelId: string, connectionId: string): string;
54
+ /**
55
+ * Acquire one token from the bucket at `scope`, applying the priority
56
+ * gate for `priority`. Creates the bucket on first call (UPSERT with
57
+ * full capacity).
58
+ */
59
+ export declare function acquireToken(db: AnyDrizzleDb, scope: string, config: RateLimitConfig, priority: ChannelPushPriority): Promise<AcquireTokenResult>;
60
+ /**
61
+ * Drain the bucket to zero and freeze it for `cooldownMs`.
62
+ *
63
+ * Called when an upstream returns 429 with a `Retry-After` hint —
64
+ * prevents subsequent dispatchers from immediately retrying through
65
+ * the same bucket and lets our outbound estimate converge with the
66
+ * channel's authoritative state. Per §14.4.
67
+ */
68
+ export declare function drainBucket(db: AnyDrizzleDb, scope: string, cooldownMs: number): Promise<void>;
69
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAAA;AAExE,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAA;IACX,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAA;CAC7D;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAItE,CAAA;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,IAAI,CAAA;IACd,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAA;IACf,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,GAAG,kBAAkB,CAAA;AAE1E;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAkF7B;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAUf"}