@commonplaces/oxlint-plugin 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -303,6 +303,11 @@ plus supplied operations in native `Layer.mock` replacements,
303
303
  preserving canonical names through named imports, namespace imports and immutable
304
304
  aliases. Shadowed names cannot borrow import identity. Every explicit operation
305
305
  requires its exact `Effect.fn("Orders.create")` or `Effect.withSpan` name.
306
+ Readonly resource groups are traversed recursively, including local immutable
307
+ aliases: `Orders.session.read` requires that full canonical span. Each supplied
308
+ leaf in `Layer.mock` is checked too. Empty resource groups, cyclic groups,
309
+ spreads and dynamic property names fail closed; local nested contract types
310
+ continue to identify data and Stream members.
306
311
  Legacy `serviceNames` selection remains available for existing consumers.
307
312
  An empty selector is rejected.
308
313
 
@@ -206,30 +206,48 @@ export const requireServiceOperationInstrumentation = defineRule({
206
206
  context.report({ node, messageId: "unsupportedOperation" });
207
207
  return;
208
208
  }
209
- Array.forEach(argument.value.properties, (property) => {
210
- if (property.type !== "Property") {
211
- context.report({ node: property, messageId: "unsupportedOperation" });
209
+ const check = (object, prefix, members, visited = []) => {
210
+ if (Array.contains(visited, object) ||
211
+ (visited.length > 0 && object.properties.length === 0)) {
212
+ context.report({ node: object, messageId: "unsupportedOperation" });
212
213
  return;
213
214
  }
214
- const key = property.computed && property.key.type !== "Literal"
215
- ? Option.none()
216
- : propertyName(property.key);
217
- Option.match(key, {
218
- onNone: () => context.report({ node: property, messageId: "unsupportedOperation" }),
219
- onSome: (member) => {
220
- if (dataMember(context, fields, member) || streamValue(context, property.value))
221
- return;
222
- const name = `${owner.name}.${member}`;
223
- if (!instrumented(context, property.value, name) &&
224
- !adapted(context, property.value, name, options.operationAdapters ?? []))
225
- context.report({
226
- node: property,
227
- messageId: "missingInstrumentation",
228
- data: { operation: name },
229
- });
230
- },
215
+ Array.forEach(object.properties, (property) => {
216
+ if (property.type !== "Property") {
217
+ context.report({ node: property, messageId: "unsupportedOperation" });
218
+ return;
219
+ }
220
+ const key = property.computed && property.key.type !== "Literal"
221
+ ? Option.none()
222
+ : propertyName(property.key);
223
+ Option.match(key, {
224
+ onNone: () => context.report({ node: property, messageId: "unsupportedOperation" }),
225
+ onSome: (member) => {
226
+ if (dataMember(context, members, member) ||
227
+ streamValue(context, property.value))
228
+ return;
229
+ const name = `${prefix}.${member}`;
230
+ const value = valueOf(context, property.value);
231
+ if (Option.isSome(value) && value.value.type === "ObjectExpression") {
232
+ const nested = Array.findFirst(members, (field) => field.type === "TSPropertySignature" &&
233
+ Option.contains(propertyName(field.key), member)).pipe(Option.flatMap((field) => field.type === "TSPropertySignature"
234
+ ? Option.fromNullishOr(field.typeAnnotation?.typeAnnotation)
235
+ : Option.none()), Option.filter((type) => type.type === "TSTypeLiteral"), Option.map((type) => (type.type === "TSTypeLiteral" ? type.members : [])), Option.getOrElse(() => []));
236
+ check(value.value, name, nested, [...visited, object]);
237
+ return;
238
+ }
239
+ if (!instrumented(context, property.value, name) &&
240
+ !adapted(context, property.value, name, options.operationAdapters ?? []))
241
+ context.report({
242
+ node: property,
243
+ messageId: "missingInstrumentation",
244
+ data: { operation: name },
245
+ });
246
+ },
247
+ });
231
248
  });
232
- });
249
+ };
250
+ check(argument.value, owner.name, fields);
233
251
  },
234
252
  });
235
253
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonplaces/oxlint-plugin",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Shared Oxlint rules for strict Effect TypeScript, Alchemy and repository code.",
5
5
  "license": "MIT",
6
6
  "author": "Cooper Corbett",