deepline 0.1.291 → 0.1.293
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/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +81 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +4 -46
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +0 -14
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +50 -29
- package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +6 -14
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +5 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +76 -11
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +410 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +494 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +0 -343
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-state-sink.ts +0 -69
- package/dist/bundling-sources/shared_libs/plays/legacy-webhook-drain.ts +0 -110
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES,
|
|
3
|
-
RUNTIME_RECEIPT_OUTPUT_MAX_BYTES,
|
|
4
|
-
} from './output-size-limits';
|
|
5
|
-
|
|
6
|
-
export const DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BATCH_SIZE = 200;
|
|
7
|
-
export const DEFAULT_RECEIPT_COMPLETION_SINK_MAX_FLUSH_MS = 10;
|
|
8
|
-
export const DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BUFFERED_BYTES =
|
|
9
|
-
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES;
|
|
10
|
-
export const DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BATCH_BYTES = 1_000_000;
|
|
11
|
-
export const DEFAULT_RECEIPT_COMPLETION_SINK_MAX_INPUT_BYTES =
|
|
12
|
-
RUNTIME_RECEIPT_OUTPUT_MAX_BYTES;
|
|
13
|
-
|
|
14
|
-
type ReceiptCompletionSinkTimer = ReturnType<typeof setTimeout>;
|
|
15
|
-
|
|
16
|
-
export interface ReceiptCompletionSinkFlushEvent {
|
|
17
|
-
count: number;
|
|
18
|
-
bytes: number;
|
|
19
|
-
elapsedMs?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface RuntimeReceiptCompletionSinkOptions<TInput, TOutput> {
|
|
23
|
-
maxBatchSize?: number;
|
|
24
|
-
maxFlushMs?: number;
|
|
25
|
-
maxBufferedBytes?: number;
|
|
26
|
-
/** Flush trigger and maximum ordinary transport batch size. */
|
|
27
|
-
maxBatchBytes?: number;
|
|
28
|
-
maxInputBytes?: number;
|
|
29
|
-
estimateBytes?: (input: TInput) => number;
|
|
30
|
-
completeMany: (inputs: TInput[]) => Promise<TOutput[]>;
|
|
31
|
-
onFlushStart?: (event: ReceiptCompletionSinkFlushEvent) => void;
|
|
32
|
-
onFlushDone?: (event: ReceiptCompletionSinkFlushEvent) => void;
|
|
33
|
-
onFlushError?: (
|
|
34
|
-
event: ReceiptCompletionSinkFlushEvent & { error: unknown },
|
|
35
|
-
) => void;
|
|
36
|
-
now?: () => number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface PendingReceiptCompletion<TInput, TOutput> {
|
|
40
|
-
input: TInput;
|
|
41
|
-
bytes: number;
|
|
42
|
-
resolve: (output: TOutput) => void;
|
|
43
|
-
reject: (error: unknown) => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
type CapacityWaiter = {
|
|
47
|
-
resolve: () => void;
|
|
48
|
-
reject: (error: unknown) => void;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function normalizePositiveInteger(
|
|
52
|
-
value: number | undefined,
|
|
53
|
-
fallback: number,
|
|
54
|
-
): number {
|
|
55
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;
|
|
56
|
-
return Math.max(1, Math.floor(value));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function defaultReceiptCompletionByteEstimate(input: unknown): number {
|
|
60
|
-
try {
|
|
61
|
-
return new TextEncoder().encode(JSON.stringify(input)).byteLength;
|
|
62
|
-
} catch {
|
|
63
|
-
return 1024;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class ReceiptCompletionBufferLimitError extends Error {
|
|
68
|
-
readonly code = 'RECEIPT_COMPLETION_BUFFER_LIMIT_EXCEEDED';
|
|
69
|
-
|
|
70
|
-
constructor(message: string) {
|
|
71
|
-
super(message);
|
|
72
|
-
this.name = 'ReceiptCompletionBufferLimitError';
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export class ReceiptCompletionResultCountError extends Error {
|
|
77
|
-
readonly code = 'RECEIPT_COMPLETION_RESULT_COUNT_MISMATCH';
|
|
78
|
-
|
|
79
|
-
constructor(message: string) {
|
|
80
|
-
super(message);
|
|
81
|
-
this.name = 'ReceiptCompletionResultCountError';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export class RuntimeReceiptCompletionSink<TInput, TOutput> {
|
|
86
|
-
readonly maxBatchSize: number;
|
|
87
|
-
readonly maxFlushMs: number;
|
|
88
|
-
readonly maxBufferedBytes: number;
|
|
89
|
-
readonly maxBatchBytes: number;
|
|
90
|
-
readonly maxInputBytes: number;
|
|
91
|
-
|
|
92
|
-
#pending: Array<PendingReceiptCompletion<TInput, TOutput>> = [];
|
|
93
|
-
#pendingHead = 0;
|
|
94
|
-
/** Pending plus in-flight bytes. Released only after transport settles. */
|
|
95
|
-
#residentBytes = 0;
|
|
96
|
-
#pendingBytes = 0;
|
|
97
|
-
#capacityWaiters: CapacityWaiter[] = [];
|
|
98
|
-
#flushTimer: ReceiptCompletionSinkTimer | null = null;
|
|
99
|
-
#flushPromise: Promise<void> | null = null;
|
|
100
|
-
#closed = false;
|
|
101
|
-
#failure: Error | null = null;
|
|
102
|
-
#completeMany: (inputs: TInput[]) => Promise<TOutput[]>;
|
|
103
|
-
#estimateBytes: (input: TInput) => number;
|
|
104
|
-
#onFlushStart?: (event: ReceiptCompletionSinkFlushEvent) => void;
|
|
105
|
-
#onFlushDone?: (event: ReceiptCompletionSinkFlushEvent) => void;
|
|
106
|
-
#onFlushError?: (
|
|
107
|
-
event: ReceiptCompletionSinkFlushEvent & { error: unknown },
|
|
108
|
-
) => void;
|
|
109
|
-
#now: () => number;
|
|
110
|
-
|
|
111
|
-
constructor(options: RuntimeReceiptCompletionSinkOptions<TInput, TOutput>) {
|
|
112
|
-
this.maxBatchSize = normalizePositiveInteger(
|
|
113
|
-
options.maxBatchSize,
|
|
114
|
-
DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BATCH_SIZE,
|
|
115
|
-
);
|
|
116
|
-
this.maxFlushMs = normalizePositiveInteger(
|
|
117
|
-
options.maxFlushMs,
|
|
118
|
-
DEFAULT_RECEIPT_COMPLETION_SINK_MAX_FLUSH_MS,
|
|
119
|
-
);
|
|
120
|
-
this.maxBufferedBytes = normalizePositiveInteger(
|
|
121
|
-
options.maxBufferedBytes,
|
|
122
|
-
DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BUFFERED_BYTES,
|
|
123
|
-
);
|
|
124
|
-
this.maxBatchBytes = normalizePositiveInteger(
|
|
125
|
-
options.maxBatchBytes,
|
|
126
|
-
DEFAULT_RECEIPT_COMPLETION_SINK_MAX_BATCH_BYTES,
|
|
127
|
-
);
|
|
128
|
-
this.maxInputBytes = normalizePositiveInteger(
|
|
129
|
-
options.maxInputBytes,
|
|
130
|
-
DEFAULT_RECEIPT_COMPLETION_SINK_MAX_INPUT_BYTES,
|
|
131
|
-
);
|
|
132
|
-
this.#completeMany = options.completeMany;
|
|
133
|
-
this.#estimateBytes =
|
|
134
|
-
options.estimateBytes ?? defaultReceiptCompletionByteEstimate;
|
|
135
|
-
this.#onFlushStart = options.onFlushStart;
|
|
136
|
-
this.#onFlushDone = options.onFlushDone;
|
|
137
|
-
this.#onFlushError = options.onFlushError;
|
|
138
|
-
this.#now = options.now ?? Date.now;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
addComplete(input: TInput): Promise<TOutput> {
|
|
142
|
-
return this.addMany([input]).then((outputs) => outputs[0]!);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async addMany(inputs: readonly TInput[]): Promise<TOutput[]> {
|
|
146
|
-
if (inputs.length === 0) return Promise.resolve([]);
|
|
147
|
-
this.#throwIfUnavailable();
|
|
148
|
-
const outputPromises = inputs.map((input) => this.#admit(input));
|
|
149
|
-
this.#scheduleFlush();
|
|
150
|
-
return await Promise.all(outputPromises);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async addManyAndFlush(inputs: readonly TInput[]): Promise<TOutput[]> {
|
|
154
|
-
const outputsPromise = this.addMany(inputs);
|
|
155
|
-
const flushPromise = this.flush();
|
|
156
|
-
const [outputs] = await Promise.all([outputsPromise, flushPromise]);
|
|
157
|
-
return outputs;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async flush(): Promise<void> {
|
|
161
|
-
if (this.#failure) throw this.#failure;
|
|
162
|
-
this.#clearFlushTimer();
|
|
163
|
-
this.#flushPromise ??= this.#runFlushLoop().finally(() => {
|
|
164
|
-
this.#flushPromise = null;
|
|
165
|
-
});
|
|
166
|
-
return await this.#flushPromise;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async close(): Promise<void> {
|
|
170
|
-
this.#closed = true;
|
|
171
|
-
this.#wakeCapacityWaiters();
|
|
172
|
-
await this.flush();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
getStats(): { pending: number; pendingBytes: number; flushing: boolean } {
|
|
176
|
-
return {
|
|
177
|
-
pending: this.#pendingCount(),
|
|
178
|
-
pendingBytes: this.#residentBytes,
|
|
179
|
-
flushing: Boolean(this.#flushPromise),
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async #admit(input: TInput): Promise<TOutput> {
|
|
184
|
-
const bytes = Math.max(1, Math.ceil(this.#estimateBytes(input)));
|
|
185
|
-
if (bytes > this.maxInputBytes) {
|
|
186
|
-
return Promise.reject(
|
|
187
|
-
new ReceiptCompletionBufferLimitError(
|
|
188
|
-
`Runtime receipt completion payload is ${bytes} bytes, above the ${this.maxInputBytes} byte per-receipt limit. Parse large provider/search responses before returning them from the tool boundary.`,
|
|
189
|
-
),
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
if (bytes > this.maxBufferedBytes) {
|
|
193
|
-
throw new ReceiptCompletionBufferLimitError(
|
|
194
|
-
`Runtime receipt completion payload is ${bytes} bytes, above the ${this.maxBufferedBytes} byte sink capacity.`,
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
while (this.#residentBytes + bytes > this.maxBufferedBytes) {
|
|
198
|
-
this.#throwIfUnavailable();
|
|
199
|
-
void this.flush().catch(() => undefined);
|
|
200
|
-
await new Promise<void>((resolve, reject) => {
|
|
201
|
-
this.#capacityWaiters.push({ resolve, reject });
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
this.#throwIfUnavailable();
|
|
205
|
-
this.#residentBytes += bytes;
|
|
206
|
-
this.#pendingBytes += bytes;
|
|
207
|
-
const completion = new Promise<TOutput>((resolve, reject) => {
|
|
208
|
-
this.#pending.push({ input, bytes, resolve, reject });
|
|
209
|
-
if (this.#pendingCount() >= this.maxBatchSize) {
|
|
210
|
-
void this.flush().catch(() => undefined);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
if (this.#pendingBytes >= this.maxBatchBytes) {
|
|
214
|
-
void this.flush().catch(() => undefined);
|
|
215
|
-
}
|
|
216
|
-
this.#scheduleFlush();
|
|
217
|
-
return await completion;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
#scheduleFlush(): void {
|
|
221
|
-
if (this.#flushTimer || this.#flushPromise || this.#pendingCount() === 0) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.#flushTimer = setTimeout(() => {
|
|
225
|
-
this.#flushTimer = null;
|
|
226
|
-
void this.flush().catch(() => undefined);
|
|
227
|
-
}, this.maxFlushMs);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
#clearFlushTimer(): void {
|
|
231
|
-
if (!this.#flushTimer) return;
|
|
232
|
-
clearTimeout(this.#flushTimer);
|
|
233
|
-
this.#flushTimer = null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async #runFlushLoop(): Promise<void> {
|
|
237
|
-
while (this.#pendingCount() > 0) {
|
|
238
|
-
const batch = this.#takeBatch();
|
|
239
|
-
try {
|
|
240
|
-
await this.#flushBatch(batch);
|
|
241
|
-
} catch (error) {
|
|
242
|
-
this.#fail(error);
|
|
243
|
-
throw this.#failure;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
#takeBatch(): Array<PendingReceiptCompletion<TInput, TOutput>> {
|
|
249
|
-
const batch: Array<PendingReceiptCompletion<TInput, TOutput>> = [];
|
|
250
|
-
let batchBytes = 0;
|
|
251
|
-
while (this.#pendingCount() > 0 && batch.length < this.maxBatchSize) {
|
|
252
|
-
const next = this.#pending[this.#pendingHead]!;
|
|
253
|
-
if (batch.length > 0 && batchBytes + next.bytes > this.maxBatchBytes) {
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
this.#pendingHead += 1;
|
|
257
|
-
batch.push(next);
|
|
258
|
-
batchBytes += next.bytes;
|
|
259
|
-
this.#pendingBytes = Math.max(0, this.#pendingBytes - next.bytes);
|
|
260
|
-
}
|
|
261
|
-
if (
|
|
262
|
-
this.#pendingHead > 1_024 &&
|
|
263
|
-
this.#pendingHead * 2 >= this.#pending.length
|
|
264
|
-
) {
|
|
265
|
-
this.#pending = this.#pending.slice(this.#pendingHead);
|
|
266
|
-
this.#pendingHead = 0;
|
|
267
|
-
}
|
|
268
|
-
return batch;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async #flushBatch(
|
|
272
|
-
batch: Array<PendingReceiptCompletion<TInput, TOutput>>,
|
|
273
|
-
): Promise<void> {
|
|
274
|
-
const inputs = batch.map((item) => item.input);
|
|
275
|
-
const bytes = batch.reduce((sum, item) => sum + item.bytes, 0);
|
|
276
|
-
const startedAt = this.#now();
|
|
277
|
-
this.#onFlushStart?.({ count: inputs.length, bytes });
|
|
278
|
-
try {
|
|
279
|
-
const outputs = await this.#completeMany(inputs);
|
|
280
|
-
if (outputs.length !== batch.length) {
|
|
281
|
-
throw new ReceiptCompletionResultCountError(
|
|
282
|
-
`Runtime receipt completion sink expected ${batch.length} result(s), received ${outputs.length}.`,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
outputs.forEach((output, index) => batch[index]!.resolve(output));
|
|
286
|
-
this.#onFlushDone?.({
|
|
287
|
-
count: inputs.length,
|
|
288
|
-
bytes,
|
|
289
|
-
elapsedMs: this.#now() - startedAt,
|
|
290
|
-
});
|
|
291
|
-
} catch (error) {
|
|
292
|
-
for (const item of batch) {
|
|
293
|
-
item.reject(error);
|
|
294
|
-
}
|
|
295
|
-
this.#onFlushError?.({
|
|
296
|
-
count: inputs.length,
|
|
297
|
-
bytes,
|
|
298
|
-
elapsedMs: this.#now() - startedAt,
|
|
299
|
-
error,
|
|
300
|
-
});
|
|
301
|
-
throw error;
|
|
302
|
-
} finally {
|
|
303
|
-
this.#residentBytes = Math.max(0, this.#residentBytes - bytes);
|
|
304
|
-
this.#wakeCapacityWaiters();
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
#pendingCount(): number {
|
|
309
|
-
return this.#pending.length - this.#pendingHead;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
#throwIfUnavailable(): void {
|
|
313
|
-
if (this.#failure) throw this.#failure;
|
|
314
|
-
if (this.#closed) {
|
|
315
|
-
throw new Error('Runtime receipt completion sink is already closed.');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
#wakeCapacityWaiters(): void {
|
|
320
|
-
const waiters = this.#capacityWaiters;
|
|
321
|
-
this.#capacityWaiters = [];
|
|
322
|
-
for (const waiter of waiters) {
|
|
323
|
-
if (this.#failure) waiter.reject(this.#failure);
|
|
324
|
-
else if (this.#closed) {
|
|
325
|
-
waiter.reject(
|
|
326
|
-
new Error('Runtime receipt completion sink is already closed.'),
|
|
327
|
-
);
|
|
328
|
-
} else waiter.resolve();
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
#fail(error: unknown): void {
|
|
333
|
-
this.#failure =
|
|
334
|
-
error instanceof Error ? error : new Error(String(error ?? 'Unknown'));
|
|
335
|
-
while (this.#pendingCount() > 0) {
|
|
336
|
-
const pending = this.#pending[this.#pendingHead++]!;
|
|
337
|
-
this.#residentBytes = Math.max(0, this.#residentBytes - pending.bytes);
|
|
338
|
-
this.#pendingBytes = Math.max(0, this.#pendingBytes - pending.bytes);
|
|
339
|
-
pending.reject(this.#failure);
|
|
340
|
-
}
|
|
341
|
-
this.#wakeCapacityWaiters();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { RuntimeReceiptCompletionSink } from './receipt-completion-sink';
|
|
2
|
-
|
|
3
|
-
/** Claims/state transitions are small; flush once a 512 KB SQL payload forms. */
|
|
4
|
-
export const DEFAULT_RECEIPT_STATE_SINK_MAX_BATCH_BYTES = 512_000;
|
|
5
|
-
|
|
6
|
-
export interface RuntimeReceiptStateSinkOptions<TInput, TOutput> {
|
|
7
|
-
maxBatchSize?: number;
|
|
8
|
-
maxFlushMs?: number;
|
|
9
|
-
maxBatchBytes?: number;
|
|
10
|
-
maxBufferedBytes?: number;
|
|
11
|
-
maxInputBytes?: number;
|
|
12
|
-
estimateBytes?: (input: TInput) => number;
|
|
13
|
-
applyMany: (inputs: TInput[]) => Promise<TOutput[]>;
|
|
14
|
-
onFlushStart?: (event: { count: number; bytes: number }) => void;
|
|
15
|
-
onFlushDone?: (event: {
|
|
16
|
-
count: number;
|
|
17
|
-
bytes: number;
|
|
18
|
-
elapsedMs?: number;
|
|
19
|
-
}) => void;
|
|
20
|
-
onFlushError?: (event: {
|
|
21
|
-
count: number;
|
|
22
|
-
bytes: number;
|
|
23
|
-
elapsedMs?: number;
|
|
24
|
-
error: unknown;
|
|
25
|
-
}) => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Small coalescing sink for receipt state transitions where callers need the
|
|
30
|
-
* resulting receipt before continuing, but sibling row/tool promises arrive in
|
|
31
|
-
* the same event-loop turn. It is intentionally thin over the proven completion
|
|
32
|
-
* sink so mark-running, mark-queued, and future state changes share one
|
|
33
|
-
* batching shape.
|
|
34
|
-
*/
|
|
35
|
-
export class RuntimeReceiptStateSink<TInput, TOutput> {
|
|
36
|
-
#sink: RuntimeReceiptCompletionSink<TInput, TOutput>;
|
|
37
|
-
|
|
38
|
-
constructor(options: RuntimeReceiptStateSinkOptions<TInput, TOutput>) {
|
|
39
|
-
this.#sink = new RuntimeReceiptCompletionSink<TInput, TOutput>({
|
|
40
|
-
maxBatchSize: options.maxBatchSize,
|
|
41
|
-
maxFlushMs: options.maxFlushMs,
|
|
42
|
-
maxBatchBytes:
|
|
43
|
-
options.maxBatchBytes ?? DEFAULT_RECEIPT_STATE_SINK_MAX_BATCH_BYTES,
|
|
44
|
-
maxBufferedBytes: options.maxBufferedBytes,
|
|
45
|
-
maxInputBytes: options.maxInputBytes,
|
|
46
|
-
estimateBytes: options.estimateBytes,
|
|
47
|
-
completeMany: options.applyMany,
|
|
48
|
-
onFlushStart: options.onFlushStart,
|
|
49
|
-
onFlushDone: options.onFlushDone,
|
|
50
|
-
onFlushError: options.onFlushError,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
add(input: TInput): Promise<TOutput> {
|
|
55
|
-
return this.#sink.addComplete(input);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
addMany(inputs: readonly TInput[]): Promise<TOutput[]> {
|
|
59
|
-
return this.#sink.addMany(inputs);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async flush(): Promise<void> {
|
|
63
|
-
await this.#sink.flush();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async close(): Promise<void> {
|
|
67
|
-
await this.#sink.close();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
export const LEGACY_WEBHOOK_RETRY_CODES = [
|
|
2
|
-
'ACTIVE_CONCURRENCY_LIMIT',
|
|
3
|
-
'SCHEDULER_CAPACITY',
|
|
4
|
-
'TRANSIENT_LAUNCH_FAILURE',
|
|
5
|
-
] as const;
|
|
6
|
-
|
|
7
|
-
export type LegacyWebhookRetryCode =
|
|
8
|
-
(typeof LEGACY_WEBHOOK_RETRY_CODES)[number];
|
|
9
|
-
|
|
10
|
-
export const LEGACY_WEBHOOK_QUARANTINE_CODES = [
|
|
11
|
-
'PLAY_DEFINITION_MISSING',
|
|
12
|
-
'PLAY_BINDING_MISSING',
|
|
13
|
-
'PLAY_BINDING_DISABLED',
|
|
14
|
-
'PLAY_REVISION_MISSING',
|
|
15
|
-
'PLAY_REVISION_MISMATCH',
|
|
16
|
-
'ARTIFACT_RUNTIME_INCOMPATIBLE',
|
|
17
|
-
'ARTIFACT_REFERENCE_MISSING',
|
|
18
|
-
'WEBHOOK_PAYLOAD_INVALID',
|
|
19
|
-
'PLAY_PRE_RUN_VALIDATION_FAILED',
|
|
20
|
-
'RUN_IDENTITY_CONFLICT',
|
|
21
|
-
'MAX_DELIVERY_ATTEMPTS_EXCEEDED',
|
|
22
|
-
] as const;
|
|
23
|
-
|
|
24
|
-
export type LegacyWebhookQuarantineCode =
|
|
25
|
-
(typeof LEGACY_WEBHOOK_QUARANTINE_CODES)[number];
|
|
26
|
-
|
|
27
|
-
export function isLegacyWebhookQuarantineCode(
|
|
28
|
-
value: unknown,
|
|
29
|
-
): value is LegacyWebhookQuarantineCode {
|
|
30
|
-
return (
|
|
31
|
-
typeof value === 'string' &&
|
|
32
|
-
(LEGACY_WEBHOOK_QUARANTINE_CODES as readonly string[]).includes(value)
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type LegacyWebhookQuarantineFailure =
|
|
37
|
-
| { code: 'PLAY_DEFINITION_MISSING' }
|
|
38
|
-
| { code: 'PLAY_BINDING_MISSING' }
|
|
39
|
-
| { code: 'PLAY_BINDING_DISABLED'; actualStatus: string }
|
|
40
|
-
| { code: 'PLAY_REVISION_MISSING' }
|
|
41
|
-
| {
|
|
42
|
-
code: 'PLAY_REVISION_MISMATCH';
|
|
43
|
-
expectedDefinitionId: string;
|
|
44
|
-
actualDefinitionId: string;
|
|
45
|
-
}
|
|
46
|
-
| {
|
|
47
|
-
code: 'ARTIFACT_RUNTIME_INCOMPATIBLE';
|
|
48
|
-
expectedArtifactKind: 'cjs_node20';
|
|
49
|
-
actualArtifactKind: string;
|
|
50
|
-
}
|
|
51
|
-
| { code: 'ARTIFACT_REFERENCE_MISSING' }
|
|
52
|
-
| {
|
|
53
|
-
code: 'WEBHOOK_PAYLOAD_INVALID';
|
|
54
|
-
reason: 'unsupported_shape';
|
|
55
|
-
}
|
|
56
|
-
| { code: 'PLAY_PRE_RUN_VALIDATION_FAILED' }
|
|
57
|
-
| { code: 'RUN_IDENTITY_CONFLICT' }
|
|
58
|
-
| { code: 'MAX_DELIVERY_ATTEMPTS_EXCEEDED' };
|
|
59
|
-
|
|
60
|
-
export type LegacyWebhookDrainState =
|
|
61
|
-
| { kind: 'pending' }
|
|
62
|
-
| {
|
|
63
|
-
kind: 'leased';
|
|
64
|
-
leaseOwner: string;
|
|
65
|
-
leaseExpiresAt: number;
|
|
66
|
-
attempts: number;
|
|
67
|
-
}
|
|
68
|
-
| {
|
|
69
|
-
kind: 'retry';
|
|
70
|
-
failureCode: LegacyWebhookRetryCode;
|
|
71
|
-
retryAt: number;
|
|
72
|
-
attempts: number;
|
|
73
|
-
error: string;
|
|
74
|
-
}
|
|
75
|
-
| {
|
|
76
|
-
kind: 'quarantined';
|
|
77
|
-
failure: LegacyWebhookQuarantineFailure;
|
|
78
|
-
quarantinedAt: number;
|
|
79
|
-
attempts: number;
|
|
80
|
-
error: string;
|
|
81
|
-
}
|
|
82
|
-
| { kind: 'launched'; launchedAt: number }
|
|
83
|
-
| {
|
|
84
|
-
kind: 'migrated';
|
|
85
|
-
migratedAt: number;
|
|
86
|
-
migratedWebhookEventId: string;
|
|
87
|
-
migratedAbsurdTaskId: string;
|
|
88
|
-
}
|
|
89
|
-
| {
|
|
90
|
-
kind: 'retired';
|
|
91
|
-
retiredAt: number;
|
|
92
|
-
retiredBy: string;
|
|
93
|
-
reason: string;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export type LegacyWebhookDrainOutcome =
|
|
97
|
-
| { kind: 'started' }
|
|
98
|
-
| { kind: 'quarantined'; failure: LegacyWebhookQuarantineFailure }
|
|
99
|
-
| { kind: 'retry'; failureCode: LegacyWebhookRetryCode }
|
|
100
|
-
| { kind: 'lease_lost' };
|
|
101
|
-
|
|
102
|
-
export function isLegacyWebhookPayload(
|
|
103
|
-
value: unknown,
|
|
104
|
-
): value is Record<string, unknown> | unknown[] {
|
|
105
|
-
return Array.isArray(value) || (value !== null && typeof value === 'object');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function assertNever(value: never, context: string): never {
|
|
109
|
-
throw new Error(`${context}: ${JSON.stringify(value)}`);
|
|
110
|
-
}
|