@beignet/core 0.0.32 → 0.0.34
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 +42 -0
- package/README.md +409 -34
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +63 -47
- package/dist/application/index.js.map +1 -1
- package/dist/contracts/contract-builder.d.ts +0 -4
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +0 -6
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/events/index.d.ts +6 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +20 -10
- package/dist/events/index.js.map +1 -1
- package/dist/flags/index.d.ts +0 -4
- package/dist/flags/index.d.ts.map +1 -1
- package/dist/flags/index.js +0 -4
- package/dist/flags/index.js.map +1 -1
- package/dist/jobs/index.d.ts +415 -4
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +392 -3
- package/dist/jobs/index.js.map +1 -1
- package/dist/notifications/index.d.ts +194 -10
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +397 -59
- package/dist/notifications/index.js.map +1 -1
- package/dist/outbox/index.d.ts +150 -1
- package/dist/outbox/index.d.ts.map +1 -1
- package/dist/outbox/index.js +170 -1
- package/dist/outbox/index.js.map +1 -1
- package/dist/ports/events.d.ts +2 -2
- package/dist/ports/index.d.ts +7 -2
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +2 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/providers/instrumentation.d.ts +4 -0
- package/dist/providers/instrumentation.d.ts.map +1 -1
- package/dist/providers/instrumentation.js.map +1 -1
- package/dist/providers/provider.d.ts +1 -1
- package/dist/schedules/index.d.ts +8 -7
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +47 -16
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/hooks/index.d.ts +1 -0
- package/dist/server/hooks/index.d.ts.map +1 -1
- package/dist/server/hooks/index.js +1 -0
- package/dist/server/hooks/index.js.map +1 -1
- package/dist/server/hooks/rate-limit.d.ts +26 -13
- package/dist/server/hooks/rate-limit.d.ts.map +1 -1
- package/dist/server/hooks/rate-limit.js +28 -34
- package/dist/server/hooks/rate-limit.js.map +1 -1
- package/dist/server/hooks/security.d.ts +179 -0
- package/dist/server/hooks/security.d.ts.map +1 -0
- package/dist/server/hooks/security.js +225 -0
- package/dist/server/hooks/security.js.map +1 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/instrumentation.d.ts.map +1 -1
- package/dist/server/instrumentation.js +22 -6
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/request-context.d.ts +4 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +3 -0
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/runtime-integrity.d.ts +84 -0
- package/dist/server/runtime-integrity.d.ts.map +1 -0
- package/dist/server/runtime-integrity.js +160 -0
- package/dist/server/runtime-integrity.js.map +1 -0
- package/dist/server/server.d.ts +10 -10
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +75 -11
- package/dist/server/server.js.map +1 -1
- package/dist/server/trusted-proxy.d.ts +77 -0
- package/dist/server/trusted-proxy.d.ts.map +1 -0
- package/dist/server/trusted-proxy.js +129 -0
- package/dist/server/trusted-proxy.js.map +1 -0
- package/dist/tasks/index.d.ts +6 -7
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +22 -5
- package/dist/tasks/index.js.map +1 -1
- package/dist/tenancy/index.d.ts +41 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +34 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/tracing/execution.d.ts +14 -0
- package/dist/tracing/execution.d.ts.map +1 -0
- package/dist/tracing/execution.js +17 -0
- package/dist/tracing/execution.js.map +1 -0
- package/dist/tracing/index.d.ts +49 -0
- package/dist/tracing/index.d.ts.map +1 -1
- package/dist/tracing/index.js +59 -0
- package/dist/tracing/index.js.map +1 -1
- package/dist/uploads/client.d.ts.map +1 -1
- package/dist/uploads/client.js +44 -6
- package/dist/uploads/client.js.map +1 -1
- package/dist/uploads/index.d.ts +71 -0
- package/dist/uploads/index.d.ts.map +1 -1
- package/dist/uploads/index.js +344 -15
- package/dist/uploads/index.js.map +1 -1
- package/dist/webhooks/index.d.ts +55 -4
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/index.js +106 -2
- package/dist/webhooks/index.js.map +1 -1
- package/package.json +5 -5
- package/skills/app-architecture/SKILL.md +31 -2
- package/src/application/index.ts +88 -56
- package/src/contracts/contract-builder.ts +0 -7
- package/src/events/index.ts +27 -14
- package/src/flags/index.ts +0 -5
- package/src/jobs/index.ts +895 -7
- package/src/notifications/index.ts +678 -70
- package/src/outbox/index.ts +358 -2
- package/src/ports/events.ts +2 -2
- package/src/ports/index.ts +17 -1
- package/src/providers/instrumentation.ts +4 -0
- package/src/providers/provider.ts +1 -1
- package/src/schedules/index.ts +60 -26
- package/src/server/hooks/index.ts +10 -0
- package/src/server/hooks/rate-limit.ts +52 -46
- package/src/server/hooks/security.ts +503 -0
- package/src/server/index.ts +8 -0
- package/src/server/instrumentation.ts +25 -5
- package/src/server/request-context.ts +7 -0
- package/src/server/runtime-integrity.ts +322 -0
- package/src/server/server.ts +88 -35
- package/src/server/trusted-proxy.ts +249 -0
- package/src/tasks/index.ts +29 -12
- package/src/tenancy/index.ts +55 -0
- package/src/testing/index.ts +2 -0
- package/src/tracing/execution.ts +37 -0
- package/src/tracing/index.ts +137 -0
- package/src/uploads/client.ts +65 -6
- package/src/uploads/index.ts +539 -14
- package/src/webhooks/index.ts +238 -9
package/src/outbox/index.ts
CHANGED
|
@@ -269,6 +269,116 @@ export interface OutboxMarkFailedInput {
|
|
|
269
269
|
now?: Date;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Default maximum messages returned from outbox admin list calls.
|
|
274
|
+
*/
|
|
275
|
+
export const DEFAULT_OUTBOX_ADMIN_LIST_LIMIT = 50;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Shared filters for outbox admin read operations.
|
|
279
|
+
*/
|
|
280
|
+
export interface OutboxMessageQuery {
|
|
281
|
+
/**
|
|
282
|
+
* Status or statuses to include.
|
|
283
|
+
*/
|
|
284
|
+
status?: OutboxMessageStatus | readonly OutboxMessageStatus[];
|
|
285
|
+
/**
|
|
286
|
+
* Message kind to include.
|
|
287
|
+
*/
|
|
288
|
+
kind?: OutboxMessageKind;
|
|
289
|
+
/**
|
|
290
|
+
* Event or job name to include.
|
|
291
|
+
*/
|
|
292
|
+
name?: string;
|
|
293
|
+
/**
|
|
294
|
+
* Include messages last updated before this timestamp.
|
|
295
|
+
*/
|
|
296
|
+
updatedBefore?: Date;
|
|
297
|
+
/**
|
|
298
|
+
* Include delivered messages delivered before this timestamp.
|
|
299
|
+
*/
|
|
300
|
+
deliveredBefore?: Date;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Options for listing outbox messages through the admin port.
|
|
305
|
+
*/
|
|
306
|
+
export interface OutboxListMessagesOptions extends OutboxMessageQuery {
|
|
307
|
+
/**
|
|
308
|
+
* Maximum messages to return. Defaults to
|
|
309
|
+
* `DEFAULT_OUTBOX_ADMIN_LIST_LIMIT`.
|
|
310
|
+
*/
|
|
311
|
+
limit?: number;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Options for counting outbox messages through the admin port.
|
|
316
|
+
*/
|
|
317
|
+
export interface OutboxCountMessagesOptions extends OutboxMessageQuery {}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Input for returning a dead-lettered message to the pending queue.
|
|
321
|
+
*/
|
|
322
|
+
export interface OutboxRequeueMessageInput {
|
|
323
|
+
/**
|
|
324
|
+
* Dead-lettered message ID.
|
|
325
|
+
*/
|
|
326
|
+
id: string;
|
|
327
|
+
/**
|
|
328
|
+
* Earliest time the message may be claimed again. Defaults to `now`.
|
|
329
|
+
*/
|
|
330
|
+
availableAt?: Date;
|
|
331
|
+
/**
|
|
332
|
+
* Reset attempts to zero before requeueing. Defaults to preserving the
|
|
333
|
+
* attempt count so operators can decide whether to grant a fresh retry
|
|
334
|
+
* budget.
|
|
335
|
+
*/
|
|
336
|
+
resetAttempts?: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* Requeue timestamp.
|
|
339
|
+
*/
|
|
340
|
+
now?: Date;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Input for purging dead-lettered messages.
|
|
345
|
+
*/
|
|
346
|
+
export interface OutboxPurgeDeadLetteredInput {
|
|
347
|
+
/**
|
|
348
|
+
* Only purge messages last updated before this timestamp. Omit only when the
|
|
349
|
+
* caller intentionally wants to purge all dead-lettered messages.
|
|
350
|
+
*/
|
|
351
|
+
before?: Date;
|
|
352
|
+
/**
|
|
353
|
+
* Maximum messages to purge.
|
|
354
|
+
*/
|
|
355
|
+
limit?: number;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Input for pruning delivered messages.
|
|
360
|
+
*/
|
|
361
|
+
export interface OutboxPruneDeliveredInput {
|
|
362
|
+
/**
|
|
363
|
+
* Prune delivered messages delivered before this timestamp.
|
|
364
|
+
*/
|
|
365
|
+
before: Date;
|
|
366
|
+
/**
|
|
367
|
+
* Maximum messages to prune.
|
|
368
|
+
*/
|
|
369
|
+
limit?: number;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Result for outbox admin delete operations.
|
|
374
|
+
*/
|
|
375
|
+
export interface OutboxDeleteResult {
|
|
376
|
+
/**
|
|
377
|
+
* Number of rows deleted.
|
|
378
|
+
*/
|
|
379
|
+
deleted: number;
|
|
380
|
+
}
|
|
381
|
+
|
|
272
382
|
/**
|
|
273
383
|
* App-facing outbox storage port.
|
|
274
384
|
*
|
|
@@ -294,10 +404,48 @@ export interface OutboxPort {
|
|
|
294
404
|
markFailed(input: OutboxMarkFailedInput): Promise<void>;
|
|
295
405
|
}
|
|
296
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Operational outbox admin port.
|
|
409
|
+
*
|
|
410
|
+
* Keep this separate from `OutboxPort` so request and drain contexts can expose
|
|
411
|
+
* only the hot-path delivery operations. Wire this port into maintenance
|
|
412
|
+
* contexts for CLI commands, runbooks, and devtools.
|
|
413
|
+
*/
|
|
414
|
+
export interface OutboxAdminPort {
|
|
415
|
+
/**
|
|
416
|
+
* List messages ordered by newest update first.
|
|
417
|
+
*/
|
|
418
|
+
listMessages(
|
|
419
|
+
options?: OutboxListMessagesOptions,
|
|
420
|
+
): Promise<readonly OutboxMessage[]>;
|
|
421
|
+
/**
|
|
422
|
+
* Count messages matching admin filters.
|
|
423
|
+
*/
|
|
424
|
+
countMessages(options?: OutboxCountMessagesOptions): Promise<number>;
|
|
425
|
+
/**
|
|
426
|
+
* Fetch one message by ID.
|
|
427
|
+
*/
|
|
428
|
+
getMessage(id: string): Promise<OutboxMessage | null>;
|
|
429
|
+
/**
|
|
430
|
+
* Return a dead-lettered message to pending state.
|
|
431
|
+
*/
|
|
432
|
+
requeueMessage(input: OutboxRequeueMessageInput): Promise<OutboxMessage>;
|
|
433
|
+
/**
|
|
434
|
+
* Delete dead-lettered messages.
|
|
435
|
+
*/
|
|
436
|
+
purgeDeadLettered(
|
|
437
|
+
input?: OutboxPurgeDeadLetteredInput,
|
|
438
|
+
): Promise<OutboxDeleteResult>;
|
|
439
|
+
/**
|
|
440
|
+
* Delete delivered messages older than a retention cutoff.
|
|
441
|
+
*/
|
|
442
|
+
pruneDelivered(input: OutboxPruneDeliveredInput): Promise<OutboxDeleteResult>;
|
|
443
|
+
}
|
|
444
|
+
|
|
297
445
|
/**
|
|
298
446
|
* In-memory outbox for tests and local examples.
|
|
299
447
|
*/
|
|
300
|
-
export interface MemoryOutboxPort extends OutboxPort {
|
|
448
|
+
export interface MemoryOutboxPort extends OutboxPort, OutboxAdminPort {
|
|
301
449
|
/**
|
|
302
450
|
* Current message snapshots.
|
|
303
451
|
*/
|
|
@@ -504,6 +652,22 @@ export class OutboxClaimError extends Error {
|
|
|
504
652
|
}
|
|
505
653
|
}
|
|
506
654
|
|
|
655
|
+
/**
|
|
656
|
+
* Error thrown when an outbox admin operation cannot be completed safely.
|
|
657
|
+
*/
|
|
658
|
+
export class OutboxAdminError extends Error {
|
|
659
|
+
/**
|
|
660
|
+
* Message ID involved in the admin error, when applicable.
|
|
661
|
+
*/
|
|
662
|
+
readonly id?: string;
|
|
663
|
+
|
|
664
|
+
constructor(args: { message: string; id?: string }) {
|
|
665
|
+
super(args.message);
|
|
666
|
+
this.name = "OutboxAdminError";
|
|
667
|
+
this.id = args.id;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
507
671
|
function assertNonEmptyString(name: string, value: string): void {
|
|
508
672
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
509
673
|
throw new Error(`${name} must be a non-empty string`);
|
|
@@ -516,6 +680,12 @@ function assertPositiveInteger(name: string, value: number): void {
|
|
|
516
680
|
}
|
|
517
681
|
}
|
|
518
682
|
|
|
683
|
+
function assertValidDate(name: string, value: Date): void {
|
|
684
|
+
if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
|
|
685
|
+
throw new Error(`${name} must be a valid Date`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
519
689
|
function cloneDate(value: Date): Date {
|
|
520
690
|
return new Date(value.getTime());
|
|
521
691
|
}
|
|
@@ -645,6 +815,75 @@ function copyMessage(message: OutboxMessage): OutboxMessage {
|
|
|
645
815
|
};
|
|
646
816
|
}
|
|
647
817
|
|
|
818
|
+
function normalizeStatusFilter(
|
|
819
|
+
status: OutboxMessageQuery["status"],
|
|
820
|
+
): ReadonlySet<OutboxMessageStatus> | undefined {
|
|
821
|
+
if (status === undefined) return undefined;
|
|
822
|
+
return new Set(Array.isArray(status) ? status : [status]);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function messageMatchesQuery(
|
|
826
|
+
message: OutboxMessage,
|
|
827
|
+
query: OutboxMessageQuery = {},
|
|
828
|
+
): boolean {
|
|
829
|
+
const statuses = normalizeStatusFilter(query.status);
|
|
830
|
+
if (statuses && !statuses.has(message.status)) return false;
|
|
831
|
+
if (query.kind !== undefined && message.kind !== query.kind) return false;
|
|
832
|
+
if (query.name !== undefined && message.name !== query.name) return false;
|
|
833
|
+
if (
|
|
834
|
+
query.updatedBefore !== undefined &&
|
|
835
|
+
message.updatedAt.getTime() >= query.updatedBefore.getTime()
|
|
836
|
+
) {
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
if (
|
|
840
|
+
query.deliveredBefore !== undefined &&
|
|
841
|
+
(message.deliveredAt === null ||
|
|
842
|
+
message.deliveredAt.getTime() >= query.deliveredBefore.getTime())
|
|
843
|
+
) {
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function compareMessagesForAdminList(
|
|
850
|
+
left: OutboxMessage,
|
|
851
|
+
right: OutboxMessage,
|
|
852
|
+
): number {
|
|
853
|
+
const updated = right.updatedAt.getTime() - left.updatedAt.getTime();
|
|
854
|
+
if (updated !== 0) return updated;
|
|
855
|
+
const created = right.createdAt.getTime() - left.createdAt.getTime();
|
|
856
|
+
if (created !== 0) return created;
|
|
857
|
+
return right.id.localeCompare(left.id);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function compareMessagesForAdminCleanup(
|
|
861
|
+
left: OutboxMessage,
|
|
862
|
+
right: OutboxMessage,
|
|
863
|
+
): number {
|
|
864
|
+
const updated = left.updatedAt.getTime() - right.updatedAt.getTime();
|
|
865
|
+
if (updated !== 0) return updated;
|
|
866
|
+
const created = left.createdAt.getTime() - right.createdAt.getTime();
|
|
867
|
+
if (created !== 0) return created;
|
|
868
|
+
return left.id.localeCompare(right.id);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function compareDeliveredMessagesForPrune(
|
|
872
|
+
left: OutboxMessage,
|
|
873
|
+
right: OutboxMessage,
|
|
874
|
+
): number {
|
|
875
|
+
const delivered =
|
|
876
|
+
(left.deliveredAt?.getTime() ?? 0) - (right.deliveredAt?.getTime() ?? 0);
|
|
877
|
+
if (delivered !== 0) return delivered;
|
|
878
|
+
return compareMessagesForAdminCleanup(left, right);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function resolveAdminListLimit(limit: number | undefined): number {
|
|
882
|
+
const resolved = limit ?? DEFAULT_OUTBOX_ADMIN_LIST_LIMIT;
|
|
883
|
+
assertPositiveInteger("limit", resolved);
|
|
884
|
+
return resolved;
|
|
885
|
+
}
|
|
886
|
+
|
|
648
887
|
function toClaimedMessage(message: OutboxMessage): ClaimedOutboxMessage {
|
|
649
888
|
if (
|
|
650
889
|
message.status !== "claimed" ||
|
|
@@ -764,6 +1003,120 @@ export function createMemoryOutbox(
|
|
|
764
1003
|
return [...messages.values()].map(copyMessage);
|
|
765
1004
|
},
|
|
766
1005
|
|
|
1006
|
+
async listMessages(options = {}) {
|
|
1007
|
+
const limit = resolveAdminListLimit(options.limit);
|
|
1008
|
+
if (options.updatedBefore) {
|
|
1009
|
+
assertValidDate("updatedBefore", options.updatedBefore);
|
|
1010
|
+
}
|
|
1011
|
+
if (options.deliveredBefore) {
|
|
1012
|
+
assertValidDate("deliveredBefore", options.deliveredBefore);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return [...messages.values()]
|
|
1016
|
+
.filter((message) => messageMatchesQuery(message, options))
|
|
1017
|
+
.sort(compareMessagesForAdminList)
|
|
1018
|
+
.slice(0, limit)
|
|
1019
|
+
.map(copyMessage);
|
|
1020
|
+
},
|
|
1021
|
+
|
|
1022
|
+
async countMessages(options = {}) {
|
|
1023
|
+
if (options.updatedBefore) {
|
|
1024
|
+
assertValidDate("updatedBefore", options.updatedBefore);
|
|
1025
|
+
}
|
|
1026
|
+
if (options.deliveredBefore) {
|
|
1027
|
+
assertValidDate("deliveredBefore", options.deliveredBefore);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return [...messages.values()].filter((message) =>
|
|
1031
|
+
messageMatchesQuery(message, options),
|
|
1032
|
+
).length;
|
|
1033
|
+
},
|
|
1034
|
+
|
|
1035
|
+
async getMessage(id) {
|
|
1036
|
+
assertNonEmptyString("id", id);
|
|
1037
|
+
const message = messages.get(id);
|
|
1038
|
+
return message ? copyMessage(message) : null;
|
|
1039
|
+
},
|
|
1040
|
+
|
|
1041
|
+
async requeueMessage(input) {
|
|
1042
|
+
assertNonEmptyString("id", input.id);
|
|
1043
|
+
const message = messages.get(input.id);
|
|
1044
|
+
if (!message) {
|
|
1045
|
+
throw new OutboxAdminError({
|
|
1046
|
+
id: input.id,
|
|
1047
|
+
message: `Outbox message "${input.id}" does not exist.`,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
if (message.status !== "deadLettered") {
|
|
1051
|
+
throw new OutboxAdminError({
|
|
1052
|
+
id: input.id,
|
|
1053
|
+
message: `Outbox message "${input.id}" is not dead-lettered.`,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const now = input.now ?? storeNow();
|
|
1058
|
+
const availableAt = input.availableAt ?? now;
|
|
1059
|
+
assertValidDate("now", now);
|
|
1060
|
+
assertValidDate("availableAt", availableAt);
|
|
1061
|
+
|
|
1062
|
+
message.status = "pending";
|
|
1063
|
+
message.availableAt = cloneDate(availableAt);
|
|
1064
|
+
message.claimToken = null;
|
|
1065
|
+
message.claimedAt = null;
|
|
1066
|
+
message.lockedUntil = null;
|
|
1067
|
+
if (input.resetAttempts) message.attempts = 0;
|
|
1068
|
+
message.updatedAt = cloneDate(now);
|
|
1069
|
+
|
|
1070
|
+
return copyMessage(message);
|
|
1071
|
+
},
|
|
1072
|
+
|
|
1073
|
+
async purgeDeadLettered(input = {}) {
|
|
1074
|
+
const limit =
|
|
1075
|
+
input.limit === undefined
|
|
1076
|
+
? undefined
|
|
1077
|
+
: resolveAdminListLimit(input.limit);
|
|
1078
|
+
if (input.before) assertValidDate("before", input.before);
|
|
1079
|
+
|
|
1080
|
+
const candidates = [...messages.values()]
|
|
1081
|
+
.filter(
|
|
1082
|
+
(message) =>
|
|
1083
|
+
message.status === "deadLettered" &&
|
|
1084
|
+
(input.before === undefined ||
|
|
1085
|
+
message.updatedAt.getTime() < input.before.getTime()),
|
|
1086
|
+
)
|
|
1087
|
+
.sort(compareMessagesForAdminCleanup)
|
|
1088
|
+
.slice(0, limit);
|
|
1089
|
+
|
|
1090
|
+
for (const message of candidates) {
|
|
1091
|
+
messages.delete(message.id);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return { deleted: candidates.length };
|
|
1095
|
+
},
|
|
1096
|
+
|
|
1097
|
+
async pruneDelivered(input) {
|
|
1098
|
+
assertValidDate("before", input.before);
|
|
1099
|
+
const limit =
|
|
1100
|
+
input.limit === undefined
|
|
1101
|
+
? undefined
|
|
1102
|
+
: resolveAdminListLimit(input.limit);
|
|
1103
|
+
const candidates = [...messages.values()]
|
|
1104
|
+
.filter(
|
|
1105
|
+
(message) =>
|
|
1106
|
+
message.status === "delivered" &&
|
|
1107
|
+
message.deliveredAt !== null &&
|
|
1108
|
+
message.deliveredAt.getTime() < input.before.getTime(),
|
|
1109
|
+
)
|
|
1110
|
+
.sort(compareDeliveredMessagesForPrune)
|
|
1111
|
+
.slice(0, limit);
|
|
1112
|
+
|
|
1113
|
+
for (const message of candidates) {
|
|
1114
|
+
messages.delete(message.id);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
return { deleted: candidates.length };
|
|
1118
|
+
},
|
|
1119
|
+
|
|
767
1120
|
async enqueue(input) {
|
|
768
1121
|
const message = createOutboxMessage(input, {
|
|
769
1122
|
createId: createStoreId,
|
|
@@ -1058,7 +1411,10 @@ async function deliverOutboxMessage(
|
|
|
1058
1411
|
}
|
|
1059
1412
|
)[SINGLE_ATTEMPT_DISPATCH];
|
|
1060
1413
|
if (singleAttempt) {
|
|
1061
|
-
await singleAttempt(job, payload
|
|
1414
|
+
await singleAttempt(job, payload, {
|
|
1415
|
+
attempt: message.attempts,
|
|
1416
|
+
maxAttempts: message.maxAttempts,
|
|
1417
|
+
});
|
|
1062
1418
|
return;
|
|
1063
1419
|
}
|
|
1064
1420
|
|
package/src/ports/events.ts
CHANGED
|
@@ -51,9 +51,9 @@ export type InferJobPayload<J extends JobDef> = InferContractJobPayload<J>;
|
|
|
51
51
|
*
|
|
52
52
|
* @example
|
|
53
53
|
* ```ts
|
|
54
|
-
* import {
|
|
54
|
+
* import { createMemoryEventBus } from "@beignet/provider-event-bus-memory";
|
|
55
55
|
*
|
|
56
|
-
* const eventBus =
|
|
56
|
+
* const eventBus = createMemoryEventBus();
|
|
57
57
|
*
|
|
58
58
|
* // Subscribe to an event
|
|
59
59
|
* const unsubscribe = eventBus.subscribe(UserRegistered, (payload) => {
|
package/src/ports/index.ts
CHANGED
|
@@ -224,7 +224,6 @@ export {
|
|
|
224
224
|
createMemoryFlags,
|
|
225
225
|
createStaticFlags,
|
|
226
226
|
defineFlag,
|
|
227
|
-
defineFlagRegistry,
|
|
228
227
|
defineFlags,
|
|
229
228
|
evaluateFlag,
|
|
230
229
|
getFlagDetails,
|
|
@@ -281,14 +280,22 @@ export type {
|
|
|
281
280
|
*/
|
|
282
281
|
export type {
|
|
283
282
|
ClaimedOutboxMessage,
|
|
283
|
+
OutboxAdminPort,
|
|
284
284
|
OutboxClaimBatchOptions,
|
|
285
|
+
OutboxCountMessagesOptions,
|
|
286
|
+
OutboxDeleteResult,
|
|
285
287
|
OutboxEnqueueInput,
|
|
288
|
+
OutboxListMessagesOptions,
|
|
286
289
|
OutboxMarkDeliveredInput,
|
|
287
290
|
OutboxMarkFailedInput,
|
|
288
291
|
OutboxMessage,
|
|
289
292
|
OutboxMessageKind,
|
|
293
|
+
OutboxMessageQuery,
|
|
290
294
|
OutboxMessageStatus,
|
|
291
295
|
OutboxPort,
|
|
296
|
+
OutboxPruneDeliveredInput,
|
|
297
|
+
OutboxPurgeDeadLetteredInput,
|
|
298
|
+
OutboxRequeueMessageInput,
|
|
292
299
|
} from "../outbox/index.js";
|
|
293
300
|
/**
|
|
294
301
|
* Payments port exports.
|
|
@@ -359,6 +366,15 @@ export {
|
|
|
359
366
|
SearchOptionsError,
|
|
360
367
|
searchDocuments,
|
|
361
368
|
} from "../search/index.js";
|
|
369
|
+
/**
|
|
370
|
+
* Tenant scope exports.
|
|
371
|
+
*/
|
|
372
|
+
export type { TenantScope } from "../tenancy/index.js";
|
|
373
|
+
export {
|
|
374
|
+
createTenantScope,
|
|
375
|
+
requireTenantScope,
|
|
376
|
+
tenantScopeId,
|
|
377
|
+
} from "../tenancy/index.js";
|
|
362
378
|
/**
|
|
363
379
|
* Webhook exports.
|
|
364
380
|
*
|
|
@@ -39,6 +39,10 @@ export interface BaseProviderInstrumentationEvent {
|
|
|
39
39
|
* W3C traceparent header value, when available.
|
|
40
40
|
*/
|
|
41
41
|
traceparent?: string;
|
|
42
|
+
/**
|
|
43
|
+
* W3C tracestate value associated with the current operation.
|
|
44
|
+
*/
|
|
45
|
+
tracestate?: string;
|
|
42
46
|
/**
|
|
43
47
|
* Watcher category used by devtools and instrumentation sinks.
|
|
44
48
|
*/
|
|
@@ -170,7 +170,7 @@ export interface ServiceProviderMetadata {
|
|
|
170
170
|
*
|
|
171
171
|
* @example
|
|
172
172
|
* ```ts
|
|
173
|
-
* const
|
|
173
|
+
* const cacheProvider = createProvider({
|
|
174
174
|
* name: "cache-redis",
|
|
175
175
|
* config: {
|
|
176
176
|
* schema: z.object({ URL: z.string().url() }),
|
package/src/schedules/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { runWithResolvedTracingContext } from "../tracing/execution.js";
|
|
3
|
+
import type { TracingPort } from "../tracing/index.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Any Standard Schema compatible validator.
|
|
@@ -134,9 +136,7 @@ export interface ScheduleHandleArgs<S extends ScheduleDef, Ctx> {
|
|
|
134
136
|
* Parsed schedule payload.
|
|
135
137
|
*/
|
|
136
138
|
payload: InferSchedulePayload<S>;
|
|
137
|
-
/**
|
|
138
|
-
* Handler context.
|
|
139
|
-
*/
|
|
139
|
+
/** Handler context. */
|
|
140
140
|
ctx: Ctx;
|
|
141
141
|
/**
|
|
142
142
|
* Run metadata.
|
|
@@ -219,10 +219,10 @@ export type ScheduleRunArgs<
|
|
|
219
219
|
Ctx,
|
|
220
220
|
Payload = unknown,
|
|
221
221
|
> = ScheduleRunOptions<Payload> & {
|
|
222
|
-
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
/** Handler context or factory resolved inside the schedule span. */
|
|
223
|
+
ctx: Ctx | (() => MaybePromise<Ctx>);
|
|
224
|
+
/** Runtime tracing port used before a lazy context factory runs. */
|
|
225
|
+
tracing?: TracingPort;
|
|
226
226
|
};
|
|
227
227
|
|
|
228
228
|
/**
|
|
@@ -374,6 +374,8 @@ export interface InlineScheduleRunnerOptions<Ctx> {
|
|
|
374
374
|
* Static schedule context or factory evaluated for each run.
|
|
375
375
|
*/
|
|
376
376
|
ctx?: Ctx | (() => MaybePromise<Ctx>);
|
|
377
|
+
/** Runtime tracing port used before a lazy context factory runs. */
|
|
378
|
+
tracing?: TracingPort;
|
|
377
379
|
/**
|
|
378
380
|
* Clock used when run timestamps are not provided.
|
|
379
381
|
*/
|
|
@@ -575,14 +577,33 @@ function createRunContext(
|
|
|
575
577
|
};
|
|
576
578
|
}
|
|
577
579
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
580
|
+
function scheduleTraceOperation(
|
|
581
|
+
schedule: ScheduleDef,
|
|
582
|
+
options: ScheduleRunOptions<unknown>,
|
|
583
|
+
) {
|
|
584
|
+
const attributes = {
|
|
585
|
+
"beignet.schedule.name": schedule.name,
|
|
586
|
+
...(options.attempt === undefined
|
|
587
|
+
? {}
|
|
588
|
+
: { "beignet.schedule.attempt": options.attempt }),
|
|
589
|
+
...(options.source === undefined
|
|
590
|
+
? {}
|
|
591
|
+
: { "beignet.schedule.source": options.source }),
|
|
592
|
+
} as const;
|
|
593
|
+
const metricAttributes = {
|
|
594
|
+
"beignet.schedule.name": schedule.name,
|
|
595
|
+
...(options.attempt === undefined
|
|
596
|
+
? {}
|
|
597
|
+
: { "beignet.schedule.attempt": options.attempt }),
|
|
598
|
+
} as const;
|
|
584
599
|
|
|
585
|
-
return
|
|
600
|
+
return {
|
|
601
|
+
name: `beignet.schedule ${schedule.name}`,
|
|
602
|
+
type: "schedule" as const,
|
|
603
|
+
kind: "consumer" as const,
|
|
604
|
+
attributes,
|
|
605
|
+
metricAttributes,
|
|
606
|
+
};
|
|
586
607
|
}
|
|
587
608
|
|
|
588
609
|
async function resolveSchedulePayload<S extends ScheduleDef>(
|
|
@@ -739,14 +760,21 @@ export async function runSchedule<
|
|
|
739
760
|
schedule: S,
|
|
740
761
|
args: ScheduleRunArgs<Ctx, InferSchedulePayload<S>>,
|
|
741
762
|
): Promise<void> {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
await schedule.handle({
|
|
746
|
-
schedule,
|
|
747
|
-
payload,
|
|
763
|
+
await runWithResolvedTracingContext({
|
|
764
|
+
tracing: args.tracing,
|
|
748
765
|
ctx: args.ctx,
|
|
749
|
-
|
|
766
|
+
operation: scheduleTraceOperation(schedule, args),
|
|
767
|
+
run: async (ctx) => {
|
|
768
|
+
const run = createRunContext(args, () => new Date());
|
|
769
|
+
const payload = await resolveSchedulePayload(schedule, args, run);
|
|
770
|
+
|
|
771
|
+
await schedule.handle({
|
|
772
|
+
schedule,
|
|
773
|
+
payload,
|
|
774
|
+
ctx,
|
|
775
|
+
run,
|
|
776
|
+
});
|
|
777
|
+
},
|
|
750
778
|
});
|
|
751
779
|
}
|
|
752
780
|
|
|
@@ -780,11 +808,17 @@ export function createInlineScheduleRunner<Ctx>(
|
|
|
780
808
|
options.onHookError,
|
|
781
809
|
lifecycleArgs,
|
|
782
810
|
);
|
|
783
|
-
await
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
run
|
|
811
|
+
await runWithResolvedTracingContext({
|
|
812
|
+
tracing: options.tracing,
|
|
813
|
+
ctx: options.ctx as Ctx | (() => MaybePromise<Ctx>),
|
|
814
|
+
operation: scheduleTraceOperation(schedule, runOptions),
|
|
815
|
+
run: (ctx) =>
|
|
816
|
+
schedule.handle({
|
|
817
|
+
schedule,
|
|
818
|
+
payload: payload as InferSchedulePayload<S>,
|
|
819
|
+
ctx,
|
|
820
|
+
run,
|
|
821
|
+
}),
|
|
788
822
|
});
|
|
789
823
|
await recordScheduleEvent(options, schedule, "completed", run, {
|
|
790
824
|
payload,
|
|
@@ -46,6 +46,16 @@ export {
|
|
|
46
46
|
type RateLimitIpSource,
|
|
47
47
|
type RateLimitOptions,
|
|
48
48
|
} from "./rate-limit.js";
|
|
49
|
+
export {
|
|
50
|
+
applySecurityHeaders,
|
|
51
|
+
type CsrfFailureReason,
|
|
52
|
+
type CsrfHooksOptions,
|
|
53
|
+
type CsrfTokenOptions,
|
|
54
|
+
createCsrfHooks,
|
|
55
|
+
createSecurityHeadersHooks,
|
|
56
|
+
type SecurityHeadersOptions,
|
|
57
|
+
type StrictTransportSecurityOptions,
|
|
58
|
+
} from "./security.js";
|
|
49
59
|
|
|
50
60
|
/**
|
|
51
61
|
* Flatten hook arrays into a single hook list.
|