@fedify/lint 2.4.0-pr.934.40 → 2.4.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.
@@ -0,0 +1,2706 @@
1
+ let _fxts_core = require("@fxts/core");
2
+ //#region deno.json
3
+ var name = "@fedify/lint";
4
+ var version = "2.4.0";
5
+ //#endregion
6
+ //#region src/lib/const.ts
7
+ /**
8
+ * Mapping of actor property names to their corresponding Context method names
9
+ * and dispatcher methods.
10
+ * Used by lint rules to validate property existence and correct method usage.
11
+ */
12
+ const properties = {
13
+ id: {
14
+ name: "id",
15
+ path: ["id"],
16
+ getter: "getActorUri",
17
+ portableGetter: "getPortableActorUri",
18
+ setter: "setActorDispatcher",
19
+ requiresIdentifier: true
20
+ },
21
+ following: {
22
+ name: "following",
23
+ path: ["following"],
24
+ getter: "getFollowingUri",
25
+ portableGetter: "getPortableFollowingUri",
26
+ setter: "setFollowingDispatcher",
27
+ requiresIdentifier: true
28
+ },
29
+ followers: {
30
+ name: "followers",
31
+ path: ["followers"],
32
+ getter: "getFollowersUri",
33
+ portableGetter: "getPortableFollowersUri",
34
+ setter: "setFollowersDispatcher",
35
+ requiresIdentifier: true
36
+ },
37
+ outbox: {
38
+ name: "outbox",
39
+ path: ["outbox"],
40
+ getter: "getOutboxUri",
41
+ portableGetter: "getPortableOutboxUri",
42
+ setter: "setOutboxDispatcher",
43
+ requiresIdentifier: true
44
+ },
45
+ inbox: {
46
+ name: "inbox",
47
+ path: ["inbox"],
48
+ getter: "getInboxUri",
49
+ portableGetter: "getPortableInboxUri",
50
+ setter: "setInboxListeners",
51
+ requiresIdentifier: true
52
+ },
53
+ liked: {
54
+ name: "liked",
55
+ path: ["liked"],
56
+ getter: "getLikedUri",
57
+ portableGetter: "getPortableLikedUri",
58
+ setter: "setLikedDispatcher",
59
+ requiresIdentifier: true
60
+ },
61
+ featured: {
62
+ name: "featured",
63
+ path: ["featured"],
64
+ getter: "getFeaturedUri",
65
+ portableGetter: "getPortableFeaturedUri",
66
+ setter: "setFeaturedDispatcher",
67
+ requiresIdentifier: true
68
+ },
69
+ featuredTags: {
70
+ name: "featuredTags",
71
+ path: ["featuredTags"],
72
+ getter: "getFeaturedTagsUri",
73
+ portableGetter: "getPortableFeaturedTagsUri",
74
+ setter: "setFeaturedTagsDispatcher",
75
+ requiresIdentifier: true
76
+ },
77
+ sharedInbox: {
78
+ name: "sharedInbox",
79
+ path: ["endpoints", "sharedInbox"],
80
+ getter: "getInboxUri",
81
+ setter: "setInboxListeners",
82
+ requiresIdentifier: false,
83
+ nested: {
84
+ parent: "endpoints",
85
+ wrapper: "Endpoints"
86
+ }
87
+ },
88
+ uploadMedia: {
89
+ name: "uploadMedia",
90
+ path: ["endpoints", "uploadMedia"],
91
+ getter: "getMediaUploaderUri",
92
+ setter: "setMediaUploader",
93
+ requiresIdentifier: true,
94
+ nested: {
95
+ parent: "endpoints",
96
+ wrapper: "Endpoints"
97
+ }
98
+ },
99
+ publicKey: {
100
+ name: "publicKey",
101
+ path: ["publicKey"],
102
+ getter: "getActorKeyPairs",
103
+ setter: "setKeyPairsDispatcher",
104
+ requiresIdentifier: true,
105
+ isKeyProperty: true
106
+ },
107
+ assertionMethod: {
108
+ name: "assertionMethod",
109
+ path: ["assertionMethod"],
110
+ getter: "getActorKeyPairs",
111
+ setter: "setKeyPairsDispatcher",
112
+ requiresIdentifier: true,
113
+ isKeyProperty: true
114
+ },
115
+ preferredUsername: {
116
+ name: "preferredUsername",
117
+ path: ["preferredUsername"],
118
+ setter: "setActorDispatcher",
119
+ requiresIdentifier: false,
120
+ pluralName: "preferredUsernames"
121
+ }
122
+ };
123
+ /**
124
+ * Rule IDs for all Fedify lint rules
125
+ */
126
+ const RULE_IDS = {
127
+ actorIdRequired: "actor-id-required",
128
+ actorFollowingPropertyRequired: "actor-following-property-required",
129
+ actorFollowersPropertyRequired: "actor-followers-property-required",
130
+ actorOutboxPropertyRequired: "actor-outbox-property-required",
131
+ actorLikedPropertyRequired: "actor-liked-property-required",
132
+ actorFeaturedPropertyRequired: "actor-featured-property-required",
133
+ actorFeaturedTagsPropertyRequired: "actor-featured-tags-property-required",
134
+ actorInboxPropertyRequired: "actor-inbox-property-required",
135
+ actorSharedInboxPropertyRequired: "actor-shared-inbox-property-required",
136
+ actorUploadMediaPropertyRequired: "actor-upload-media-property-required",
137
+ actorPublicKeyRequired: "actor-public-key-required",
138
+ actorAssertionMethodRequired: "actor-assertion-method-required",
139
+ actorPreferredUsernameRequired: "actor-preferred-username-required",
140
+ actorIdMismatch: "actor-id-mismatch",
141
+ actorFollowingPropertyMismatch: "actor-following-property-mismatch",
142
+ actorFollowersPropertyMismatch: "actor-followers-property-mismatch",
143
+ actorOutboxPropertyMismatch: "actor-outbox-property-mismatch",
144
+ actorLikedPropertyMismatch: "actor-liked-property-mismatch",
145
+ actorFeaturedPropertyMismatch: "actor-featured-property-mismatch",
146
+ actorFeaturedTagsPropertyMismatch: "actor-featured-tags-property-mismatch",
147
+ actorInboxPropertyMismatch: "actor-inbox-property-mismatch",
148
+ actorSharedInboxPropertyMismatch: "actor-shared-inbox-property-mismatch",
149
+ actorUploadMediaPropertyMismatch: "actor-upload-media-property-mismatch",
150
+ collectionFilteringNotImplemented: "collection-filtering-not-implemented",
151
+ outboxListenerDeliveryRequired: "outbox-listener-delivery-required",
152
+ outboxListenerDeliveryNotAwaited: "outbox-listener-delivery-not-awaited",
153
+ mediaUploaderObjectUriRequired: "media-uploader-object-uri-required",
154
+ mediaUploaderAuthorizationRequired: "media-uploader-authorization-required"
155
+ };
156
+ //#endregion
157
+ //#region src/lib/messages.ts
158
+ /**
159
+ * Generates error message for *-required rules.
160
+ * Used when a property is missing from the actor dispatcher return value.
161
+ *
162
+ * @param name - The property name (e.g., "id", "inbox", "following")
163
+ */
164
+ const actorPropertyRequired = ({ setter, path, getter, requiresIdentifier = true }) => {
165
+ const propertyPath = path.join(".");
166
+ return `When \`${setter}\` is configured, the \`${propertyPath}\` property is recommended. ${getter == null ? `Set the \`${propertyPath}\` property directly on the actor object.` : `Use \`${getExpectedCall({
167
+ ctxName: "Context",
168
+ methodName: getter,
169
+ idName: "identifier",
170
+ path: path.join("."),
171
+ requiresIdentifier
172
+ })}\` for the \`${propertyPath}\` property URI.`}`;
173
+ };
174
+ /**
175
+ * Generates error message for *-mismatch rules.
176
+ * Used when a property exists but uses the wrong context method.
177
+ *
178
+ * @param propertyName - The property name or path
179
+ * (e.g., "id", "endpoints.sharedInbox")
180
+ * @param expectedCall - The expected method call
181
+ * (e.g., "ctx.getActorUri(identifier)")
182
+ */
183
+ const actorPropertyMismatch = (context) => `Actor's \`${context.path}\` property must match \`${getExpectedCall(context)}\`. \
184
+ Ensure you're using the correct context method.`;
185
+ const getExpectedCall = ({ ctxName, methodName, requiresIdentifier, idName }) => requiresIdentifier ? `${ctxName}.${methodName}(${idName})` : `${ctxName}.${methodName}()`;
186
+ /**
187
+ * Error message for collection filtering not implemented.
188
+ */
189
+ const COLLECTION_FILTERING_NOT_IMPLEMENTED_ERROR = "Collection dispatcher should implement filtering to avoid large response payloads. Add a fourth parameter (filter) to handle filtering. See: https://fedify.dev/manual/collections#filtering-by-server";
190
+ //#endregion
191
+ //#region src/lib/utils.ts
192
+ const eq = (value) => (other) => value === other;
193
+ function cases(pred, ifTrue, ifFalse) {
194
+ return (value) => pred(value) ? ifTrue(value) : ifFalse(value);
195
+ }
196
+ //#endregion
197
+ //#region src/lib/pred.ts
198
+ function allOf(...predicates) {
199
+ return (value) => predicates.every((predicate) => predicate(value));
200
+ }
201
+ const anyOf = (...predicates) => (value) => predicates.some((predicate) => predicate(value));
202
+ const isNode = (obj) => (0, _fxts_core.isObject)(obj) && "type" in obj;
203
+ /**
204
+ * Checks if a node is of a specific type.
205
+ */
206
+ const isNodeType = (type) => (node) => (0, _fxts_core.pipe)(node, (0, _fxts_core.prop)("type"), eq(type));
207
+ /**
208
+ * Checks if a node is of a specific name.
209
+ */
210
+ const isNodeName = (name) => (node) => (0, _fxts_core.pipe)(node, (0, _fxts_core.prop)("name"), eq(name));
211
+ /**
212
+ * Checks if a node's callee is a MemberExpression.
213
+ */
214
+ const hasMemberExpressionCallee = (node) => node.callee.type === "MemberExpression";
215
+ /**
216
+ * Checks if a node's callee property is an Identifier.
217
+ */
218
+ const hasIdentifierProperty = (node) => "callee" in node && "property" in node.callee && "type" in node.callee.property && node.callee.property.type === "Identifier";
219
+ /**
220
+ * Checks if a node's callee property name matches the given method name.
221
+ */
222
+ const hasMethodName = (methodName) => (node) => "callee" in node && "property" in node.callee && "name" in node.callee.property && node.callee.property.name === methodName;
223
+ /**
224
+ * Checks if a CallExpression has minimum required arguments.
225
+ */
226
+ const hasMinArguments = (min) => (node) => node.arguments.length >= min;
227
+ /**
228
+ * Checks if an expression is a function (arrow or regular).
229
+ */
230
+ const isFunction = (expr) => anyOf(isNodeType("ArrowFunctionExpression"), isNodeType("FunctionExpression"))(expr);
231
+ /**
232
+ * Checks if a CallExpression is a setActorDispatcher call with
233
+ * proper structure.
234
+ */
235
+ const isSetActorDispatcherCall = (node) => allOf(hasMemberExpressionCallee, hasIdentifierProperty, hasMethodName("setActorDispatcher"), hasMinArguments(2))(node);
236
+ /**
237
+ * Checks if a function has at least n parameters.
238
+ */
239
+ const hasMinParams = (min) => (fn) => fn.params.length >= min;
240
+ //#endregion
241
+ //#region src/lib/property-checker.ts
242
+ /**
243
+ * Checks if a node has a key with a specific name.
244
+ */
245
+ const hasKeyName = (propertyName) => (node) => (0, _fxts_core.pipe)(node, (0, _fxts_core.prop)("key"), allOf(isNodeType("Identifier"), (0, _fxts_core.pipeLazy)((0, _fxts_core.prop)("name"), eq(propertyName))));
246
+ /**
247
+ * Checks if a node is a Property with an Identifier key of a specific name.
248
+ */
249
+ const isPropertyWithName = (propertyName) => (node) => allOf(isNodeType("Property"), hasKeyName(propertyName))(node);
250
+ /**
251
+ * Creates a predicate function that checks if a nested property exists.
252
+ * @param path Array of property names forming the path
253
+ * (e.g., ["endpoints", "sharedInbox"])
254
+ * @returns A predicate function that checks if the nested property exists
255
+ */
256
+ function createPropertyChecker(checker) {
257
+ const inner = ([first, ...rest]) => (node) => {
258
+ if (!isPropertyWithName(first)(node)) return false;
259
+ if ((0, _fxts_core.isEmpty)(rest)) return checker(node.value);
260
+ if (isNodeType("NewExpression")(node.value)) {
261
+ if (node.value.arguments.length === 0) return false;
262
+ const firstArg = node.value.arguments[0];
263
+ if (!isNodeType("ObjectExpression")(firstArg)) return false;
264
+ return firstArg.properties.some(inner(rest));
265
+ }
266
+ return false;
267
+ };
268
+ return inner;
269
+ }
270
+ /**
271
+ * Checks if an ObjectExpression node contains a property.
272
+ * @param propertyChecker The predicate function to check properties
273
+ * @returns A function that checks the ObjectExpression
274
+ */
275
+ const checkObjectExpression = (propertyChecker) => (obj) => obj.properties.some(propertyChecker);
276
+ /**
277
+ * Checks if a ConditionalExpression (ternary operator) has the property in
278
+ * both branches.
279
+ * @param propertyChecker The predicate function to check properties
280
+ * @returns A function that checks the ConditionalExpression
281
+ */
282
+ const checkConditionalExpression = (propertyChecker) => (node) => [node.consequent, node.alternate].every(checkBranchWith(propertyChecker));
283
+ const unwrapTypeScriptExpression = (node) => {
284
+ switch (node.type) {
285
+ case "TSAsExpression":
286
+ case "TSSatisfiesExpression":
287
+ case "TSNonNullExpression":
288
+ case "TSTypeAssertion": return unwrapTypeScriptExpression(node.expression);
289
+ default: return node;
290
+ }
291
+ };
292
+ const isNullLiteral = (node) => node.type === "Literal" && node.value === null;
293
+ const isTombstoneExpression = (node) => node.type === "NewExpression" && node.callee.type === "Identifier" && node.callee.name === "Tombstone";
294
+ const checkBranchWith = (propertyChecker) => (branch) => {
295
+ const expression = unwrapTypeScriptExpression(branch);
296
+ if (isNullLiteral(expression) || isTombstoneExpression(expression)) return true;
297
+ return (0, _fxts_core.pipe)(expression, cases(isNodeType("ConditionalExpression"), checkConditionalExpression(propertyChecker), (0, _fxts_core.pipeLazy)(extractObjectExpression, cases(_fxts_core.isObject, checkObjectExpression(propertyChecker), (0, _fxts_core.always)(false)))));
298
+ };
299
+ /**
300
+ * Extracts the first argument if it's an ObjectExpression.
301
+ */
302
+ const extractFirstObjectExpression = (node) => (0, _fxts_core.pipe)(node, (0, _fxts_core.prop)("arguments"), _fxts_core.head, (0, _fxts_core.unless)(isNodeType("ObjectExpression"), (0, _fxts_core.always)(null)));
303
+ /**
304
+ * Extracts ObjectExpression from NewExpression.
305
+ */
306
+ const extractObjectExpression = cases(isNodeType("NewExpression"), extractFirstObjectExpression, (0, _fxts_core.always)(null));
307
+ /**
308
+ * Checks if a ReturnStatement node contains a property.
309
+ * @param propertyChecker The predicate function to check properties
310
+ * @returns A function that checks the ReturnStatement
311
+ */
312
+ const checkReturnStatement = (propertyChecker) => (node) => (0, _fxts_core.pipe)(node, (0, _fxts_core.prop)("argument"), cases(_fxts_core.isObject, checkBranchWith(propertyChecker), (0, _fxts_core.always)(false)));
313
+ /**
314
+ * Creates a function that recursively checks for a property in an AST node.
315
+ * @param propertyChecker The predicate function to check properties
316
+ * @returns A recursive function that checks the AST node
317
+ */
318
+ const createPropertySearcher = (propertyChecker) => {
319
+ return (node) => {
320
+ switch (node.type) {
321
+ case "ReturnStatement": return checkReturnStatement(propertyChecker)(node);
322
+ case "BlockStatement": return checkAllReturnPaths(propertyChecker)(node);
323
+ case "NewExpression":
324
+ if (isTombstoneExpression(node)) return true;
325
+ return (0, _fxts_core.pipe)(node, extractFirstObjectExpression, (0, _fxts_core.when)(_fxts_core.isObject, checkObjectExpression(propertyChecker)), Boolean);
326
+ default: return checkBranchWith(propertyChecker)(node);
327
+ }
328
+ };
329
+ };
330
+ const checkAllReturnPaths = (propertyChecker) => (node) => (0, _fxts_core.pipe)(node, collectReturnPaths, cases(_fxts_core.isEmpty, (0, _fxts_core.always)(false), (0, _fxts_core.every)(checkReturnStatement(propertyChecker))));
331
+ /**
332
+ * Collects all return statements from a node, traversing control flow.
333
+ * This handles if/else branches, loops, etc.
334
+ */
335
+ const collectReturnPaths = (node) => (0, _fxts_core.pipe)(node, flatten, _fxts_core.toArray);
336
+ function* flatten(node) {
337
+ switch (node.type) {
338
+ case "ReturnStatement":
339
+ yield node;
340
+ return;
341
+ case "IfStatement":
342
+ if (node.consequent) yield* flatten(node.consequent);
343
+ if (node.alternate) yield* flatten(node.alternate);
344
+ return;
345
+ case "BlockStatement":
346
+ for (const statement of node.body) yield* flatten(statement);
347
+ return;
348
+ case "SwitchStatement":
349
+ for (const switchCase of node.cases) for (const statement of switchCase.consequent) yield* flatten(statement);
350
+ return;
351
+ case "TryStatement":
352
+ yield* flatten(node.block);
353
+ if (node.handler) yield* flatten(node.handler.body);
354
+ if (node.finalizer) yield* flatten(node.finalizer);
355
+ return;
356
+ case "WhileStatement":
357
+ case "DoWhileStatement":
358
+ case "ForStatement":
359
+ case "ForInStatement":
360
+ case "ForOfStatement":
361
+ yield* flatten(node.body);
362
+ return;
363
+ case "LabeledStatement":
364
+ yield* flatten(node.body);
365
+ return;
366
+ case "WithStatement":
367
+ yield* flatten(node.body);
368
+ return;
369
+ default: return;
370
+ }
371
+ }
372
+ //#endregion
373
+ //#region src/lib/tracker.ts
374
+ /**
375
+ * Helper to track variable names that store the result of createFederation() or createFederationBuilder() calls
376
+ */
377
+ function trackFederationVariables() {
378
+ const federationVariables = /* @__PURE__ */ new Set();
379
+ const isFederationObject = (obj) => {
380
+ switch (obj.type) {
381
+ case "Identifier": return federationVariables.has(obj.name);
382
+ case "CallExpression":
383
+ if (isCreateFederation(obj)) return true;
384
+ if (obj.callee.type === "MemberExpression") return isFederationObject(obj.callee.object);
385
+ return false;
386
+ case "MemberExpression": return isFederationObject(obj.object);
387
+ }
388
+ return false;
389
+ };
390
+ return {
391
+ VariableDeclarator(node) {
392
+ const init = node.init;
393
+ const id = node.id;
394
+ if (init?.type === "CallExpression") {
395
+ if (isCreateFederation(init) && id.type === "Identifier") federationVariables.add(id.name);
396
+ }
397
+ },
398
+ isFederationVariable(name) {
399
+ return federationVariables.has(name);
400
+ },
401
+ isFederationObject
402
+ };
403
+ }
404
+ const isCreateFederation = (node) => node.callee.type === "Identifier" && /^create(Federation|FederationBuilder)$/i.test(node.callee.name);
405
+ //#endregion
406
+ //#region src/lib/required.ts
407
+ /**
408
+ * Checks if a CallExpression is a specific dispatcher method call.
409
+ */
410
+ const isDispatcherMethodCall = (methodName) => (node) => allOf(hasMemberExpressionCallee, hasIdentifierProperty, hasMethodName(methodName))(node);
411
+ /**
412
+ * Tracks dispatcher method calls on federation objects.
413
+ */
414
+ const createDispatcherTracker = (dispatcherMethod, federationTracker) => {
415
+ let dispatcherConfigured = false;
416
+ return {
417
+ isDispatcherConfigured: () => dispatcherConfigured,
418
+ checkDispatcherCall: (node) => {
419
+ if (isDispatcherMethodCall(dispatcherMethod)(node) && federationTracker.isFederationObject(node.callee.object)) dispatcherConfigured = true;
420
+ }
421
+ };
422
+ };
423
+ function createRequiredRule(config, description) {
424
+ return (context) => {
425
+ const federationTracker = trackFederationVariables();
426
+ const dispatcherTracker = createDispatcherTracker(config.setter, federationTracker);
427
+ const actorDispatchers = [];
428
+ const propertyChecker = createPropertyChecker(Boolean)(config.path);
429
+ const pluralPropertyChecker = config.pluralName == null ? null : createPropertyChecker(Boolean)([...config.path.slice(0, -1), config.pluralName]);
430
+ const propertySearcher = createPropertySearcher(pluralPropertyChecker == null ? propertyChecker : (node) => propertyChecker(node) || pluralPropertyChecker(node));
431
+ return {
432
+ VariableDeclarator: federationTracker.VariableDeclarator,
433
+ CallExpression(node) {
434
+ dispatcherTracker.checkDispatcherCall(node);
435
+ if (!isSetActorDispatcherCall(node)) return;
436
+ if (!federationTracker.isFederationObject(node.callee.object)) return;
437
+ const dispatcher = node.arguments[1];
438
+ if (isFunction(dispatcher)) actorDispatchers.push(dispatcher);
439
+ },
440
+ "Program:exit"() {
441
+ if (!dispatcherTracker.isDispatcherConfigured()) return;
442
+ for (const dispatcher of actorDispatchers) if (!propertySearcher(dispatcher.body)) context.report({
443
+ node: dispatcher,
444
+ ...description
445
+ });
446
+ }
447
+ };
448
+ };
449
+ }
450
+ /**
451
+ * Creates a required rule with the given property configuration.
452
+ */
453
+ function createRequiredRuleDeno(config) {
454
+ return { create: createRequiredRule(config, { message: actorPropertyRequired(config) }) };
455
+ }
456
+ /**
457
+ * Creates a required ESLint rule with the given property configuration.
458
+ */
459
+ function createRequiredRuleEslint(config) {
460
+ return {
461
+ meta: {
462
+ type: "suggestion",
463
+ docs: { description: `Ensure actor dispatcher returns ${config.path.join(".")} property.` },
464
+ schema: [],
465
+ messages: { required: "{{ message }}" }
466
+ },
467
+ create: createRequiredRule(config, {
468
+ messageId: "required",
469
+ data: { message: actorPropertyRequired(config) }
470
+ })
471
+ };
472
+ }
473
+ createRequiredRuleDeno(properties.assertionMethod);
474
+ const eslint$27 = createRequiredRuleEslint(properties.assertionMethod);
475
+ //#endregion
476
+ //#region src/lib/mismatch.ts
477
+ const isIdentifierWithName = (name) => (node) => allOf(isNodeType("Identifier"), isNodeName(name))(node);
478
+ /**
479
+ * Checks if a node is a CallExpression calling the expected context method.
480
+ */
481
+ const isExpectedMethodCall = ({ ctxName, idName, methodName, requiresIdentifier }, identifierFirst = false) => (node) => {
482
+ if (!isNodeType("CallExpression")(node) || !hasMemberExpressionCallee(node) || !isIdentifierWithName(ctxName)(node.callee.object) || !isIdentifierWithName(methodName)(node.callee.property)) return false;
483
+ if (!requiresIdentifier) return (0, _fxts_core.isEmpty)(node.arguments);
484
+ if (identifierFirst) return node.arguments.length > 0 && isIdentifierWithName(idName)(node.arguments[0]);
485
+ return allOf((0, _fxts_core.negate)(_fxts_core.isEmpty), (0, _fxts_core.some)(isIdentifierWithName(idName)))(node.arguments);
486
+ };
487
+ /** Accepts an inline compatible ID built from the matching portable URI. */
488
+ const isCompatiblePortableMethodCall = (context, portableGetter) => (node) => isNodeType("CallExpression")(node) && isIdentifierWithName("toCompatibleEf61Id")(node.callee) && node.arguments.length > 0 && isExpectedMethodCall({
489
+ ...context,
490
+ methodName: portableGetter
491
+ }, true)(node.arguments[0]);
492
+ /**
493
+ * Extracts parameter names from a function.
494
+ */
495
+ const extractParams = (fn) => {
496
+ const params = fn.params;
497
+ if (params.length < 2) return [null, null];
498
+ return params.slice(0, 2).map(getNameIfIdentifier);
499
+ };
500
+ const getNameIfIdentifier = (node) => node?.type === "Identifier" ? node.name : null;
501
+ function createMismatchRule(config, describe) {
502
+ return (context) => {
503
+ const tracker = trackFederationVariables();
504
+ return {
505
+ VariableDeclarator: tracker.VariableDeclarator,
506
+ CallExpression(node) {
507
+ if (!isSetActorDispatcherCall(node) || !hasMemberExpressionCallee(node) || !tracker.isFederationObject(node.callee.object)) return;
508
+ const dispatcherArg = node.arguments[1];
509
+ if (!isFunction(dispatcherArg)) return;
510
+ const [ctxName, idName] = extractParams(dispatcherArg);
511
+ if (!ctxName || !idName) return;
512
+ const methodCallContext = {
513
+ path: config.path.join("."),
514
+ ctxName,
515
+ idName,
516
+ methodName: config.getter,
517
+ requiresIdentifier: config.requiresIdentifier
518
+ };
519
+ if (!createPropertySearcher(createPropertyChecker(Boolean)(config.path))(dispatcherArg.body)) return;
520
+ if (!createPropertySearcher(createPropertyChecker((value) => isExpectedMethodCall(methodCallContext)(value) || config.portableGetter != null && (isExpectedMethodCall({
521
+ ...methodCallContext,
522
+ methodName: config.portableGetter
523
+ }, true)(value) || isCompatiblePortableMethodCall(methodCallContext, config.portableGetter)(value)))(config.path))(dispatcherArg.body)) context.report({
524
+ node: dispatcherArg,
525
+ ...describe(methodCallContext)
526
+ });
527
+ }
528
+ };
529
+ };
530
+ }
531
+ /**
532
+ * Creates a lint rule that checks if a property uses
533
+ * the correct context method.
534
+ *
535
+ * @param config Property configuration containing name, getter, setter, and
536
+ * nested info
537
+ * @returns A Deno lint rule
538
+ */
539
+ const createMismatchRuleDeno = (config) => ({ create: createMismatchRule(config, (context) => ({ message: actorPropertyMismatch(context) })) });
540
+ const createMismatchRuleEslint = (config) => ({
541
+ meta: {
542
+ type: "problem",
543
+ docs: { description: `Ensure actor's ${config.path.join(".")} property uses correct context method` },
544
+ schema: [],
545
+ messages: { mismatch: "{{ message }}" }
546
+ },
547
+ create: createMismatchRule(config, (context) => ({
548
+ messageId: "mismatch",
549
+ data: { message: actorPropertyMismatch(context) }
550
+ }))
551
+ });
552
+ createMismatchRuleDeno(properties.featured);
553
+ const eslint$26 = createMismatchRuleEslint(properties.featured);
554
+ createRequiredRuleDeno(properties.featured);
555
+ const eslint$25 = createRequiredRuleEslint(properties.featured);
556
+ createMismatchRuleDeno(properties.featuredTags);
557
+ const eslint$24 = createMismatchRuleEslint(properties.featuredTags);
558
+ createRequiredRuleDeno(properties.featuredTags);
559
+ const eslint$23 = createRequiredRuleEslint(properties.featuredTags);
560
+ createMismatchRuleDeno(properties.followers);
561
+ const eslint$22 = createMismatchRuleEslint(properties.followers);
562
+ createRequiredRuleDeno(properties.followers);
563
+ const eslint$21 = createRequiredRuleEslint(properties.followers);
564
+ createMismatchRuleDeno(properties.following);
565
+ const eslint$20 = createMismatchRuleEslint(properties.following);
566
+ createRequiredRuleDeno(properties.following);
567
+ const eslint$19 = createRequiredRuleEslint(properties.following);
568
+ createMismatchRuleDeno(properties.id);
569
+ const eslint$18 = createMismatchRuleEslint(properties.id);
570
+ createRequiredRuleDeno(properties.id);
571
+ const eslint$17 = createRequiredRuleEslint(properties.id);
572
+ createMismatchRuleDeno(properties.inbox);
573
+ const eslint$16 = createMismatchRuleEslint(properties.inbox);
574
+ createRequiredRuleDeno(properties.inbox);
575
+ const eslint$15 = createRequiredRuleEslint(properties.inbox);
576
+ createMismatchRuleDeno(properties.liked);
577
+ const eslint$14 = createMismatchRuleEslint(properties.liked);
578
+ createRequiredRuleDeno(properties.liked);
579
+ const eslint$13 = createRequiredRuleEslint(properties.liked);
580
+ createMismatchRuleDeno(properties.outbox);
581
+ const eslint$12 = createMismatchRuleEslint(properties.outbox);
582
+ createRequiredRuleDeno(properties.outbox);
583
+ const eslint$11 = createRequiredRuleEslint(properties.outbox);
584
+ createRequiredRuleDeno(properties.preferredUsername);
585
+ const eslint$10 = createRequiredRuleEslint(properties.preferredUsername);
586
+ createRequiredRuleDeno(properties.publicKey);
587
+ const eslint$9 = createRequiredRuleEslint(properties.publicKey);
588
+ createMismatchRuleDeno(properties.sharedInbox);
589
+ const eslint$8 = createMismatchRuleEslint(properties.sharedInbox);
590
+ createRequiredRuleDeno(properties.sharedInbox);
591
+ const eslint$7 = createRequiredRuleEslint(properties.sharedInbox);
592
+ createMismatchRuleDeno(properties.uploadMedia);
593
+ const eslint$6 = createMismatchRuleEslint(properties.uploadMedia);
594
+ createRequiredRuleDeno(properties.uploadMedia);
595
+ const eslint$5 = createRequiredRuleEslint(properties.uploadMedia);
596
+ //#endregion
597
+ //#region src/rules/collection-filtering-not-implemented.ts
598
+ /**
599
+ * The followers dispatcher method that supports filtering.
600
+ * Only setFollowersDispatcher uses the filter parameter for
601
+ * followers collection synchronization.
602
+ * See: https://fedify.dev/manual/collections#filtering-by-server
603
+ */
604
+ const FILTER_NEEDED = ["setFollowersDispatcher"];
605
+ /**
606
+ * Checks if a node is a setFollowersDispatcher call.
607
+ */
608
+ const isFollowersDispatcherCall = (node) => "callee" in node && node.callee && node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && FILTER_NEEDED.includes(node.callee.property.name);
609
+ /**
610
+ * Checks if a function node has the filter parameter (4th parameter).
611
+ * CollectionDispatcher signature: (context, identifier, cursor, filter?) => ...
612
+ */
613
+ const hasFilterParameter = hasMinParams(4);
614
+ const eslint$4 = {
615
+ meta: {
616
+ type: "suggestion",
617
+ docs: { description: "Ensure followers dispatcher implements filtering" },
618
+ schema: [],
619
+ messages: { filterRequired: COLLECTION_FILTERING_NOT_IMPLEMENTED_ERROR }
620
+ },
621
+ create(context) {
622
+ const federationTracker = trackFederationVariables();
623
+ return {
624
+ VariableDeclarator: federationTracker.VariableDeclarator,
625
+ CallExpression(node) {
626
+ if (!isFollowersDispatcherCall(node)) return;
627
+ if (!federationTracker.isFederationObject(node.callee.object)) return;
628
+ const dispatcherArg = node.arguments[1];
629
+ if (!isFunction(dispatcherArg)) return;
630
+ if (!hasFilterParameter(dispatcherArg)) context.report({
631
+ node: dispatcherArg,
632
+ messageId: "filterRequired"
633
+ });
634
+ }
635
+ };
636
+ }
637
+ };
638
+ //#endregion
639
+ //#region src/rules/media-uploader-authorization-required.ts
640
+ const MESSAGE$3 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
641
+ function getParent$1(node) {
642
+ if (node == null || typeof node !== "object") return null;
643
+ const parent = node.parent;
644
+ return isNode(parent) ? parent : null;
645
+ }
646
+ /**
647
+ * Whether the given `setMediaUploader(...)` call is directly chained with
648
+ * `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
649
+ */
650
+ function isDirectlyAuthorized(callNode) {
651
+ const member = getParent$1(callNode);
652
+ if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
653
+ const property = member.property;
654
+ if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
655
+ const call = getParent$1(member);
656
+ return call != null && call.type === "CallExpression" && call.callee === member;
657
+ }
658
+ function createRule$3(buildReport) {
659
+ return (context) => {
660
+ const federationTracker = trackFederationVariables();
661
+ const unchained = [];
662
+ const authorizedVars = /* @__PURE__ */ new Set();
663
+ return {
664
+ VariableDeclarator: federationTracker.VariableDeclarator,
665
+ CallExpression(node) {
666
+ if (hasMemberExpressionCallee(node) && hasIdentifierProperty(node) && hasMethodName("authorize")(node)) {
667
+ const object = node.callee.object;
668
+ if (isNode(object) && object.type === "Identifier") authorizedVars.add(object.name);
669
+ return;
670
+ }
671
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
672
+ if (isDirectlyAuthorized(node)) return;
673
+ const parent = getParent$1(node);
674
+ let varName = null;
675
+ if (parent != null && parent.type === "VariableDeclarator") {
676
+ const id = parent.id;
677
+ if (isNode(id) && id.type === "Identifier") varName = id.name;
678
+ }
679
+ unchained.push({
680
+ node,
681
+ varName
682
+ });
683
+ },
684
+ "Program:exit"() {
685
+ for (const { node, varName } of unchained) {
686
+ if (varName != null && authorizedVars.has(varName)) continue;
687
+ context.report({
688
+ node,
689
+ ...buildReport
690
+ });
691
+ }
692
+ }
693
+ };
694
+ };
695
+ }
696
+ const eslint$3 = {
697
+ meta: {
698
+ type: "suggestion",
699
+ docs: { description: "Warn when setMediaUploader() is registered without an authorize() hook" },
700
+ schema: [],
701
+ messages: { required: "{{ message }}" }
702
+ },
703
+ create: createRule$3({
704
+ messageId: "required",
705
+ data: { message: MESSAGE$3 }
706
+ })
707
+ };
708
+ //#endregion
709
+ //#region src/rules/media-uploader-object-uri-required.ts
710
+ const MESSAGE$2 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
711
+ const GETTER_NAME = "getObjectUri";
712
+ const getMemberPropertyName$2 = (node) => {
713
+ if (node.type !== "MemberExpression") return null;
714
+ const property = node.property;
715
+ if (property.type === "Identifier") return property.name;
716
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
717
+ return null;
718
+ };
719
+ function unwrapAssignmentPattern(node) {
720
+ let current = node ?? null;
721
+ while (current?.type === "AssignmentPattern") current = current.left;
722
+ return current;
723
+ }
724
+ /**
725
+ * Resolves the callback argument to a function node, following simple bindings
726
+ * when the callback is passed by reference (identifier or object member).
727
+ */
728
+ const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
729
+ if (isFunction(expr)) return expr;
730
+ if (expr.type === "Identifier") {
731
+ if (seen.has(expr.name)) return null;
732
+ seen.add(expr.name);
733
+ const binding = bindings.get(expr.name);
734
+ if (binding == null || !isNode(binding)) return null;
735
+ if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
736
+ if (binding.type === "Identifier") return resolveCallbackReference(binding, bindings, seen);
737
+ return null;
738
+ }
739
+ if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
740
+ const binding = bindings.get(expr.object.name);
741
+ if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
742
+ const propertyName = getMemberPropertyName$2(expr);
743
+ if (propertyName == null) return null;
744
+ for (const prop of binding.properties) {
745
+ if (!isNode(prop) || prop.type !== "Property") continue;
746
+ if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
747
+ const value = prop.value;
748
+ if (isFunction(value) || value.type === "FunctionDeclaration") return value;
749
+ }
750
+ }
751
+ return null;
752
+ };
753
+ /**
754
+ * Returns the identifier name of the callback's context parameter, used to
755
+ * require that `getObjectUri()` is called as a member of the context itself
756
+ * (`ctx.getObjectUri(...)`), not on some other object. Returns `null` when the
757
+ * parameter is destructured: destructuring `getObjectUri` off the context
758
+ * (`({ getObjectUri }) => getObjectUri(...)`) loses the method's receiver and
759
+ * throws at runtime, so that form is intentionally not recognized as valid.
760
+ */
761
+ function getContextParamName(callback) {
762
+ const contextParam = unwrapAssignmentPattern(callback.params[0]);
763
+ return contextParam?.type === "Identifier" ? contextParam.name : null;
764
+ }
765
+ const FUNCTION_NODE_TYPES$1 = /* @__PURE__ */ new Set([
766
+ "FunctionDeclaration",
767
+ "FunctionExpression",
768
+ "ArrowFunctionExpression"
769
+ ]);
770
+ /**
771
+ * Collects `const`/`let`/`var name = init` bindings declared inside the
772
+ * callback body (excluding nested functions), so an identifier used in a
773
+ * returned expression can be resolved back to the expression it was assigned.
774
+ */
775
+ function collectLocalBindings(node, bindings) {
776
+ if (node == null || typeof node !== "object") return;
777
+ if (Array.isArray(node)) {
778
+ for (const child of node) collectLocalBindings(child, bindings);
779
+ return;
780
+ }
781
+ const n = node;
782
+ if (typeof n.type !== "string") return;
783
+ if (FUNCTION_NODE_TYPES$1.has(n.type)) return;
784
+ if (n.type === "VariableDeclarator") {
785
+ const id = n.id;
786
+ if (id != null && isNode(id) && id.type === "Identifier" && n.init != null) {
787
+ if (!bindings.has(id.name)) bindings.set(id.name, n.init);
788
+ }
789
+ }
790
+ for (const key in n) {
791
+ if (key === "parent") continue;
792
+ collectLocalBindings(n[key], bindings);
793
+ }
794
+ }
795
+ /**
796
+ * Collects the returned expressions of the callback: the argument of every
797
+ * `return` statement (excluding nested functions), or, for an
798
+ * expression-bodied arrow, the body expression itself. A bare `return;`
799
+ * contributes `null`.
800
+ */
801
+ function collectReturnedExpressions(callback) {
802
+ const body = callback.body;
803
+ if (body != null && isNode(body) && body.type !== "BlockStatement") return [body];
804
+ const result = [];
805
+ const walk = (node) => {
806
+ if (node == null || typeof node !== "object") return;
807
+ if (Array.isArray(node)) {
808
+ for (const child of node) walk(child);
809
+ return;
810
+ }
811
+ const n = node;
812
+ if (typeof n.type !== "string") return;
813
+ if (n.type === "ReturnStatement") {
814
+ result.push(n.argument ?? null);
815
+ return;
816
+ }
817
+ if (FUNCTION_NODE_TYPES$1.has(n.type)) return;
818
+ for (const key in n) {
819
+ if (key === "parent") continue;
820
+ walk(n[key]);
821
+ }
822
+ };
823
+ walk(body);
824
+ return result;
825
+ }
826
+ function toNode(value) {
827
+ if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
828
+ const n = value;
829
+ return typeof n.type === "string" ? n : null;
830
+ }
831
+ /** Whether the node is a `Promise.resolve(x)` call. */
832
+ function isPromiseResolveCall(expr) {
833
+ if (expr.type !== "CallExpression") return false;
834
+ const callee = toNode(expr.callee);
835
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== "resolve") return false;
836
+ const object = toNode(callee.object);
837
+ return object != null && object.type === "Identifier" && object.name === "Promise";
838
+ }
839
+ /**
840
+ * Strips wrapper expressions that do not change the underlying value: `await`,
841
+ * parentheses, `as`/`<T>`/`!`/`satisfies` type assertions, and
842
+ * `Promise.resolve(...)` (a synchronous callback may wrap its result in an
843
+ * already-resolved promise, which `MediaUploaderCallback` permits).
844
+ */
845
+ function unwrapExpression(value) {
846
+ let current = toNode(value);
847
+ while (current != null) if (current.type === "AwaitExpression") current = toNode(current.argument);
848
+ else if (current.type === "TSAsExpression" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "ParenthesizedExpression") current = toNode(current.expression);
849
+ else if (isPromiseResolveCall(current)) {
850
+ const args = current.arguments;
851
+ current = Array.isArray(args) && args.length === 1 ? toNode(args[0]) : null;
852
+ } else return current;
853
+ return null;
854
+ }
855
+ /**
856
+ * Whether the expression is a member call `<ctx>.getObjectUri(...)` whose
857
+ * receiver is the callback's context parameter. A call on any other object
858
+ * (e.g. `helper.getObjectUri(...)`), or a bare `getObjectUri(...)` from a
859
+ * destructured parameter (which loses the receiver and throws at runtime),
860
+ * does not count.
861
+ */
862
+ function isGetObjectUriCall(expr, ctx) {
863
+ if (expr.type !== "CallExpression") return false;
864
+ if (ctx.ctxName == null) return false;
865
+ const callee = toNode(expr.callee);
866
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== GETTER_NAME) return false;
867
+ const object = unwrapExpression(callee.object);
868
+ return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
869
+ }
870
+ function isUrlConstructor(expr) {
871
+ const callee = toNode(expr.callee);
872
+ return callee != null && callee.type === "Identifier" && callee.name === "URL";
873
+ }
874
+ /** Finds the first `ObjectExpression` argument of a `NewExpression`. */
875
+ function extractObjectArg(newExpr) {
876
+ const args = newExpr.arguments;
877
+ if (!Array.isArray(args)) return null;
878
+ for (const arg of args) {
879
+ const a = toNode(arg);
880
+ if (a != null && a.type === "ObjectExpression") return a;
881
+ }
882
+ return null;
883
+ }
884
+ /**
885
+ * Whether a URL-valued expression is derived from `getObjectUri()`: a direct
886
+ * call, an identifier whose local binding resolves to one
887
+ * (`const uri = ctx.getObjectUri(...); return uri`), both branches of a
888
+ * ternary, or `new URL(<derived>)`.
889
+ */
890
+ function urlDerivesFromGetObjectUri(value, ctx, seen) {
891
+ const expr = unwrapExpression(value);
892
+ if (expr == null) return false;
893
+ if (isGetObjectUriCall(expr, ctx)) return true;
894
+ if (expr.type === "Identifier") {
895
+ const name = expr.name;
896
+ if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return urlDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
897
+ return false;
898
+ }
899
+ if (expr.type === "ConditionalExpression") return urlDerivesFromGetObjectUri(expr.consequent, ctx, seen) && urlDerivesFromGetObjectUri(expr.alternate, ctx, seen);
900
+ if (expr.type === "NewExpression" && isUrlConstructor(expr)) {
901
+ const args = expr.arguments;
902
+ return Array.isArray(args) && args.length > 0 && urlDerivesFromGetObjectUri(args[0], ctx, seen);
903
+ }
904
+ return false;
905
+ }
906
+ const SPREAD_TYPES = /* @__PURE__ */ new Set([
907
+ "SpreadElement",
908
+ "SpreadProperty",
909
+ "ExperimentalSpreadProperty"
910
+ ]);
911
+ /**
912
+ * Whether an object literal's *effective* own `id` is derived from
913
+ * `getObjectUri`. Properties are evaluated in order so a later duplicate `id`
914
+ * wins, and a spread or computed key appearing after the last static `id`
915
+ * (which could override it) is treated as unsafe. An object with no static
916
+ * `id` property (its `id` might come from an unresolved spread) is also
917
+ * unsafe.
918
+ */
919
+ function objectIdDerivesFromGetObjectUri(objExpr, ctx, seen) {
920
+ const props = objExpr.properties;
921
+ if (!Array.isArray(props)) return false;
922
+ let idValue;
923
+ let idIsSet = false;
924
+ let overridableAfterId = false;
925
+ for (const prop of props) {
926
+ const p = toNode(prop);
927
+ if (p == null) return false;
928
+ if (SPREAD_TYPES.has(p.type) || p.type === "Property" && p.computed === true) {
929
+ if (idIsSet) overridableAfterId = true;
930
+ continue;
931
+ }
932
+ if (p.type !== "Property") continue;
933
+ const key = toNode(p.key);
934
+ if ((key == null ? null : key.type === "Identifier" && typeof key.name === "string" ? key.name : key.type === "Literal" && typeof key.value === "string" ? key.value : null) !== "id") continue;
935
+ idValue = p.value;
936
+ idIsSet = true;
937
+ overridableAfterId = false;
938
+ }
939
+ if (!idIsSet || overridableAfterId) return false;
940
+ return urlDerivesFromGetObjectUri(idValue, ctx, seen);
941
+ }
942
+ /**
943
+ * Whether a single returned expression is valid: a URL derived from
944
+ * `getObjectUri()`, or a vocab object (a `new X({...})` expression) whose own
945
+ * `id` is derived from it. The object is judged solely by its `id`, so
946
+ * `getObjectUri()` appearing in another property (e.g. `url`) does not excuse a
947
+ * hard-coded `id`. A plain object literal is rejected: the handler calls
948
+ * `toJsonLd()` on the result, so a bare `{ ... }` (which lacks it) is not a
949
+ * valid return.
950
+ */
951
+ function returnDerivesFromGetObjectUri(value, ctx, seen) {
952
+ const expr = unwrapExpression(value);
953
+ if (expr == null) return false;
954
+ if (expr.type === "Identifier") {
955
+ const name = expr.name;
956
+ if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return returnDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
957
+ return false;
958
+ }
959
+ if (expr.type === "ConditionalExpression") return returnDerivesFromGetObjectUri(expr.consequent, ctx, seen) && returnDerivesFromGetObjectUri(expr.alternate, ctx, seen);
960
+ if (expr.type === "NewExpression") {
961
+ if (isUrlConstructor(expr)) return urlDerivesFromGetObjectUri(expr, ctx, seen);
962
+ const objArg = extractObjectArg(expr);
963
+ if (objArg == null) return false;
964
+ return objectIdDerivesFromGetObjectUri(objArg, ctx, seen);
965
+ }
966
+ return urlDerivesFromGetObjectUri(expr, ctx, seen);
967
+ }
968
+ /**
969
+ * Determines whether every returned value of the callback is derived from
970
+ * `getObjectUri`. Returns `false` when the callback has no return value or any
971
+ * returned value is not derived from `getObjectUri`.
972
+ */
973
+ function callbackDerivesFromGetObjectUri(callback) {
974
+ const returnedExpressions = collectReturnedExpressions(callback);
975
+ if (returnedExpressions.length < 1) return false;
976
+ const bindings = /* @__PURE__ */ new Map();
977
+ collectLocalBindings(callback.body, bindings);
978
+ const ctx = {
979
+ ctxName: getContextParamName(callback),
980
+ bindings
981
+ };
982
+ return returnedExpressions.every((expr) => returnDerivesFromGetObjectUri(expr, ctx, /* @__PURE__ */ new Set()));
983
+ }
984
+ function createRule$2(buildReport) {
985
+ return (context) => {
986
+ const federationTracker = trackFederationVariables();
987
+ const bindings = /* @__PURE__ */ new Map();
988
+ const pendingCalls = [];
989
+ const inspectCall = (node) => {
990
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || node.arguments.length < 2) return;
991
+ if (!federationTracker.isFederationObject(node.callee.object)) return;
992
+ const callbackArg = node.arguments[1];
993
+ const callback = isNode(callbackArg) && isFunction(callbackArg) ? callbackArg : isNode(callbackArg) ? resolveCallbackReference(callbackArg, bindings) : null;
994
+ if (callback == null) return;
995
+ if (callbackDerivesFromGetObjectUri(callback)) return;
996
+ context.report({
997
+ node: callback,
998
+ ...buildReport
999
+ });
1000
+ };
1001
+ return {
1002
+ VariableDeclarator(node) {
1003
+ federationTracker.VariableDeclarator(node);
1004
+ if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
1005
+ },
1006
+ FunctionDeclaration(node) {
1007
+ if (node.id != null) bindings.set(node.id.name, node);
1008
+ },
1009
+ CallExpression(node) {
1010
+ pendingCalls.push(node);
1011
+ },
1012
+ "Program:exit"() {
1013
+ for (const node of pendingCalls) inspectCall(node);
1014
+ }
1015
+ };
1016
+ };
1017
+ }
1018
+ const eslint$2 = {
1019
+ meta: {
1020
+ type: "suggestion",
1021
+ docs: { description: "Warn when a setMediaUploader() callback does not derive its return value from ctx.getObjectUri()" },
1022
+ schema: [],
1023
+ messages: { required: "{{ message }}" }
1024
+ },
1025
+ create: createRule$2({
1026
+ messageId: "required",
1027
+ data: { message: MESSAGE$2 }
1028
+ })
1029
+ };
1030
+ //#endregion
1031
+ //#region src/lib/outbox-listener.ts
1032
+ const DELIVERY_METHOD_NAMES$1 = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
1033
+ const isChainedFromOutboxListeners$1 = (expr, federationTracker) => {
1034
+ if (expr.type !== "CallExpression") return false;
1035
+ if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
1036
+ const methodName = expr.callee.property.name;
1037
+ if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
1038
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners$1(expr.callee.object, federationTracker);
1039
+ return false;
1040
+ };
1041
+ const getMemberPropertyName$1 = (expr) => {
1042
+ if (expr.type !== "MemberExpression") return null;
1043
+ const property = expr.property;
1044
+ if (property.type === "Identifier") return property.name;
1045
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
1046
+ return null;
1047
+ };
1048
+ function unwrapContextParam$1(node) {
1049
+ let current = node ?? null;
1050
+ while (current?.type === "AssignmentPattern") current = current.left;
1051
+ return current;
1052
+ }
1053
+ /**
1054
+ * Resolves an expression to the function it refers to: a direct function
1055
+ * literal, a local variable bound to one, or a property of a local object
1056
+ * literal bound to one (e.g. `handlers.deliver` where
1057
+ * `const handlers = { deliver() {} }`). Used to resolve a listener argument
1058
+ * (`.on(Activity, handler)`).
1059
+ */
1060
+ const resolveFunctionBinding = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
1061
+ if (isFunction(expr)) return expr;
1062
+ if (expr.type === "Identifier") {
1063
+ if (seen.has(expr.name)) return null;
1064
+ seen.add(expr.name);
1065
+ const binding = bindings.get(expr.name);
1066
+ if (binding == null || !isNode(binding)) return null;
1067
+ if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
1068
+ if (binding.type === "Identifier") return resolveFunctionBinding(binding, bindings, seen);
1069
+ return null;
1070
+ }
1071
+ if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
1072
+ const binding = bindings.get(expr.object.name);
1073
+ if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
1074
+ const propertyName = getMemberPropertyName$1(expr);
1075
+ if (propertyName == null) return null;
1076
+ for (const prop of binding.properties) {
1077
+ if (!isNode(prop) || prop.type !== "Property") continue;
1078
+ if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
1079
+ const value = prop.value;
1080
+ if (isFunction(value) || value.type === "FunctionDeclaration") return value;
1081
+ }
1082
+ }
1083
+ return null;
1084
+ };
1085
+ /**
1086
+ * Builds the visitor an outbox-listener rule registers: it follows
1087
+ * `federation.setOutboxListeners(...).on(Type, listener)` chains through the
1088
+ * file and, once the whole file has been seen, hands each listener, resolved
1089
+ * to its function, to `onListener`. The listener may be an inline function or
1090
+ * a name bound to one elsewhere in the file.
1091
+ */
1092
+ function createOutboxListenerVisitor(onListener) {
1093
+ const federationTracker = trackFederationVariables();
1094
+ const bindings = /* @__PURE__ */ new Map();
1095
+ const pendingCalls = [];
1096
+ const inspectCall = (node) => {
1097
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
1098
+ if (!isChainedFromOutboxListeners$1(node.callee.object, federationTracker)) return;
1099
+ const listener = node.arguments[1];
1100
+ const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveFunctionBinding(listener, bindings) : null;
1101
+ if (resolvedListener == null) return;
1102
+ onListener(resolvedListener);
1103
+ };
1104
+ return {
1105
+ VariableDeclarator(node) {
1106
+ federationTracker.VariableDeclarator(node);
1107
+ if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
1108
+ },
1109
+ FunctionDeclaration(node) {
1110
+ if (node.id != null) bindings.set(node.id.name, node);
1111
+ },
1112
+ CallExpression(node) {
1113
+ pendingCalls.push(node);
1114
+ },
1115
+ "Program:exit"() {
1116
+ for (const node of pendingCalls) inspectCall(node);
1117
+ }
1118
+ };
1119
+ }
1120
+ //#endregion
1121
+ //#region src/lib/reachability.ts
1122
+ const FUNCTION_NODE_TYPES = /* @__PURE__ */ new Set([
1123
+ "FunctionDeclaration",
1124
+ "FunctionExpression",
1125
+ "ArrowFunctionExpression"
1126
+ ]);
1127
+ const isFunctionLikeNode = (node) => FUNCTION_NODE_TYPES.has(node.type);
1128
+ const isStaticallyFalsy = (test) => test.type === "Literal" && !test.value;
1129
+ const isStaticallyTruthy = (test) => test.type === "Literal" && Boolean(test.value);
1130
+ /**
1131
+ * Whether every path through this statement unconditionally returns or
1132
+ * throws, meaning anything textually after it in the same statement list
1133
+ * never runs. Deliberately conservative: when it can't prove that, it
1134
+ * answers `false`, which keeps the following code counted as reachable
1135
+ * (a missed dead-code case is safer than wrongly hiding live code).
1136
+ */
1137
+ function alwaysExits(node) {
1138
+ switch (node.type) {
1139
+ case "ReturnStatement":
1140
+ case "ThrowStatement":
1141
+ case "BreakStatement":
1142
+ case "ContinueStatement": return true;
1143
+ case "BlockStatement": return node.body.some((statement) => alwaysExits(statement));
1144
+ case "IfStatement": {
1145
+ const test = node.test;
1146
+ if (isStaticallyFalsy(test)) return node.alternate != null && alwaysExits(node.alternate);
1147
+ if (isStaticallyTruthy(test)) return alwaysExits(node.consequent);
1148
+ if (node.alternate == null) return false;
1149
+ return alwaysExits(node.consequent) && alwaysExits(node.alternate);
1150
+ }
1151
+ case "TryStatement": return node.finalizer != null && alwaysExits(node.finalizer);
1152
+ default: return false;
1153
+ }
1154
+ }
1155
+ function collectBindingExpressions(node, out, bindings, declares = false) {
1156
+ switch (node.type) {
1157
+ case "VariableDeclaration":
1158
+ for (const decl of node.declarations) {
1159
+ const names = [];
1160
+ collectBoundNames(decl.id, names);
1161
+ if (node.kind === "var") for (const name of names) bindings?.declaredHere.add(name);
1162
+ collectBindingExpressions(decl.id, out, bindings, true);
1163
+ }
1164
+ break;
1165
+ case "ObjectPattern":
1166
+ for (const prop of node.properties) if (prop.type === "Property") {
1167
+ if (prop.computed) out.push(prop.key);
1168
+ collectBindingExpressions(prop.value, out, bindings, declares);
1169
+ } else if (prop.type === "RestElement") collectBindingExpressions(prop.argument, out, bindings, declares);
1170
+ break;
1171
+ case "ArrayPattern":
1172
+ for (const elem of node.elements) if (elem != null) collectBindingExpressions(elem, out, bindings, declares);
1173
+ break;
1174
+ case "AssignmentPattern":
1175
+ out.push(node);
1176
+ if (declares) bindings?.declarationPatterns.add(node);
1177
+ collectBindingExpressions(node.left, out, bindings, declares);
1178
+ break;
1179
+ case "RestElement":
1180
+ collectBindingExpressions(node.argument, out, bindings, declares);
1181
+ break;
1182
+ case "MemberExpression": {
1183
+ const object = node.object;
1184
+ if (object.type !== "Identifier") out.push(object);
1185
+ if (node.computed) out.push(node.property);
1186
+ break;
1187
+ }
1188
+ }
1189
+ }
1190
+ function collectReachableStatements(node, out, bindings) {
1191
+ switch (node.type) {
1192
+ case "BlockStatement":
1193
+ for (const [index, statement] of node.body.entries()) {
1194
+ collectReachableStatements(statement, out, bindings);
1195
+ if (alwaysExits(statement)) {
1196
+ for (const rest of node.body.slice(index + 1)) if (rest.type === "FunctionDeclaration") out.push(rest);
1197
+ return;
1198
+ }
1199
+ }
1200
+ return;
1201
+ case "IfStatement": {
1202
+ const test = node.test;
1203
+ out.push(test);
1204
+ if (!isStaticallyFalsy(test)) collectReachableStatements(node.consequent, out, bindings);
1205
+ if (node.alternate != null && !isStaticallyTruthy(test)) collectReachableStatements(node.alternate, out, bindings);
1206
+ return;
1207
+ }
1208
+ case "TryStatement":
1209
+ collectReachableStatements(node.block, out, bindings);
1210
+ if (node.handler != null) collectReachableStatements(node.handler.body, out, bindings);
1211
+ if (node.finalizer != null) collectReachableStatements(node.finalizer, out, bindings);
1212
+ return;
1213
+ case "SwitchStatement":
1214
+ out.push(node.discriminant);
1215
+ for (const switchCase of node.cases) {
1216
+ if (switchCase.test != null) out.push(switchCase.test);
1217
+ for (const statement of switchCase.consequent) {
1218
+ collectReachableStatements(statement, out, bindings);
1219
+ if (alwaysExits(statement)) break;
1220
+ }
1221
+ }
1222
+ return;
1223
+ case "WhileStatement":
1224
+ out.push(node.test);
1225
+ if (!isStaticallyFalsy(node.test)) collectReachableStatements(node.body, out, bindings);
1226
+ return;
1227
+ case "DoWhileStatement":
1228
+ out.push(node.test);
1229
+ collectReachableStatements(node.body, out, bindings);
1230
+ return;
1231
+ case "ForStatement":
1232
+ if (node.init != null) out.push(node.init);
1233
+ if (node.test != null) out.push(node.test);
1234
+ if (node.test == null || !isStaticallyFalsy(node.test)) {
1235
+ if (node.update != null) out.push(node.update);
1236
+ collectReachableStatements(node.body, out, bindings);
1237
+ }
1238
+ return;
1239
+ case "ForInStatement":
1240
+ case "ForOfStatement":
1241
+ collectBindingExpressions(node.left, out, bindings);
1242
+ out.push(node.right);
1243
+ collectReachableStatements(node.body, out, bindings);
1244
+ return;
1245
+ case "LabeledStatement":
1246
+ collectReachableStatements(node.body, out, bindings);
1247
+ return;
1248
+ case "WithStatement":
1249
+ out.push(node.object);
1250
+ collectReachableStatements(node.body, out, bindings);
1251
+ return;
1252
+ default:
1253
+ out.push(node);
1254
+ return;
1255
+ }
1256
+ }
1257
+ /**
1258
+ * Collects plain-value references to identifiers: `deliver()`,
1259
+ * `forEach(deliver)`, a shorthand `{ deliver }`, and so on. Skips positions
1260
+ * that name something rather than reference a value: a declaration's own
1261
+ * `id`/params, the non-computed `.property` of a member expression (so
1262
+ * `someService.deliver()` never counts as a reference to an unrelated local
1263
+ * `deliver`), and the target of an assignment, which writes to a name
1264
+ * instead of reading it.
1265
+ */
1266
+ function collectReferencedNames(node, out, crossFunctions = false) {
1267
+ if (node == null || typeof node !== "object") return;
1268
+ if (Array.isArray(node)) {
1269
+ for (const item of node) collectReferencedNames(item, out, crossFunctions);
1270
+ return;
1271
+ }
1272
+ if (!isNode(node)) return;
1273
+ const n = node;
1274
+ if (n.type === "Identifier") {
1275
+ out.add(n.name);
1276
+ return;
1277
+ }
1278
+ if (n.type === "MemberExpression" && !n.computed) {
1279
+ collectReferencedNames(n.object, out, crossFunctions);
1280
+ return;
1281
+ }
1282
+ if (n.type === "Property" && !n.computed) {
1283
+ collectReferencedNames(n.value, out, crossFunctions);
1284
+ return;
1285
+ }
1286
+ if (n.type === "VariableDeclarator") {
1287
+ if (n.init != null) collectReferencedNames(n.init, out, crossFunctions);
1288
+ return;
1289
+ }
1290
+ if (n.type === "ClassDeclaration" || n.type === "ClassExpression") {
1291
+ collectReferencedNames(n.superClass, out, crossFunctions);
1292
+ collectReferencedNames(n.body, out, crossFunctions);
1293
+ return;
1294
+ }
1295
+ if ((n.type === "MethodDefinition" || n.type === "PropertyDefinition") && !n.computed) {
1296
+ collectReferencedNames(n.value, out, crossFunctions);
1297
+ return;
1298
+ }
1299
+ if (n.type === "AssignmentExpression" && n.operator === "=") {
1300
+ if (getAssignmentTargetName(n.left) != null) {
1301
+ collectReferencedNames(n.right, out, crossFunctions);
1302
+ let current = n.left;
1303
+ while (current.type === "MemberExpression") {
1304
+ if (current.computed) collectReferencedNames(current.property, out, crossFunctions);
1305
+ current = current.object;
1306
+ }
1307
+ return;
1308
+ }
1309
+ }
1310
+ if (n.type === "AssignmentPattern") {
1311
+ if (getAssignmentTargetName(n.left) != null) {
1312
+ collectReferencedNames(n.right, out, crossFunctions);
1313
+ let current = n.left;
1314
+ while (current.type === "MemberExpression") {
1315
+ if (current.computed) collectReferencedNames(current.property, out, crossFunctions);
1316
+ current = current.object;
1317
+ }
1318
+ return;
1319
+ }
1320
+ }
1321
+ if (isFunctionLikeNode(n)) {
1322
+ if (crossFunctions) collectReferencedNames(n.body, out, crossFunctions);
1323
+ return;
1324
+ }
1325
+ const record = n;
1326
+ for (const key in record) {
1327
+ if (key === "parent") continue;
1328
+ collectReferencedNames(record[key], out, crossFunctions);
1329
+ }
1330
+ }
1331
+ /**
1332
+ * The name an assignment writes to: `x` for `x = ...`, and the root object
1333
+ * for `obj.a.b = ...`. `null` for anything more exotic.
1334
+ */
1335
+ function getAssignmentTargetName(target) {
1336
+ let current = target;
1337
+ while (current.type === "MemberExpression") current = current.object;
1338
+ return current.type === "Identifier" ? current.name : null;
1339
+ }
1340
+ /** Every identifier a declaration pattern binds (`a`, `{ a, b: c }`, `[a]`). */
1341
+ function collectBoundNames(pattern, out) {
1342
+ if (pattern == null || typeof pattern !== "object" || !isNode(pattern)) return;
1343
+ const p = pattern;
1344
+ if (p.type === "TSParameterProperty") {
1345
+ collectBoundNames(p.parameter, out);
1346
+ return;
1347
+ }
1348
+ switch (p.type) {
1349
+ case "Identifier":
1350
+ out.push(p.name);
1351
+ return;
1352
+ case "AssignmentPattern":
1353
+ collectBoundNames(p.left, out);
1354
+ return;
1355
+ case "RestElement":
1356
+ collectBoundNames(p.argument, out);
1357
+ return;
1358
+ case "ArrayPattern":
1359
+ for (const element of p.elements) collectBoundNames(element, out);
1360
+ return;
1361
+ case "ObjectPattern":
1362
+ for (const prop of p.properties) collectBoundNames(prop.value ?? prop.argument, out);
1363
+ return;
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Every name the function scope rooted at `root` binds or rebinds anywhere,
1368
+ * reachable or not: `fn`'s parameters and own name, any declaration (in a
1369
+ * block, a loop head, a `catch` clause, a class static block or a TypeScript
1370
+ * enum too), and any assignment or update of the name itself. Does not
1371
+ * descend into a nested function's body. Deliberately broad, since a name
1372
+ * outside this set is taken to be an enclosing scope's binding throughout
1373
+ * `root`.
1374
+ */
1375
+ function collectScopeBoundNames(root, fn) {
1376
+ const names = /* @__PURE__ */ new Set();
1377
+ const add = (pattern) => {
1378
+ const bound = [];
1379
+ collectBoundNames(pattern, bound);
1380
+ for (const name of bound) names.add(name);
1381
+ };
1382
+ const addAssigned = (target) => {
1383
+ let current = target;
1384
+ while (isNode(current) && TS_EXPRESSION_WRAPPERS.has(current.type)) current = current.expression;
1385
+ add(current);
1386
+ };
1387
+ for (const param of fn?.params ?? []) add(param);
1388
+ if (fn?.type === "FunctionExpression") add(fn.id);
1389
+ const visit = (node) => {
1390
+ if (node == null || typeof node !== "object") return;
1391
+ if (Array.isArray(node)) {
1392
+ for (const item of node) visit(item);
1393
+ return;
1394
+ }
1395
+ if (!isNode(node)) return;
1396
+ const record = node;
1397
+ switch (node.type) {
1398
+ case "FunctionDeclaration":
1399
+ case "TSDeclareFunction":
1400
+ add(record.id);
1401
+ return;
1402
+ case "FunctionExpression":
1403
+ case "ArrowFunctionExpression": return;
1404
+ case "ClassDeclaration":
1405
+ case "ClassExpression":
1406
+ case "VariableDeclarator":
1407
+ case "TSEnumDeclaration":
1408
+ case "TSModuleDeclaration":
1409
+ case "TSImportEqualsDeclaration":
1410
+ add(record.id);
1411
+ break;
1412
+ case "TSEnumMember": {
1413
+ const id = record.id;
1414
+ if (id.type === "Identifier") names.add(id.name);
1415
+ else if (id.type === "Literal" && typeof id.value === "string") names.add(id.value);
1416
+ break;
1417
+ }
1418
+ case "CatchClause":
1419
+ add(record.param);
1420
+ break;
1421
+ case "AssignmentExpression":
1422
+ addAssigned(record.left);
1423
+ break;
1424
+ case "UpdateExpression":
1425
+ addAssigned(record.argument);
1426
+ break;
1427
+ case "ForInStatement":
1428
+ case "ForOfStatement":
1429
+ addAssigned(record.left);
1430
+ break;
1431
+ }
1432
+ for (const key in record) if (key !== "parent") visit(record[key]);
1433
+ };
1434
+ for (const param of fn?.params ?? []) visit(param);
1435
+ visit(root);
1436
+ return names;
1437
+ }
1438
+ const TS_EXPRESSION_WRAPPERS = /* @__PURE__ */ new Set([
1439
+ "TSAsExpression",
1440
+ "TSNonNullExpression",
1441
+ "TSSatisfiesExpression",
1442
+ "TSTypeAssertion"
1443
+ ]);
1444
+ /**
1445
+ * Finds the function literals a value holds itself: the value is the
1446
+ * function, or it sits in an object or array literal, a conditional, or a
1447
+ * class body. Stops at a call, so a function handed to one as an argument,
1448
+ * or invoked by it, is not held by whatever the call's result is bound to.
1449
+ * Those count on their own wherever they appear.
1450
+ */
1451
+ function collectHeldFunctions(node, out) {
1452
+ if (node == null || typeof node !== "object") return;
1453
+ if (Array.isArray(node)) {
1454
+ for (const item of node) collectHeldFunctions(item, out);
1455
+ return;
1456
+ }
1457
+ if (!isNode(node)) return;
1458
+ const n = node;
1459
+ if (isFunctionLikeNode(n)) {
1460
+ out.push(n);
1461
+ return;
1462
+ }
1463
+ if (n.type === "CallExpression" || n.type === "NewExpression") return;
1464
+ const record = n;
1465
+ for (const key in record) {
1466
+ if (key === "parent") continue;
1467
+ collectHeldFunctions(record[key], out);
1468
+ }
1469
+ }
1470
+ /**
1471
+ * Collects the functions each name in `node`'s own scope holds: `function
1472
+ * name() {}`, `class Name {}`, and the value of `const name = ...` or a
1473
+ * later `name = ...` or `name.prop = ...`, including a function inside an
1474
+ * object or array literal (see `collectHeldFunctions`). A function held
1475
+ * under a name counts as used as soon as the name is mentioned, however it
1476
+ * is mentioned, so this never has to work out how the name reaches the
1477
+ * function. Does not descend into a found function's own body: a name bound
1478
+ * inside it is only found once that function is itself resolved as
1479
+ * reachable, so it can be layered on top of (and correctly shadow) the outer
1480
+ * scope's names. What goes into `augmentingAssignments` instead of `out`
1481
+ * extends the names' enclosing candidates rather than shadowing them: an
1482
+ * assignment-form loop default, and a property write through an object that
1483
+ * this scope inherits (a name not in `localNames`).
1484
+ */
1485
+ function collectFunctionsByName(node, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames) {
1486
+ if (node == null || typeof node !== "object") return;
1487
+ if (Array.isArray(node)) {
1488
+ for (const item of node) collectFunctionsByName(item, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1489
+ return;
1490
+ }
1491
+ if (!isNode(node)) return;
1492
+ const n = node;
1493
+ const bindTo = (names, from, bindings = out) => {
1494
+ const functions = [];
1495
+ collectHeldFunctions(from, functions);
1496
+ if (functions.length < 1) return;
1497
+ for (const name of names) bindings.set(name, [...bindings.get(name) ?? [], ...functions]);
1498
+ };
1499
+ if (n.type === "FunctionDeclaration") {
1500
+ if (n.id?.name != null) bindTo([n.id.name], n);
1501
+ return;
1502
+ }
1503
+ if (n.type === "ClassDeclaration") {
1504
+ if (n.id?.name != null) bindTo([n.id.name], n);
1505
+ return;
1506
+ }
1507
+ if (isFunctionLikeNode(n)) return;
1508
+ if (n.type === "StaticBlock") {
1509
+ collectFunctionsByName(n.body, out, augmentingAssignments, declarationPatterns, /* @__PURE__ */ new Set(), scopeDeclarations, localNames);
1510
+ return;
1511
+ }
1512
+ if (n.type === "VariableDeclaration") {
1513
+ if (n.kind === "var" || scopeDeclarations.has(n)) for (const decl of n.declarations) {
1514
+ const names = [];
1515
+ collectBoundNames(decl.id, names);
1516
+ for (const name of names) declaredHere.add(name);
1517
+ }
1518
+ for (const decl of n.declarations) collectFunctionsByName(decl, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1519
+ return;
1520
+ }
1521
+ if (n.type === "VariableDeclarator") {
1522
+ const decl = n;
1523
+ const names = [];
1524
+ collectBoundNames(decl.id, names);
1525
+ if (decl.init != null) {
1526
+ bindTo(names, decl.init);
1527
+ collectFunctionsByName(decl.init, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1528
+ }
1529
+ return;
1530
+ }
1531
+ if (n.type === "AssignmentPattern") {
1532
+ const names = [];
1533
+ if (n.left.type === "MemberExpression") {
1534
+ const name = getAssignmentTargetName(n.left);
1535
+ if (name != null) names.push(name);
1536
+ } else collectBoundNames(n.left, names);
1537
+ bindTo(names, n.right, n.left.type === "MemberExpression" || !declarationPatterns.has(n) ? augmentingAssignments : out);
1538
+ collectFunctionsByName(n.right, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1539
+ return;
1540
+ }
1541
+ if (n.type === "AssignmentExpression") {
1542
+ const name = getAssignmentTargetName(n.left);
1543
+ if (name != null) bindTo([name], n.right, n.left.type === "MemberExpression" && !localNames.has(name) ? augmentingAssignments : out);
1544
+ collectFunctionsByName(n.right, out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1545
+ return;
1546
+ }
1547
+ const record = n;
1548
+ for (const key in record) {
1549
+ if (key === "parent") continue;
1550
+ collectFunctionsByName(record[key], out, augmentingAssignments, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1551
+ }
1552
+ }
1553
+ /**
1554
+ * Finds function literals directly nested in a reachable statement, without
1555
+ * descending past them -- their own reachability is decided separately.
1556
+ */
1557
+ function collectNestedFunctions(node, out) {
1558
+ if (node == null || typeof node !== "object") return;
1559
+ if (Array.isArray(node)) {
1560
+ for (const item of node) collectNestedFunctions(item, out);
1561
+ return;
1562
+ }
1563
+ if (!isNode(node)) return;
1564
+ const n = node;
1565
+ if (isFunctionLikeNode(n)) {
1566
+ out.push(n);
1567
+ return;
1568
+ }
1569
+ const record = n;
1570
+ for (const key in record) {
1571
+ if (key === "parent") continue;
1572
+ collectNestedFunctions(record[key], out);
1573
+ }
1574
+ }
1575
+ /**
1576
+ * Walks every scope that is actually reachable from `root`, calling `visit`
1577
+ * for each, and returns the set of function nodes found to be used: `root`
1578
+ * itself feeds a worklist, and each function it (or a function already on
1579
+ * the worklist) uses from *its own* reachable statements -- never from a
1580
+ * dead branch or some other not-yet-reached function's body -- gets queued in
1581
+ * turn. `outerFunctionsByName` is layered fresh for each scope, so a name
1582
+ * bound at an inner scope shadows a same-named one further out instead of
1583
+ * overwriting it globally, and a dead branch that merely mentions a name
1584
+ * never queues what it holds.
1585
+ */
1586
+ function walkUsedScopes(root, visit) {
1587
+ const used = /* @__PURE__ */ new Set();
1588
+ const visited = /* @__PURE__ */ new Set();
1589
+ const processScope = (scopeRoot, scopeFn, outerFunctionsByName, outerStaleNames) => {
1590
+ if (visited.has(scopeRoot)) return;
1591
+ visited.add(scopeRoot);
1592
+ const statements = [];
1593
+ const declarationPatterns = /* @__PURE__ */ new Set();
1594
+ const declaredHere = /* @__PURE__ */ new Set();
1595
+ collectReachableStatements(scopeRoot, statements, {
1596
+ declarationPatterns,
1597
+ declaredHere
1598
+ });
1599
+ const functionsHere = /* @__PURE__ */ new Map();
1600
+ const augmentingAssignmentsHere = /* @__PURE__ */ new Map();
1601
+ for (const param of scopeFn?.params ?? []) {
1602
+ const names = [];
1603
+ collectBoundNames(param, names);
1604
+ for (const name of names) functionsHere.set(name, []);
1605
+ }
1606
+ const scopeDeclarations = new Set(scopeRoot.type === "BlockStatement" ? scopeRoot.body : []);
1607
+ const scopeBoundNames = collectScopeBoundNames(scopeRoot, scopeFn);
1608
+ const localNames = /* @__PURE__ */ new Set([...scopeBoundNames, ...outerStaleNames]);
1609
+ for (const statement of statements) collectFunctionsByName(statement, functionsHere, augmentingAssignmentsHere, declarationPatterns, declaredHere, scopeDeclarations, localNames);
1610
+ const staleNames = new Set([...localNames].filter((name) => !functionsHere.has(name)));
1611
+ const functionsByName = new Map(outerFunctionsByName);
1612
+ for (const [name, functions] of functionsHere) functionsByName.set(name, functions);
1613
+ for (const [name, functions] of augmentingAssignmentsHere) {
1614
+ const base = declaredHere.has(name) ? functionsHere.get(name) ?? [] : functionsByName.get(name) ?? [];
1615
+ functionsByName.set(name, [...base, ...functions]);
1616
+ }
1617
+ visit({
1618
+ fn: scopeFn,
1619
+ statements,
1620
+ functionsByName
1621
+ });
1622
+ const referencedNames = /* @__PURE__ */ new Set();
1623
+ for (const statement of statements) collectReferencedNames(statement, referencedNames);
1624
+ const reached = /* @__PURE__ */ new Set();
1625
+ for (const [name, functions] of functionsByName) {
1626
+ if (!referencedNames.has(name)) continue;
1627
+ for (const fn of functions) reached.add(fn);
1628
+ }
1629
+ const held = /* @__PURE__ */ new Set();
1630
+ for (const bindings of [functionsHere, augmentingAssignmentsHere]) for (const functions of bindings.values()) for (const fn of functions) held.add(fn);
1631
+ for (const statement of statements) {
1632
+ const nested = [];
1633
+ collectNestedFunctions(statement, nested);
1634
+ for (const fn of nested) if (!held.has(fn)) reached.add(fn);
1635
+ }
1636
+ for (const reachedFn of reached) {
1637
+ used.add(reachedFn);
1638
+ processScope(reachedFn.body, reachedFn, functionsByName, staleNames);
1639
+ }
1640
+ };
1641
+ processScope(root, null, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set());
1642
+ return used;
1643
+ }
1644
+ /**
1645
+ * The set of function nodes that are actually reachable from `root`. See
1646
+ * `walkUsedScopes`.
1647
+ */
1648
+ function computeUsedFunctions(root) {
1649
+ return walkUsedScopes(root, () => {});
1650
+ }
1651
+ /**
1652
+ * A node's `[start, end)` character offsets into the whole source file.
1653
+ * Both engines always populate this -- ESLint forces it on regardless of
1654
+ * parser options, and Deno.lint exposes it the same way as every other
1655
+ * child property (see the `for...in` note on why plain property access
1656
+ * still works even though it's not an own enumerable property).
1657
+ */
1658
+ function getRange(node) {
1659
+ return node.range;
1660
+ }
1661
+ //#endregion
1662
+ //#region src/rules/outbox-listener-delivery-not-awaited.ts
1663
+ const MESSAGE$1 = "Delivery is not awaited, so the activity may be lost once the handler returns (for example on Cloudflare Workers). Await it, return it, or pass it to waitUntil().";
1664
+ const HANDLED = {
1665
+ fate: "handled",
1666
+ owner: null
1667
+ };
1668
+ const DROPPED = {
1669
+ fate: "dropped",
1670
+ owner: null
1671
+ };
1672
+ /** Wrappers that pass a value through unchanged. */
1673
+ const TRANSPARENT_WRAPPERS = /* @__PURE__ */ new Set([
1674
+ "ChainExpression",
1675
+ "TSAsExpression",
1676
+ "TSNonNullExpression",
1677
+ "TSSatisfiesExpression",
1678
+ "TSTypeAssertion",
1679
+ "ParenthesizedExpression"
1680
+ ]);
1681
+ /**
1682
+ * `Promise` methods that turn promises into one promise. `race()` and
1683
+ * `any()` settle as soon as one input does, which is accepted as a
1684
+ * deliberate choice to stop waiting, like `void`. A `race()` or `any()`
1685
+ * that is itself dropped is still reported.
1686
+ */
1687
+ const PROMISE_COMBINATORS = /* @__PURE__ */ new Set([
1688
+ "all",
1689
+ "allSettled",
1690
+ "race",
1691
+ "any"
1692
+ ]);
1693
+ /** Methods on a promise that return a new promise carrying the chain on. */
1694
+ const PROMISE_CHAIN_METHODS = /* @__PURE__ */ new Set([
1695
+ "then",
1696
+ "catch",
1697
+ "finally"
1698
+ ]);
1699
+ const get = (node, key) => node == null ? void 0 : node[key];
1700
+ const asNode = (value) => isNode(value) ? value : null;
1701
+ const parentOf = (node) => asNode(get(node, "parent"));
1702
+ function unwrap(node) {
1703
+ let current = node;
1704
+ while (TRANSPARENT_WRAPPERS.has(current.type)) {
1705
+ const inner = asNode(get(current, "expression"));
1706
+ if (inner == null) break;
1707
+ current = inner;
1708
+ }
1709
+ return current;
1710
+ }
1711
+ /** The name a member expression accesses, when it is spelled out plainly. */
1712
+ function memberName(node) {
1713
+ if (node.type !== "MemberExpression") return null;
1714
+ const property = asNode(get(node, "property"));
1715
+ if (property == null) return null;
1716
+ if (get(node, "computed") !== true) return property.type === "Identifier" ? property.name : null;
1717
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
1718
+ if (property.type === "TemplateLiteral") {
1719
+ const quasis = get(property, "quasis");
1720
+ if (get(property, "expressions").length === 0 && quasis.length === 1) return quasis[0].cooked ?? quasis[0].value?.cooked ?? null;
1721
+ }
1722
+ return null;
1723
+ }
1724
+ /** Visits every node in `node`, stopping at a nested function unless asked to cross it. */
1725
+ function visitAll(node, visitor, crossFunctions = true) {
1726
+ if (node == null || typeof node !== "object") return;
1727
+ if (Array.isArray(node)) {
1728
+ for (const item of node) visitAll(item, visitor, crossFunctions);
1729
+ return;
1730
+ }
1731
+ if (!isNode(node)) return;
1732
+ if (!crossFunctions && isFunctionLikeNode(node)) return;
1733
+ visitor(node);
1734
+ const record = node;
1735
+ for (const key in record) if (key !== "parent") visitAll(record[key], visitor, crossFunctions);
1736
+ }
1737
+ /** Collects the calls in `node` that run in the same function, not in one nested in it. */
1738
+ function collectCalls(node, out) {
1739
+ visitAll(node, (n) => {
1740
+ if (n.type === "CallExpression") out.push(n);
1741
+ }, false);
1742
+ }
1743
+ function enclosingFunction$1(node) {
1744
+ for (let current = parentOf(node); current != null;) {
1745
+ if (isFunctionLikeNode(current)) return current;
1746
+ current = parentOf(current);
1747
+ }
1748
+ return null;
1749
+ }
1750
+ /** The names an object pattern binds the delivery methods to. */
1751
+ function deliveryAliasesOf(pattern, out) {
1752
+ if (pattern.type !== "ObjectPattern") return;
1753
+ for (const prop of get(pattern, "properties")) {
1754
+ if (prop.type !== "Property") continue;
1755
+ const key = asNode(get(prop, "key"));
1756
+ const keyName = key?.type === "Identifier" ? key.name : key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
1757
+ if (keyName == null || !DELIVERY_METHOD_NAMES$1.has(keyName)) continue;
1758
+ const value = asNode(get(prop, "value"));
1759
+ if (value?.type === "Identifier") out.add(value.name);
1760
+ else if (value?.type === "AssignmentPattern") {
1761
+ const left = asNode(get(value, "left"));
1762
+ if (left?.type === "Identifier") out.add(left.name);
1763
+ }
1764
+ }
1765
+ }
1766
+ /**
1767
+ * Works out how the listener refers to the delivery methods: through its
1768
+ * context parameter (`ctx.sendActivity`), or through a name bound to one of
1769
+ * them, either in the parameter (`{ sendActivity }`) or in code that can run
1770
+ * (`const { sendActivity } = ctx`, `const send = ctx.sendActivity`). A name
1771
+ * bound in a helper nothing uses, or in a dead branch, does not count.
1772
+ */
1773
+ function findDeliveryTargets(listener, scopes) {
1774
+ const aliases = /* @__PURE__ */ new Set();
1775
+ const contextParam = unwrapContextParam$1(listener.params[0]);
1776
+ const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
1777
+ if (contextParam != null) deliveryAliasesOf(contextParam, aliases);
1778
+ if (contextName != null) for (const scope of scopes) for (const statement of scope.statements) visitAll(statement, (node) => {
1779
+ if (node.type !== "VariableDeclarator") return;
1780
+ const id = asNode(get(node, "id"));
1781
+ const init = asNode(get(node, "init"));
1782
+ if (id == null || init == null) return;
1783
+ const source = unwrap(init);
1784
+ if (source.type === "Identifier" && source.name === contextName) {
1785
+ deliveryAliasesOf(id, aliases);
1786
+ return;
1787
+ }
1788
+ if (id.type !== "Identifier" || source.type !== "MemberExpression") return;
1789
+ const object = unwrap(asNode(get(source, "object")) ?? source);
1790
+ const name = memberName(source);
1791
+ if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES$1.has(name)) aliases.add(id.name);
1792
+ }, false);
1793
+ return {
1794
+ contextName,
1795
+ aliases
1796
+ };
1797
+ }
1798
+ /**
1799
+ * Where the value a callback returns goes, judged by the call that receives
1800
+ * the callback.
1801
+ */
1802
+ function callbackResultOutcome(call, analysis) {
1803
+ const name = memberName(unwrap(asNode(get(call, "callee")) ?? call));
1804
+ if (name === "forEach") return DROPPED;
1805
+ if (name === "map" || name === "flatMap") return fateOf(call, analysis, "promises");
1806
+ if (name != null && PROMISE_CHAIN_METHODS.has(name)) return fateOf(call, analysis, "promise");
1807
+ return HANDLED;
1808
+ }
1809
+ /** Where the promise a function returns goes, given who calls the function. */
1810
+ function returnedOutcome(fn, analysis) {
1811
+ if (fn == null) return HANDLED;
1812
+ if (fn === analysis.listener) return {
1813
+ fate: "returned",
1814
+ owner: fn
1815
+ };
1816
+ const parent = parentOf(fn);
1817
+ if (parent?.type === "CallExpression") {
1818
+ if (get(parent, "callee") === fn) return fateOf(parent, analysis, "promise");
1819
+ if (get(parent, "arguments").includes(fn)) return callbackResultOutcome(parent, analysis);
1820
+ }
1821
+ return {
1822
+ fate: "returned",
1823
+ owner: fn
1824
+ };
1825
+ }
1826
+ /**
1827
+ * Where an array of promises, or an object holding one, goes when it is
1828
+ * returned. The listener's caller does not wait for what is inside it. Any
1829
+ * other function hands it to its own callers, who may well pass it to
1830
+ * `Promise.all()` or read the promise back out of it.
1831
+ */
1832
+ function returnedContainerOutcome(fn, analysis) {
1833
+ return fn === analysis.listener ? DROPPED : HANDLED;
1834
+ }
1835
+ /** Follows the value of `expression` up through its parents to where it ends up. */
1836
+ function fateOf(expression, analysis, shape = "promise") {
1837
+ let current = expression;
1838
+ let currentShape = shape;
1839
+ for (;;) {
1840
+ const parent = parentOf(current);
1841
+ if (parent == null) return HANDLED;
1842
+ if (TRANSPARENT_WRAPPERS.has(parent.type)) {
1843
+ current = parent;
1844
+ continue;
1845
+ }
1846
+ switch (parent.type) {
1847
+ case "AwaitExpression": return currentShape === "promise" ? {
1848
+ fate: "awaited",
1849
+ owner: enclosingFunction$1(parent)
1850
+ } : DROPPED;
1851
+ case "ReturnStatement": return currentShape === "promise" ? returnedOutcome(enclosingFunction$1(parent), analysis) : returnedContainerOutcome(enclosingFunction$1(parent), analysis);
1852
+ case "ArrowFunctionExpression":
1853
+ if (get(parent, "body") !== current) return HANDLED;
1854
+ return currentShape === "promise" ? returnedOutcome(parent, analysis) : returnedContainerOutcome(parent, analysis);
1855
+ case "ExpressionStatement": return DROPPED;
1856
+ case "UnaryExpression": return get(parent, "operator") === "void" ? HANDLED : DROPPED;
1857
+ case "SequenceExpression": {
1858
+ const expressions = get(parent, "expressions");
1859
+ if (expressions[expressions.length - 1] !== current) return DROPPED;
1860
+ current = parent;
1861
+ continue;
1862
+ }
1863
+ case "ConditionalExpression":
1864
+ if (get(parent, "test") === current) return DROPPED;
1865
+ current = parent;
1866
+ continue;
1867
+ case "IfStatement":
1868
+ case "WhileStatement":
1869
+ case "DoWhileStatement":
1870
+ case "ForStatement": return DROPPED;
1871
+ case "LogicalExpression":
1872
+ if (get(parent, "operator") === "&&" && get(parent, "left") === current) return DROPPED;
1873
+ current = parent;
1874
+ continue;
1875
+ case "Property":
1876
+ if (get(parent, "computed") && get(parent, "key") === current) return DROPPED;
1877
+ current = parent;
1878
+ continue;
1879
+ case "ObjectExpression":
1880
+ if (currentShape === "promise") currentShape = "object";
1881
+ current = parent;
1882
+ continue;
1883
+ case "ArrayExpression":
1884
+ if (currentShape !== "promise") return HANDLED;
1885
+ currentShape = "promises";
1886
+ current = parent;
1887
+ continue;
1888
+ case "SpreadElement": {
1889
+ const array = parentOf(parent);
1890
+ if (currentShape !== "promises" || array?.type !== "ArrayExpression") return HANDLED;
1891
+ current = array;
1892
+ continue;
1893
+ }
1894
+ case "MemberExpression": {
1895
+ if (get(parent, "computed") && get(parent, "property") === current) return DROPPED;
1896
+ if (get(parent, "object") !== current) return HANDLED;
1897
+ const call = parentOf(parent);
1898
+ const name = memberName(parent);
1899
+ if (currentShape === "promise" && call?.type === "CallExpression" && get(call, "callee") === parent && name != null && PROMISE_CHAIN_METHODS.has(name)) {
1900
+ current = call;
1901
+ continue;
1902
+ }
1903
+ return HANDLED;
1904
+ }
1905
+ case "CallExpression": {
1906
+ if (get(parent, "callee") === current) return HANDLED;
1907
+ const callee = unwrap(asNode(get(parent, "callee")) ?? parent);
1908
+ const name = memberName(callee);
1909
+ if (name === "waitUntil") return HANDLED;
1910
+ const object = callee.type === "MemberExpression" ? unwrap(asNode(get(callee, "object")) ?? callee) : null;
1911
+ if (object?.type === "Identifier" && object.name === "Promise" && name != null && PROMISE_COMBINATORS.has(name)) {
1912
+ current = parent;
1913
+ currentShape = "promise";
1914
+ continue;
1915
+ }
1916
+ return HANDLED;
1917
+ }
1918
+ case "VariableDeclarator": {
1919
+ const id = asNode(get(parent, "id"));
1920
+ if (get(parent, "init") !== current || id?.type !== "Identifier") return HANDLED;
1921
+ return analysis.mentioned.has(id.name) ? HANDLED : DROPPED;
1922
+ }
1923
+ case "AssignmentExpression": {
1924
+ if (get(parent, "right") !== current) return HANDLED;
1925
+ const name = getAssignmentTargetName(asNode(get(parent, "left")) ?? parent);
1926
+ if (name == null) return HANDLED;
1927
+ return analysis.mentioned.has(name) ? HANDLED : DROPPED;
1928
+ }
1929
+ case "AssignmentPattern": {
1930
+ if (get(parent, "right") !== current) return HANDLED;
1931
+ const name = getAssignmentTargetName(asNode(get(parent, "left")) ?? parent);
1932
+ if (name == null) return HANDLED;
1933
+ return analysis.mentioned.has(name) ? HANDLED : DROPPED;
1934
+ }
1935
+ default: return HANDLED;
1936
+ }
1937
+ }
1938
+ }
1939
+ /** Reports every place in `listener` where a delivery promise is dropped. */
1940
+ function checkListener(listener, report) {
1941
+ const scopes = [];
1942
+ walkUsedScopes(listener.body, (scope) => scopes.push(scope));
1943
+ const targets = findDeliveryTargets(listener, scopes);
1944
+ if (targets.contextName == null && targets.aliases.size === 0) return;
1945
+ const isDeliveryCall = (call) => {
1946
+ const callee = unwrap(call.callee);
1947
+ if (callee.type === "MemberExpression") {
1948
+ const name = memberName(callee);
1949
+ if (name == null || !DELIVERY_METHOD_NAMES$1.has(name)) return false;
1950
+ const object = unwrap(asNode(get(callee, "object")) ?? callee);
1951
+ return object.type === "Identifier" && object.name === targets.contextName;
1952
+ }
1953
+ return callee.type === "Identifier" && targets.aliases.has(callee.name);
1954
+ };
1955
+ const mentioned = /* @__PURE__ */ new Set();
1956
+ collectReferencedNames(listener.body, mentioned, true);
1957
+ const analysis = {
1958
+ listener,
1959
+ mentioned
1960
+ };
1961
+ const callsByScope = scopes.map((scope) => {
1962
+ const calls = [];
1963
+ for (const statement of scope.statements) collectCalls(statement, calls);
1964
+ return calls;
1965
+ });
1966
+ const carrying = /* @__PURE__ */ new Map();
1967
+ const carriesDelivery = (call, scope) => {
1968
+ if (isDeliveryCall(call)) return true;
1969
+ const callee = unwrap(call.callee);
1970
+ if (callee.type !== "Identifier") return false;
1971
+ return scope.functionsByName.get(callee.name)?.some((helper) => carrying.has(helper)) ?? false;
1972
+ };
1973
+ const noteCarrying = ({ fate, owner }) => {
1974
+ if (owner == null || owner === listener) return false;
1975
+ if (fate !== "awaited" && fate !== "returned") return false;
1976
+ const known = carrying.get(owner);
1977
+ if (known === "awaited" || known === fate) return false;
1978
+ carrying.set(owner, fate);
1979
+ return true;
1980
+ };
1981
+ for (let changed = true; changed;) {
1982
+ changed = false;
1983
+ scopes.forEach((scope, index) => {
1984
+ for (const call of callsByScope[index]) if (carriesDelivery(call, scope) && noteCarrying(fateOf(call, analysis))) changed = true;
1985
+ });
1986
+ }
1987
+ const reported = /* @__PURE__ */ new Set();
1988
+ const flag = (node) => {
1989
+ if (reported.has(node)) return;
1990
+ reported.add(node);
1991
+ report(node);
1992
+ };
1993
+ scopes.forEach((scope, index) => {
1994
+ for (const call of callsByScope[index]) {
1995
+ if (!carriesDelivery(call, scope)) continue;
1996
+ if (fateOf(call, analysis).fate === "dropped") flag(call);
1997
+ }
1998
+ });
1999
+ for (const [fn, kind] of carrying) if (kind === "awaited" && returnedOutcome(fn, analysis).fate === "dropped") flag(fn);
2000
+ scopes.forEach((scope, index) => {
2001
+ for (const call of callsByScope[index]) for (const arg of call.arguments) {
2002
+ if (arg.type !== "Identifier") continue;
2003
+ const held = scope.functionsByName.get(arg.name);
2004
+ if (held == null || !held.some((fn) => carrying.has(fn))) continue;
2005
+ if (callbackResultOutcome(call, analysis).fate === "dropped") flag(arg);
2006
+ }
2007
+ });
2008
+ }
2009
+ function createRule$1(buildReport) {
2010
+ return (context) => createOutboxListenerVisitor((listener) => checkListener(listener, (node) => {
2011
+ context.report({
2012
+ node,
2013
+ ...buildReport
2014
+ });
2015
+ }));
2016
+ }
2017
+ const eslint$1 = {
2018
+ meta: {
2019
+ type: "suggestion",
2020
+ docs: { description: "Warn when an outbox listener delivers an activity without awaiting it" },
2021
+ schema: [],
2022
+ messages: { notAwaited: "{{ message }}" }
2023
+ },
2024
+ create: createRule$1({
2025
+ messageId: "notAwaited",
2026
+ data: { message: MESSAGE$1 }
2027
+ })
2028
+ };
2029
+ //#endregion
2030
+ //#region src/rules/outbox-listener-delivery-required.ts
2031
+ const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
2032
+ const isChainedFromOutboxListeners = (expr, federationTracker) => {
2033
+ if (expr.type !== "CallExpression") return false;
2034
+ if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
2035
+ const methodName = expr.callee.property.name;
2036
+ if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
2037
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
2038
+ return false;
2039
+ };
2040
+ const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
2041
+ const getMemberPropertyName = (expr) => {
2042
+ if (expr.type !== "MemberExpression") return null;
2043
+ const property = expr.property;
2044
+ if (property.type === "Identifier" && !expr.computed) return property.name;
2045
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
2046
+ return null;
2047
+ };
2048
+ function unwrapContextParam(node) {
2049
+ let current = node ?? null;
2050
+ while (current?.type === "AssignmentPattern") current = current.left;
2051
+ return current;
2052
+ }
2053
+ const getParent = (node) => node.parent ?? null;
2054
+ const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
2055
+ function bindPattern(node, bindings) {
2056
+ switch (node.type) {
2057
+ case "Identifier":
2058
+ bindings.set(node.name, { value: null });
2059
+ break;
2060
+ case "AssignmentPattern":
2061
+ bindPattern(node.left, bindings);
2062
+ break;
2063
+ case "RestElement":
2064
+ bindPattern(node.argument, bindings);
2065
+ break;
2066
+ case "ObjectPattern":
2067
+ for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
2068
+ break;
2069
+ case "ArrayPattern":
2070
+ for (const element of node.elements) if (element != null) bindPattern(element, bindings);
2071
+ break;
2072
+ }
2073
+ }
2074
+ function createBindingIndex() {
2075
+ const scopes = /* @__PURE__ */ new Map();
2076
+ const scopeBindings = (scope) => {
2077
+ let bindings = scopes.get(scope);
2078
+ if (bindings == null) {
2079
+ bindings = /* @__PURE__ */ new Map();
2080
+ scopes.set(scope, bindings);
2081
+ if (isFunctionLike(scope)) {
2082
+ if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
2083
+ for (const param of scope.params) bindPattern(param, bindings);
2084
+ }
2085
+ if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
2086
+ if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
2087
+ }
2088
+ return bindings;
2089
+ };
2090
+ const enclosingScope = (node, functionScope = false) => {
2091
+ let scope = getParent(node);
2092
+ while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
2093
+ "BlockStatement",
2094
+ "ForStatement",
2095
+ "ForInStatement",
2096
+ "ForOfStatement",
2097
+ "SwitchStatement",
2098
+ "CatchClause"
2099
+ ].includes(scope.type))) scope = getParent(scope);
2100
+ return scope;
2101
+ };
2102
+ const lookup = (node, name) => {
2103
+ for (let scope = node; scope != null; scope = getParent(scope)) {
2104
+ const binding = scopeBindings(scope).get(name);
2105
+ if (binding != null) return binding;
2106
+ }
2107
+ return null;
2108
+ };
2109
+ return {
2110
+ lookup,
2111
+ declare(node, pattern, value, hoisted = false) {
2112
+ const bindings = scopeBindings(enclosingScope(node, hoisted));
2113
+ if (pattern.type === "Identifier") {
2114
+ if (hoisted && value == null && bindings.has(pattern.name)) return;
2115
+ if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
2116
+ bindings.set(pattern.name, {
2117
+ value,
2118
+ initializedVariable: node.type === "VariableDeclarator" && value != null
2119
+ });
2120
+ } else bindPattern(pattern, bindings);
2121
+ }
2122
+ };
2123
+ }
2124
+ const position = (node) => node.range?.[0] ?? node.start ?? -1;
2125
+ const enclosingFunction = (node) => {
2126
+ for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
2127
+ return null;
2128
+ };
2129
+ const isInside = (node, ancestor) => {
2130
+ for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
2131
+ return false;
2132
+ };
2133
+ const isDirectStatement = (node, fn) => {
2134
+ let current = node;
2135
+ while (getParent(current) !== fn.body) {
2136
+ const parent = getParent(current);
2137
+ if (parent == null || ![
2138
+ "AwaitExpression",
2139
+ "ExpressionStatement",
2140
+ "ReturnStatement",
2141
+ "VariableDeclarator",
2142
+ "VariableDeclaration",
2143
+ "BlockStatement"
2144
+ ].includes(parent.type)) return false;
2145
+ current = parent;
2146
+ }
2147
+ return [
2148
+ "ExpressionStatement",
2149
+ "ReturnStatement",
2150
+ "VariableDeclaration",
2151
+ "BlockStatement"
2152
+ ].includes(current.type);
2153
+ };
2154
+ const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
2155
+ if (expr.type !== "Identifier") return expr;
2156
+ const binding = bindings.lookup(expr, expr.name);
2157
+ if (binding == null || binding.value == null || seen.has(binding)) return null;
2158
+ seen.add(binding);
2159
+ return resolveBindingValue(binding.value, bindings, seen);
2160
+ };
2161
+ const resolveListenerReference = (expr, bindings) => {
2162
+ const target = resolveBindingValue(expr, bindings);
2163
+ if (target == null) return null;
2164
+ if (isFunctionLike(target)) return target;
2165
+ if (target.type !== "MemberExpression") return null;
2166
+ const object = resolveBindingValue(target.object, bindings);
2167
+ if (object?.type !== "ObjectExpression") return null;
2168
+ const propertyName = getMemberPropertyName(target);
2169
+ if (propertyName == null) return null;
2170
+ for (const prop of object.properties) {
2171
+ if (prop.type !== "Property") continue;
2172
+ if ((prop.key.type === "Identifier" && !prop.computed ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName) continue;
2173
+ const value = resolveBindingValue(prop.value, bindings);
2174
+ if (value != null && isFunctionLike(value)) return value;
2175
+ }
2176
+ return null;
2177
+ };
2178
+ function escapeRegExp(value) {
2179
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
2180
+ }
2181
+ function stripCommentsAndStrings(code) {
2182
+ let result = "";
2183
+ let index = 0;
2184
+ const skipQuotedString = (quote) => {
2185
+ const start = index;
2186
+ index += 1;
2187
+ while (index < code.length) {
2188
+ const char = code[index];
2189
+ if (char === "\\") {
2190
+ index += 2;
2191
+ continue;
2192
+ }
2193
+ index += 1;
2194
+ if (char === quote) break;
2195
+ }
2196
+ const literal = code.slice(start, index);
2197
+ const value = literal.slice(1, -1);
2198
+ result += DELIVERY_METHOD_NAMES.has(value) ? literal : `${quote}${quote}`;
2199
+ };
2200
+ const stripTemplateLiteral = () => {
2201
+ const start = index;
2202
+ index += 1;
2203
+ let raw = "";
2204
+ let hasExpression = false;
2205
+ while (index < code.length) {
2206
+ const char = code[index];
2207
+ if (char === "\\") {
2208
+ raw += char;
2209
+ raw += code[index + 1] ?? "";
2210
+ index += 2;
2211
+ continue;
2212
+ }
2213
+ if (char === "`") {
2214
+ index += 1;
2215
+ if (!hasExpression && DELIVERY_METHOD_NAMES.has(raw)) result += code.slice(start, index);
2216
+ else result += "``";
2217
+ return;
2218
+ }
2219
+ if (char === "$" && code[index + 1] === "{") {
2220
+ hasExpression = true;
2221
+ result += "`${";
2222
+ index += 2;
2223
+ let depth = 1;
2224
+ while (index < code.length && depth > 0) {
2225
+ const exprChar = code[index];
2226
+ const next = code[index + 1];
2227
+ if (exprChar === "'" || exprChar === "\"") {
2228
+ skipQuotedString(exprChar);
2229
+ continue;
2230
+ }
2231
+ if (exprChar === "`") {
2232
+ stripTemplateLiteral();
2233
+ continue;
2234
+ }
2235
+ if (exprChar === "/" && next === "*") {
2236
+ index += 2;
2237
+ while (index < code.length) {
2238
+ if (code[index] === "*" && code[index + 1] === "/") {
2239
+ index += 2;
2240
+ break;
2241
+ }
2242
+ index += 1;
2243
+ }
2244
+ continue;
2245
+ }
2246
+ if (exprChar === "/" && next === "/") {
2247
+ index += 2;
2248
+ while (index < code.length && code[index] !== "\n") index += 1;
2249
+ continue;
2250
+ }
2251
+ result += exprChar;
2252
+ index += 1;
2253
+ if (exprChar === "{") depth += 1;
2254
+ else if (exprChar === "}") depth -= 1;
2255
+ }
2256
+ continue;
2257
+ }
2258
+ raw += char;
2259
+ index += 1;
2260
+ }
2261
+ result += "``";
2262
+ };
2263
+ while (index < code.length) {
2264
+ const char = code[index];
2265
+ const next = code[index + 1];
2266
+ if (char === "/" && next === "*") {
2267
+ index += 2;
2268
+ while (index < code.length) {
2269
+ if (code[index] === "*" && code[index + 1] === "/") {
2270
+ index += 2;
2271
+ break;
2272
+ }
2273
+ index += 1;
2274
+ }
2275
+ continue;
2276
+ }
2277
+ if (char === "/" && next === "/") {
2278
+ index += 2;
2279
+ while (index < code.length && code[index] !== "\n") index += 1;
2280
+ continue;
2281
+ }
2282
+ if (char === "'" || char === "\"") {
2283
+ skipQuotedString(char);
2284
+ continue;
2285
+ }
2286
+ if (char === "`") {
2287
+ stripTemplateLiteral();
2288
+ continue;
2289
+ }
2290
+ result += char;
2291
+ index += 1;
2292
+ }
2293
+ return result;
2294
+ }
2295
+ function getDeliveryAliasName(node) {
2296
+ if (node.type === "Identifier") return node.name;
2297
+ if (node.type === "AssignmentPattern" && node.left.type === "Identifier") return node.left.name;
2298
+ return null;
2299
+ }
2300
+ function buildContextExpressionPattern(contextName) {
2301
+ const name = escapeRegExp(contextName);
2302
+ const boundedName = String.raw`(?<![\w$])${name}(?![\w$])`;
2303
+ return String.raw`(?:${boundedName}|\(\s*${boundedName}(?:\s+as\s+[^)]+)?\s*\))`;
2304
+ }
2305
+ /**
2306
+ * Builds the source text to scan for a delivery call: the reachable
2307
+ * statements of `root`, with every nested function literal either folded
2308
+ * in (its own reachable text spliced in place, wherever that function's
2309
+ * own declaration happens to live) or blanked out, depending on whether
2310
+ * `used` (from `computeUsedFunctions`) says it is actually invoked.
2311
+ *
2312
+ * Splices each function by its own range rather than by matching its
2313
+ * source text, and applies the splices from the end of the statement
2314
+ * backward. That keeps two functions with byte-identical bodies (e.g. two
2315
+ * object-literal methods that both merely call `ctx.sendActivity(...)`)
2316
+ * from colliding: a text-based replacement would find and blank out both
2317
+ * occurrences the first time either one is processed, since it matches by
2318
+ * content everywhere in the statement rather than by which node is
2319
+ * actually being replaced. Replacing from the end backward also means a
2320
+ * later replacement's length change never shifts the still-unprocessed
2321
+ * offsets of an earlier one.
2322
+ */
2323
+ function collectDeliveryScanCode(sourceCode, root, used, ignored, visited) {
2324
+ if (visited.has(root)) return "";
2325
+ visited.add(root);
2326
+ const statements = [];
2327
+ collectReachableStatements(root, statements);
2328
+ return statements.map((statement) => {
2329
+ const text = sourceCode.getText(statement);
2330
+ const [statementStart] = getRange(statement);
2331
+ const nested = [];
2332
+ collectNestedFunctions(statement, nested);
2333
+ const byDescendingStart = [...nested].sort((a, b) => getRange(b)[0] - getRange(a)[0]);
2334
+ let result = text;
2335
+ for (const fn of byDescendingStart) {
2336
+ const [fnStart, fnEnd] = getRange(fn);
2337
+ const replacement = used.has(fn) && !ignored.has(fn) ? collectDeliveryScanCode(sourceCode, fn.body, used, ignored, visited) : "";
2338
+ result = result.slice(0, fnStart - statementStart) + `\n${replacement.length > 0 ? replacement : "()=>{}"}\n` + result.slice(fnEnd - statementStart);
2339
+ }
2340
+ return result;
2341
+ }).join("\n");
2342
+ }
2343
+ function unwrapArgument(node) {
2344
+ while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
2345
+ return node;
2346
+ }
2347
+ function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
2348
+ const ignored = [];
2349
+ const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
2350
+ for (const assignment of assignments) {
2351
+ if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
2352
+ const setup = enclosingFunction(assignment);
2353
+ if (setup == null || setup === listener || !isInside(setup, listener)) continue;
2354
+ const setupParent = getParent(setup);
2355
+ const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
2356
+ const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
2357
+ if (!namedLocalSetup || passedToCall) continue;
2358
+ const installed = assignment.right;
2359
+ if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
2360
+ ignored.push(installed);
2361
+ continue;
2362
+ }
2363
+ const property = getMemberPropertyName(assignment.left);
2364
+ if (property == null) continue;
2365
+ const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
2366
+ if (setupCalls.length === 0) {
2367
+ const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
2368
+ ignored.push(called ? assignment.right : setup);
2369
+ continue;
2370
+ }
2371
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2372
+ const deliveryCalls = directCalls.filter((call) => {
2373
+ const callee = call.callee;
2374
+ return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
2375
+ });
2376
+ if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
2377
+ }
2378
+ return ignored;
2379
+ }
2380
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2381
+ let indices = visited.get(listener);
2382
+ if (indices?.has(contextIndex)) return false;
2383
+ if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
2384
+ indices.add(contextIndex);
2385
+ const ignored = ignoredLocalSetupFunctions(listener, bindings, calls, assignments);
2386
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignored, bindings, calls, assignments)) return true;
2387
+ const param = unwrapContextParam(listener.params[contextIndex]);
2388
+ if (param?.type !== "Identifier") return false;
2389
+ const contextBinding = bindings.lookup(listener, param.name);
2390
+ const reachable = [];
2391
+ collectReachableStatements(listener.body, reachable);
2392
+ for (const call of calls) {
2393
+ let child = call;
2394
+ let owner = getParent(child);
2395
+ while (owner != null && !isFunctionLike(owner)) {
2396
+ const field = owner;
2397
+ if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
2398
+ child = owner;
2399
+ owner = getParent(owner);
2400
+ }
2401
+ if (owner !== listener) continue;
2402
+ const [callStart, callEnd] = getRange(call);
2403
+ if (!reachable.some((statement) => {
2404
+ const [start, end] = getRange(statement);
2405
+ return start <= callStart && callEnd <= end;
2406
+ })) continue;
2407
+ const helper = resolveListenerReference(call.callee, bindings);
2408
+ if (helper == null || "generator" in helper && helper.generator) continue;
2409
+ for (const [index, argument] of call.arguments.entries()) {
2410
+ if (argument.type === "SpreadElement") break;
2411
+ const arg = unwrapArgument(argument);
2412
+ if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
2413
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
2414
+ }
2415
+ }
2416
+ return false;
2417
+ }
2418
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex, ignored, bindings, calls, assignments) => {
2419
+ const used = computeUsedFunctions(listener.body);
2420
+ const ignoredSet = new Set(ignored);
2421
+ for (const assignment of assignments) {
2422
+ if (assignment.type !== "AssignmentExpression" || assignment.left.type !== "MemberExpression" || assignment.left.object.type !== "Identifier" || !isFunction(assignment.right) || !isInside(assignment, listener)) continue;
2423
+ const property = getMemberPropertyName(assignment.left);
2424
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2425
+ if (property == null || objectBinding == null || !calls.some((call) => enclosingFunction(call) === listener && call.callee.type === "MemberExpression" && call.callee.object.type === "Identifier" && getMemberPropertyName(call.callee) === property && bindings.lookup(call.callee.object, call.callee.object.name) === objectBinding)) continue;
2426
+ const installed = assignment.right;
2427
+ if (ignoredSet.has(installed)) continue;
2428
+ for (let fn = installed; fn != null && fn !== listener && !ignoredSet.has(fn); fn = enclosingFunction(fn)) used.add(fn);
2429
+ }
2430
+ const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, ignoredSet, /* @__PURE__ */ new Set()));
2431
+ const aliases = /* @__PURE__ */ new Set();
2432
+ const contextParam = unwrapContextParam(listener.params[contextIndex]);
2433
+ const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
2434
+ if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
2435
+ if (!isNode(prop) || prop.type !== "Property") continue;
2436
+ const keyName = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null;
2437
+ if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
2438
+ const alias = getDeliveryAliasName(prop.value);
2439
+ if (alias != null) aliases.add(alias);
2440
+ }
2441
+ if (contextName != null) {
2442
+ const contextExpr = buildContextExpressionPattern(contextName);
2443
+ if (new RegExp(String.raw`${contextExpr}\s*(?:\?\s*\.\s*(?:sendActivity|forwardActivity)|\.\s*(?:sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\])\s*\(`).test(code)) return true;
2444
+ const destructuringPattern = new RegExp(String.raw`(?:const|let|var)\s*{([^}]*)}\s*=\s*${contextExpr}`, "g");
2445
+ for (const match of code.matchAll(destructuringPattern)) {
2446
+ const fields = match[1].split(",").map((field) => field.trim()).filter(Boolean);
2447
+ for (const field of fields) {
2448
+ const [sourceName, aliasName] = field.split(":").map((part) => part.trim());
2449
+ if (!DELIVERY_METHOD_NAMES.has(sourceName)) continue;
2450
+ aliases.add(aliasName ?? sourceName);
2451
+ }
2452
+ }
2453
+ const aliasPattern = new RegExp(String.raw`(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*${contextExpr}\s*(?:\?\s*\.\s*(sendActivity|forwardActivity)|\.\s*(sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\])`, "g");
2454
+ for (const match of code.matchAll(aliasPattern)) aliases.add(match[1]);
2455
+ }
2456
+ return globalThis.Array.from(aliases).some((alias) => new RegExp(String.raw`\b${escapeRegExp(alias)}\s*\(`).test(code));
2457
+ };
2458
+ function createRule(buildReport) {
2459
+ return (context) => {
2460
+ const federationTracker = trackFederationVariables();
2461
+ const bindings = createBindingIndex();
2462
+ const pendingCalls = [];
2463
+ const pendingAssignments = [];
2464
+ const sourceCode = context.sourceCode;
2465
+ const inspectCall = (node) => {
2466
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
2467
+ if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
2468
+ const listener = node.arguments[1];
2469
+ const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
2470
+ if (resolvedListener == null) return;
2471
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
2472
+ context.report({
2473
+ node: resolvedListener,
2474
+ ...buildReport
2475
+ });
2476
+ };
2477
+ return {
2478
+ VariableDeclarator(node) {
2479
+ federationTracker.VariableDeclarator(node);
2480
+ const declaration = getParent(node);
2481
+ bindings.declare(node, node.id, node.init, declaration?.kind === "var");
2482
+ },
2483
+ FunctionDeclaration(node) {
2484
+ if (node.id == null) return;
2485
+ const parent = getParent(node);
2486
+ const owner = parent == null ? null : getParent(parent);
2487
+ const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
2488
+ bindings.declare(node, node.id, node, inVarBody);
2489
+ },
2490
+ ImportDeclaration(node) {
2491
+ for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
2492
+ },
2493
+ ClassDeclaration(node) {
2494
+ if (node.id != null) bindings.declare(node, node.id, null);
2495
+ },
2496
+ CallExpression(node) {
2497
+ pendingCalls.push(node);
2498
+ },
2499
+ AssignmentExpression(node) {
2500
+ pendingAssignments.push(node);
2501
+ },
2502
+ "Program:exit"() {
2503
+ for (const node of pendingCalls) inspectCall(node);
2504
+ }
2505
+ };
2506
+ };
2507
+ }
2508
+ const eslint = {
2509
+ meta: {
2510
+ type: "suggestion",
2511
+ docs: { description: "Warn when an outbox listener omits explicit delivery methods" },
2512
+ schema: [],
2513
+ messages: { required: "{{ message }}" }
2514
+ },
2515
+ create: createRule({
2516
+ messageId: "required",
2517
+ data: { message: MESSAGE }
2518
+ })
2519
+ };
2520
+ //#endregion
2521
+ Object.defineProperty(exports, "RULE_IDS", {
2522
+ enumerable: true,
2523
+ get: function() {
2524
+ return RULE_IDS;
2525
+ }
2526
+ });
2527
+ Object.defineProperty(exports, "eslint", {
2528
+ enumerable: true,
2529
+ get: function() {
2530
+ return eslint;
2531
+ }
2532
+ });
2533
+ Object.defineProperty(exports, "eslint$1", {
2534
+ enumerable: true,
2535
+ get: function() {
2536
+ return eslint$1;
2537
+ }
2538
+ });
2539
+ Object.defineProperty(exports, "eslint$10", {
2540
+ enumerable: true,
2541
+ get: function() {
2542
+ return eslint$10;
2543
+ }
2544
+ });
2545
+ Object.defineProperty(exports, "eslint$11", {
2546
+ enumerable: true,
2547
+ get: function() {
2548
+ return eslint$11;
2549
+ }
2550
+ });
2551
+ Object.defineProperty(exports, "eslint$12", {
2552
+ enumerable: true,
2553
+ get: function() {
2554
+ return eslint$12;
2555
+ }
2556
+ });
2557
+ Object.defineProperty(exports, "eslint$13", {
2558
+ enumerable: true,
2559
+ get: function() {
2560
+ return eslint$13;
2561
+ }
2562
+ });
2563
+ Object.defineProperty(exports, "eslint$14", {
2564
+ enumerable: true,
2565
+ get: function() {
2566
+ return eslint$14;
2567
+ }
2568
+ });
2569
+ Object.defineProperty(exports, "eslint$15", {
2570
+ enumerable: true,
2571
+ get: function() {
2572
+ return eslint$15;
2573
+ }
2574
+ });
2575
+ Object.defineProperty(exports, "eslint$16", {
2576
+ enumerable: true,
2577
+ get: function() {
2578
+ return eslint$16;
2579
+ }
2580
+ });
2581
+ Object.defineProperty(exports, "eslint$17", {
2582
+ enumerable: true,
2583
+ get: function() {
2584
+ return eslint$17;
2585
+ }
2586
+ });
2587
+ Object.defineProperty(exports, "eslint$18", {
2588
+ enumerable: true,
2589
+ get: function() {
2590
+ return eslint$18;
2591
+ }
2592
+ });
2593
+ Object.defineProperty(exports, "eslint$19", {
2594
+ enumerable: true,
2595
+ get: function() {
2596
+ return eslint$19;
2597
+ }
2598
+ });
2599
+ Object.defineProperty(exports, "eslint$2", {
2600
+ enumerable: true,
2601
+ get: function() {
2602
+ return eslint$2;
2603
+ }
2604
+ });
2605
+ Object.defineProperty(exports, "eslint$20", {
2606
+ enumerable: true,
2607
+ get: function() {
2608
+ return eslint$20;
2609
+ }
2610
+ });
2611
+ Object.defineProperty(exports, "eslint$21", {
2612
+ enumerable: true,
2613
+ get: function() {
2614
+ return eslint$21;
2615
+ }
2616
+ });
2617
+ Object.defineProperty(exports, "eslint$22", {
2618
+ enumerable: true,
2619
+ get: function() {
2620
+ return eslint$22;
2621
+ }
2622
+ });
2623
+ Object.defineProperty(exports, "eslint$23", {
2624
+ enumerable: true,
2625
+ get: function() {
2626
+ return eslint$23;
2627
+ }
2628
+ });
2629
+ Object.defineProperty(exports, "eslint$24", {
2630
+ enumerable: true,
2631
+ get: function() {
2632
+ return eslint$24;
2633
+ }
2634
+ });
2635
+ Object.defineProperty(exports, "eslint$25", {
2636
+ enumerable: true,
2637
+ get: function() {
2638
+ return eslint$25;
2639
+ }
2640
+ });
2641
+ Object.defineProperty(exports, "eslint$26", {
2642
+ enumerable: true,
2643
+ get: function() {
2644
+ return eslint$26;
2645
+ }
2646
+ });
2647
+ Object.defineProperty(exports, "eslint$27", {
2648
+ enumerable: true,
2649
+ get: function() {
2650
+ return eslint$27;
2651
+ }
2652
+ });
2653
+ Object.defineProperty(exports, "eslint$3", {
2654
+ enumerable: true,
2655
+ get: function() {
2656
+ return eslint$3;
2657
+ }
2658
+ });
2659
+ Object.defineProperty(exports, "eslint$4", {
2660
+ enumerable: true,
2661
+ get: function() {
2662
+ return eslint$4;
2663
+ }
2664
+ });
2665
+ Object.defineProperty(exports, "eslint$5", {
2666
+ enumerable: true,
2667
+ get: function() {
2668
+ return eslint$5;
2669
+ }
2670
+ });
2671
+ Object.defineProperty(exports, "eslint$6", {
2672
+ enumerable: true,
2673
+ get: function() {
2674
+ return eslint$6;
2675
+ }
2676
+ });
2677
+ Object.defineProperty(exports, "eslint$7", {
2678
+ enumerable: true,
2679
+ get: function() {
2680
+ return eslint$7;
2681
+ }
2682
+ });
2683
+ Object.defineProperty(exports, "eslint$8", {
2684
+ enumerable: true,
2685
+ get: function() {
2686
+ return eslint$8;
2687
+ }
2688
+ });
2689
+ Object.defineProperty(exports, "eslint$9", {
2690
+ enumerable: true,
2691
+ get: function() {
2692
+ return eslint$9;
2693
+ }
2694
+ });
2695
+ Object.defineProperty(exports, "name", {
2696
+ enumerable: true,
2697
+ get: function() {
2698
+ return name;
2699
+ }
2700
+ });
2701
+ Object.defineProperty(exports, "version", {
2702
+ enumerable: true,
2703
+ get: function() {
2704
+ return version;
2705
+ }
2706
+ });