@beignet/cli 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 +60 -0
- package/README.md +29 -19
- package/dist/choices.d.ts +3 -3
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +2 -0
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/db.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +279 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +800 -60
- package/dist/inspect.js.map +1 -1
- package/dist/make/inbox.d.ts.map +1 -1
- package/dist/make/inbox.js +8 -6
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +61 -32
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +11 -2
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +45 -11
- package/dist/make/shared.js.map +1 -1
- package/dist/make/tenancy.d.ts.map +1 -1
- package/dist/make/tenancy.js +9 -7
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +134 -60
- package/dist/make.js.map +1 -1
- package/dist/outbox.d.ts +114 -1
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +190 -0
- package/dist/outbox.js.map +1 -1
- package/dist/preflight.js +1 -1
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +91 -62
- package/dist/provider-add.js.map +1 -1
- package/dist/registry-edits.js +2 -1
- package/dist/registry-edits.js.map +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +2 -3
- package/dist/task.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +6 -3
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.js +6 -6
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +17 -10
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +3 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +11 -1
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.d.ts +5 -0
- package/dist/templates/testing.d.ts.map +1 -0
- package/dist/templates/testing.js +542 -0
- package/dist/templates/testing.js.map +1 -0
- package/dist/templates/todos.js +4 -4
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +14 -4
- package/src/choices.ts +3 -0
- package/src/config.ts +2 -0
- package/src/db.ts +1 -1
- package/src/index.ts +437 -1
- package/src/inspect.ts +1248 -156
- package/src/make/inbox.ts +8 -6
- package/src/make/payments.ts +61 -32
- package/src/make/shared.ts +79 -12
- package/src/make/tenancy.ts +9 -7
- package/src/make.ts +177 -59
- package/src/outbox.ts +363 -0
- package/src/preflight.ts +1 -1
- package/src/provider-add.ts +92 -62
- package/src/registry-edits.ts +2 -1
- package/src/task.ts +2 -3
- package/src/templates/agents.ts +6 -3
- package/src/templates/base.ts +6 -6
- package/src/templates/index.ts +2 -0
- package/src/templates/server.ts +17 -10
- package/src/templates/shared.ts +13 -1
- package/src/templates/testing.ts +545 -0
- package/src/templates/todos.ts +4 -4
- package/src/test-helpers/generated-app.ts +10 -6
package/src/outbox.ts
CHANGED
|
@@ -3,6 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import type {
|
|
4
4
|
DrainOutboxOptions,
|
|
5
5
|
DrainOutboxResult,
|
|
6
|
+
OutboxAdminPort,
|
|
7
|
+
OutboxMessage,
|
|
8
|
+
OutboxMessageKind,
|
|
9
|
+
OutboxMessageStatus,
|
|
6
10
|
OutboxRegistry,
|
|
7
11
|
} from "@beignet/core/outbox";
|
|
8
12
|
import { createJiti } from "jiti";
|
|
@@ -17,6 +21,61 @@ export type RunOutboxDrainOptions = {
|
|
|
17
21
|
batchSize?: number;
|
|
18
22
|
};
|
|
19
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Options for listing outbox messages.
|
|
26
|
+
*/
|
|
27
|
+
export type RunOutboxListOptions = {
|
|
28
|
+
cwd?: string;
|
|
29
|
+
modulePath?: string;
|
|
30
|
+
status?: OutboxMessageStatus;
|
|
31
|
+
kind?: OutboxMessageKind;
|
|
32
|
+
name?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for showing one outbox message.
|
|
38
|
+
*/
|
|
39
|
+
export type RunOutboxShowOptions = {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
modulePath?: string;
|
|
42
|
+
id: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Options for requeueing one dead-lettered outbox message.
|
|
47
|
+
*/
|
|
48
|
+
export type RunOutboxRequeueOptions = {
|
|
49
|
+
cwd?: string;
|
|
50
|
+
modulePath?: string;
|
|
51
|
+
id: string;
|
|
52
|
+
availableAt?: Date;
|
|
53
|
+
resetAttempts?: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options for purging dead-lettered outbox messages.
|
|
58
|
+
*/
|
|
59
|
+
export type RunOutboxPurgeOptions = {
|
|
60
|
+
cwd?: string;
|
|
61
|
+
modulePath?: string;
|
|
62
|
+
before?: Date;
|
|
63
|
+
all?: boolean;
|
|
64
|
+
limit?: number;
|
|
65
|
+
dryRun?: boolean;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Options for pruning delivered outbox messages.
|
|
70
|
+
*/
|
|
71
|
+
export type RunOutboxPruneOptions = {
|
|
72
|
+
cwd?: string;
|
|
73
|
+
modulePath?: string;
|
|
74
|
+
before: Date;
|
|
75
|
+
limit?: number;
|
|
76
|
+
dryRun?: boolean;
|
|
77
|
+
};
|
|
78
|
+
|
|
20
79
|
/**
|
|
21
80
|
* Result returned by `beignet outbox drain`.
|
|
22
81
|
*/
|
|
@@ -28,6 +87,53 @@ export type RunOutboxDrainResult = {
|
|
|
28
87
|
durationMs: number;
|
|
29
88
|
};
|
|
30
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Result returned by `beignet outbox list`.
|
|
92
|
+
*/
|
|
93
|
+
export type RunOutboxListResult = {
|
|
94
|
+
schemaVersion: 1;
|
|
95
|
+
cwd: string;
|
|
96
|
+
modulePath: string;
|
|
97
|
+
messages: readonly OutboxMessage[];
|
|
98
|
+
count: number;
|
|
99
|
+
durationMs: number;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Result returned by `beignet outbox show`.
|
|
104
|
+
*/
|
|
105
|
+
export type RunOutboxShowResult = {
|
|
106
|
+
schemaVersion: 1;
|
|
107
|
+
cwd: string;
|
|
108
|
+
modulePath: string;
|
|
109
|
+
message: OutboxMessage;
|
|
110
|
+
durationMs: number;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Result returned by `beignet outbox requeue`.
|
|
115
|
+
*/
|
|
116
|
+
export type RunOutboxRequeueResult = {
|
|
117
|
+
schemaVersion: 1;
|
|
118
|
+
cwd: string;
|
|
119
|
+
modulePath: string;
|
|
120
|
+
message: OutboxMessage;
|
|
121
|
+
durationMs: number;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Result returned by destructive outbox admin commands.
|
|
126
|
+
*/
|
|
127
|
+
export type RunOutboxDeleteResult = {
|
|
128
|
+
schemaVersion: 1;
|
|
129
|
+
cwd: string;
|
|
130
|
+
modulePath: string;
|
|
131
|
+
dryRun: boolean;
|
|
132
|
+
matched: number;
|
|
133
|
+
deleted: number;
|
|
134
|
+
durationMs: number;
|
|
135
|
+
};
|
|
136
|
+
|
|
31
137
|
type OutboxDrainLogger = {
|
|
32
138
|
error(message: string, meta?: Record<string, unknown>): unknown;
|
|
33
139
|
};
|
|
@@ -47,6 +153,7 @@ type OutboxDrainInstrumentation = {
|
|
|
47
153
|
|
|
48
154
|
type OutboxDrainPorts = {
|
|
49
155
|
outbox: DrainOutboxOptions["outbox"];
|
|
156
|
+
outboxAdmin?: OutboxAdminPort;
|
|
50
157
|
eventBus?: DrainOutboxOptions["eventBus"];
|
|
51
158
|
jobs?: DrainOutboxOptions["jobs"];
|
|
52
159
|
logger?: OutboxDrainLogger;
|
|
@@ -65,13 +172,24 @@ type OutboxDrainContextArgs = {
|
|
|
65
172
|
batchSize?: number;
|
|
66
173
|
};
|
|
67
174
|
|
|
175
|
+
type OutboxAdminOperation = "list" | "show" | "requeue" | "purge" | "prune";
|
|
176
|
+
|
|
177
|
+
type OutboxAdminContextArgs = {
|
|
178
|
+
operation: OutboxAdminOperation;
|
|
179
|
+
};
|
|
180
|
+
|
|
68
181
|
type OutboxModule = {
|
|
69
182
|
outboxRegistry?: unknown;
|
|
70
183
|
createOutboxDrainContext?: (args: OutboxDrainContextArgs) => unknown;
|
|
184
|
+
createOutboxAdminContext?: (args: OutboxAdminContextArgs) => unknown;
|
|
71
185
|
stopOutboxDrainContext?: (
|
|
72
186
|
ctx: OutboxDrainContext,
|
|
73
187
|
args: OutboxDrainContextArgs,
|
|
74
188
|
) => unknown;
|
|
189
|
+
stopOutboxAdminContext?: (
|
|
190
|
+
ctx: OutboxDrainContext,
|
|
191
|
+
args: OutboxAdminContextArgs,
|
|
192
|
+
) => unknown;
|
|
75
193
|
};
|
|
76
194
|
|
|
77
195
|
/**
|
|
@@ -138,6 +256,190 @@ export async function runOutboxDrain(
|
|
|
138
256
|
}
|
|
139
257
|
}
|
|
140
258
|
|
|
259
|
+
/**
|
|
260
|
+
* List outbox messages through the app's outbox admin port.
|
|
261
|
+
*/
|
|
262
|
+
export async function runOutboxList(
|
|
263
|
+
options: RunOutboxListOptions = {},
|
|
264
|
+
): Promise<RunOutboxListResult> {
|
|
265
|
+
return withOutboxAdminContext(options, "list", async ({ admin, base }) => {
|
|
266
|
+
const query = {
|
|
267
|
+
status: options.status,
|
|
268
|
+
kind: options.kind,
|
|
269
|
+
name: options.name,
|
|
270
|
+
};
|
|
271
|
+
const messages = await admin.listMessages({
|
|
272
|
+
...query,
|
|
273
|
+
limit: options.limit,
|
|
274
|
+
});
|
|
275
|
+
const count = await admin.countMessages(query);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
...base,
|
|
279
|
+
messages,
|
|
280
|
+
count,
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Show one outbox message through the app's outbox admin port.
|
|
287
|
+
*/
|
|
288
|
+
export async function runOutboxShow(
|
|
289
|
+
options: RunOutboxShowOptions,
|
|
290
|
+
): Promise<RunOutboxShowResult> {
|
|
291
|
+
return withOutboxAdminContext(options, "show", async ({ admin, base }) => {
|
|
292
|
+
const message = await admin.getMessage(options.id);
|
|
293
|
+
if (!message) {
|
|
294
|
+
throw new Error(`Outbox message "${options.id}" does not exist.`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
...base,
|
|
299
|
+
message,
|
|
300
|
+
};
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Requeue one dead-lettered outbox message.
|
|
306
|
+
*/
|
|
307
|
+
export async function runOutboxRequeue(
|
|
308
|
+
options: RunOutboxRequeueOptions,
|
|
309
|
+
): Promise<RunOutboxRequeueResult> {
|
|
310
|
+
return withOutboxAdminContext(options, "requeue", async ({ admin, base }) => {
|
|
311
|
+
const message = await admin.requeueMessage({
|
|
312
|
+
id: options.id,
|
|
313
|
+
availableAt: options.availableAt,
|
|
314
|
+
resetAttempts: options.resetAttempts,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
...base,
|
|
319
|
+
message,
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Purge dead-lettered outbox messages.
|
|
326
|
+
*/
|
|
327
|
+
export async function runOutboxPurge(
|
|
328
|
+
options: RunOutboxPurgeOptions = {},
|
|
329
|
+
): Promise<RunOutboxDeleteResult> {
|
|
330
|
+
if (!options.before && !options.all) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
"Pass --before <date> to purge old dead-lettered messages, or pass --all to purge every dead-lettered message.",
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return withOutboxAdminContext(options, "purge", async ({ admin, base }) => {
|
|
337
|
+
const query = {
|
|
338
|
+
status: "deadLettered" as const,
|
|
339
|
+
updatedBefore: options.before,
|
|
340
|
+
};
|
|
341
|
+
const matched = await admin.countMessages(query);
|
|
342
|
+
const result = options.dryRun
|
|
343
|
+
? { deleted: dryRunDeleteCount(matched, options.limit) }
|
|
344
|
+
: await admin.purgeDeadLettered({
|
|
345
|
+
before: options.before,
|
|
346
|
+
limit: options.limit,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
...base,
|
|
351
|
+
dryRun: Boolean(options.dryRun),
|
|
352
|
+
matched,
|
|
353
|
+
deleted: result.deleted,
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Prune delivered outbox messages.
|
|
360
|
+
*/
|
|
361
|
+
export async function runOutboxPrune(
|
|
362
|
+
options: RunOutboxPruneOptions,
|
|
363
|
+
): Promise<RunOutboxDeleteResult> {
|
|
364
|
+
return withOutboxAdminContext(options, "prune", async ({ admin, base }) => {
|
|
365
|
+
const query = {
|
|
366
|
+
status: "delivered" as const,
|
|
367
|
+
deliveredBefore: options.before,
|
|
368
|
+
};
|
|
369
|
+
const matched = await admin.countMessages(query);
|
|
370
|
+
const result = options.dryRun
|
|
371
|
+
? { deleted: dryRunDeleteCount(matched, options.limit) }
|
|
372
|
+
: await admin.pruneDelivered({
|
|
373
|
+
before: options.before,
|
|
374
|
+
limit: options.limit,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
...base,
|
|
379
|
+
dryRun: Boolean(options.dryRun),
|
|
380
|
+
matched,
|
|
381
|
+
deleted: result.deleted,
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function withOutboxAdminContext<T extends object>(
|
|
387
|
+
options: { cwd?: string; modulePath?: string },
|
|
388
|
+
operation: OutboxAdminOperation,
|
|
389
|
+
run: (args: {
|
|
390
|
+
admin: OutboxAdminPort;
|
|
391
|
+
base: {
|
|
392
|
+
schemaVersion: 1;
|
|
393
|
+
cwd: string;
|
|
394
|
+
modulePath: string;
|
|
395
|
+
};
|
|
396
|
+
}) => Promise<T>,
|
|
397
|
+
): Promise<T & { durationMs: number }> {
|
|
398
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
399
|
+
const config = await loadBeignetConfig(cwd);
|
|
400
|
+
const modulePath = normalizePath(options.modulePath ?? config.paths.outbox);
|
|
401
|
+
const startedAt = performance.now();
|
|
402
|
+
const outboxModule = await loadOutboxModule(cwd, modulePath);
|
|
403
|
+
const contextArgs = { operation };
|
|
404
|
+
const rawContext = outboxModule.createOutboxAdminContext
|
|
405
|
+
? await outboxModule.createOutboxAdminContext(contextArgs)
|
|
406
|
+
: await createFallbackOutboxAdminContext(outboxModule);
|
|
407
|
+
const ctx = assertOutboxAdminContext(rawContext, modulePath);
|
|
408
|
+
const admin = resolveOutboxAdminPort(ctx, modulePath);
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
const result = await run({
|
|
412
|
+
admin,
|
|
413
|
+
base: {
|
|
414
|
+
schemaVersion: 1,
|
|
415
|
+
cwd,
|
|
416
|
+
modulePath,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
return {
|
|
420
|
+
...result,
|
|
421
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
422
|
+
};
|
|
423
|
+
} finally {
|
|
424
|
+
if (outboxModule.stopOutboxAdminContext) {
|
|
425
|
+
await outboxModule.stopOutboxAdminContext(ctx, contextArgs);
|
|
426
|
+
} else {
|
|
427
|
+
await outboxModule.stopOutboxDrainContext?.(ctx, {
|
|
428
|
+
registry: maybeOutboxRegistry(outboxModule.outboxRegistry),
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async function createFallbackOutboxAdminContext(
|
|
435
|
+
outboxModule: OutboxModule,
|
|
436
|
+
): Promise<unknown> {
|
|
437
|
+
if (!outboxModule.createOutboxDrainContext) return undefined;
|
|
438
|
+
return outboxModule.createOutboxDrainContext({
|
|
439
|
+
registry: maybeOutboxRegistry(outboxModule.outboxRegistry),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
141
443
|
async function loadOutboxModule(
|
|
142
444
|
cwd: string,
|
|
143
445
|
modulePath: string,
|
|
@@ -197,6 +499,19 @@ function assertOutboxDrainContext(
|
|
|
197
499
|
return ctx as OutboxDrainContext;
|
|
198
500
|
}
|
|
199
501
|
|
|
502
|
+
function assertOutboxAdminContext(
|
|
503
|
+
ctx: unknown,
|
|
504
|
+
modulePath: string,
|
|
505
|
+
): OutboxDrainContext {
|
|
506
|
+
if (!isRecord(ctx) || !isRecord(ctx.ports)) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`${modulePath} must export createOutboxAdminContext or createOutboxDrainContext that returns an app context with ports.outboxAdmin.`,
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return ctx as OutboxDrainContext;
|
|
513
|
+
}
|
|
514
|
+
|
|
200
515
|
function isOutboxPort(value: unknown): value is DrainOutboxOptions["outbox"] {
|
|
201
516
|
return (
|
|
202
517
|
isRecord(value) &&
|
|
@@ -207,6 +522,48 @@ function isOutboxPort(value: unknown): value is DrainOutboxOptions["outbox"] {
|
|
|
207
522
|
);
|
|
208
523
|
}
|
|
209
524
|
|
|
525
|
+
function isOutboxAdminPort(value: unknown): value is OutboxAdminPort {
|
|
526
|
+
return (
|
|
527
|
+
isRecord(value) &&
|
|
528
|
+
typeof value.listMessages === "function" &&
|
|
529
|
+
typeof value.countMessages === "function" &&
|
|
530
|
+
typeof value.getMessage === "function" &&
|
|
531
|
+
typeof value.requeueMessage === "function" &&
|
|
532
|
+
typeof value.purgeDeadLettered === "function" &&
|
|
533
|
+
typeof value.pruneDelivered === "function"
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function resolveOutboxAdminPort(
|
|
538
|
+
ctx: OutboxDrainContext,
|
|
539
|
+
modulePath: string,
|
|
540
|
+
): OutboxAdminPort {
|
|
541
|
+
if (isOutboxAdminPort(ctx.ports.outboxAdmin)) return ctx.ports.outboxAdmin;
|
|
542
|
+
if (isOutboxAdminPort(ctx.ports.outbox)) return ctx.ports.outbox;
|
|
543
|
+
|
|
544
|
+
throw new Error(
|
|
545
|
+
`${modulePath} createOutboxAdminContext must return ports.outboxAdmin, or ports.outbox must implement OutboxAdminPort.`,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function maybeOutboxRegistry(registry: unknown): OutboxRegistry {
|
|
550
|
+
if (
|
|
551
|
+
isRecord(registry) &&
|
|
552
|
+
registry.events instanceof Map &&
|
|
553
|
+
registry.jobs instanceof Map
|
|
554
|
+
) {
|
|
555
|
+
return {
|
|
556
|
+
events: registry.events,
|
|
557
|
+
jobs: registry.jobs,
|
|
558
|
+
} as unknown as OutboxRegistry;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
events: new Map(),
|
|
563
|
+
jobs: new Map(),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
210
567
|
async function recordOutboxDrain(
|
|
211
568
|
ctx: OutboxDrainContext,
|
|
212
569
|
result: DrainOutboxResult,
|
|
@@ -235,6 +592,12 @@ function assertPositiveInteger(name: string, value: number): void {
|
|
|
235
592
|
}
|
|
236
593
|
}
|
|
237
594
|
|
|
595
|
+
function dryRunDeleteCount(matched: number, limit: number | undefined): number {
|
|
596
|
+
if (limit === undefined) return matched;
|
|
597
|
+
assertPositiveInteger("limit", limit);
|
|
598
|
+
return Math.min(matched, limit);
|
|
599
|
+
}
|
|
600
|
+
|
|
238
601
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
239
602
|
return typeof value === "object" && value !== null;
|
|
240
603
|
}
|
package/src/preflight.ts
CHANGED
|
@@ -283,7 +283,7 @@ export async function runPreflight(
|
|
|
283
283
|
severity: "note",
|
|
284
284
|
code: "BEIGNET_PREFLIGHT_MANUAL_REVIEW",
|
|
285
285
|
message:
|
|
286
|
-
"Manual review
|
|
286
|
+
"Manual review: CSRF policy, CSP/HSTS values, trusted proxy topology, and tracing export — see the deployment docs hardening checklist.",
|
|
287
287
|
});
|
|
288
288
|
|
|
289
289
|
let envModuleChecked = false;
|