pumuki 6.3.229 → 6.3.230

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.
@@ -15,6 +15,7 @@ import {
15
15
  hasSwiftAdHocLoggingUsage,
16
16
  hasSwiftAlamofireUsage,
17
17
  hasSwiftForEachIndicesUsage,
18
+ hasSwiftForEachSelfIdentityUsage,
18
19
  hasSwiftForceCastUsage,
19
20
  hasSwiftFontWeightBoldUsage,
20
21
  hasSwiftFixedFontSizeUsage,
@@ -292,6 +293,36 @@ struct CheckoutView: View {
292
293
  assert.equal(hasSwiftUiInlineActionLogicUsage(safe), false);
293
294
  });
294
295
 
296
+ test('hasSwiftForEachSelfIdentityUsage detecta id self y preserva ids estables', () => {
297
+ const source = `
298
+ struct FeedView: View {
299
+ let items: [Item]
300
+
301
+ var body: some View {
302
+ ForEach(items, id: \\.self) { item in
303
+ FeedRow(item: item)
304
+ }
305
+ }
306
+ }
307
+ `;
308
+ const safe = `
309
+ struct FeedView: View {
310
+ let items: [Item]
311
+
312
+ var body: some View {
313
+ ForEach(items, id: \\.id) { item in
314
+ FeedRow(item: item)
315
+ }
316
+ let sample = "ForEach(items, id: \\.self) { item in FeedRow(item: item) }"
317
+ // ForEach(items, id: \.self) { item in FeedRow(item: item) }
318
+ }
319
+ }
320
+ `;
321
+
322
+ assert.equal(hasSwiftForEachSelfIdentityUsage(source), true);
323
+ assert.equal(hasSwiftForEachSelfIdentityUsage(safe), false);
324
+ });
325
+
295
326
  test('hasSwiftUntypedNavigationLinkDestinationUsage detecta NavigationLink no tipado y preserva value navigation', () => {
296
327
  const source = `
297
328
  struct FeedView: View {
@@ -849,6 +849,10 @@ export const hasSwiftForEachIndicesUsage = (source: string): boolean => {
849
849
  );
850
850
  };
851
851
 
852
+ export const hasSwiftForEachSelfIdentityUsage = (source: string): boolean => {
853
+ return hasSwiftSanitizedRegexMatch(source, /\bForEach\s*\([^)]*\bid\s*:\s*\\\.self\b/g);
854
+ };
855
+
852
856
  export const hasSwiftInlineForEachTransformUsage = (source: string): boolean => {
853
857
  const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
854
858
  return /\bForEach\s*\(\s*(?:Array\s*\(\s*)?[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*\s*\.\s*(?:filter|map|compactMap|sorted)\s*(?:\{|\()/g.test(
@@ -678,6 +678,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
678
678
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNonPrivateStateOwnershipUsage, ruleId: 'heuristics.ios.swiftui.non-private-state-ownership.ast', code: 'HEURISTICS_IOS_SWIFTUI_NON_PRIVATE_STATE_OWNERSHIP_AST', message: 'AST heuristic detected @State/@StateObject without private visibility; SwiftUI owned state should be private.' },
679
679
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPassedValueStateWrapperUsage, ruleId: 'heuristics.ios.passed-value-state-wrapper.ast', code: 'HEURISTICS_IOS_PASSED_VALUE_STATE_WRAPPER_AST', message: 'AST heuristic detected a passed value stored as @State/@StateObject via init wrapper ownership.' },
680
680
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForEachIndicesUsage, ruleId: 'heuristics.ios.foreach-indices.ast', code: 'HEURISTICS_IOS_FOREACH_INDICES_AST', message: 'AST heuristic detected ForEach(...indices...) usage where stable element identity may be preferred.' },
681
+ { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForEachSelfIdentityUsage, ruleId: 'heuristics.ios.swiftui.foreach-self-identity.ast', code: 'HEURISTICS_IOS_SWIFTUI_FOREACH_SELF_IDENTITY_AST', message: 'AST heuristic detected ForEach(..., id: \.self) usage; prefer a stable domain identity such as id: \.id or Identifiable models.' },
681
682
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftInlineForEachTransformUsage, ruleId: 'heuristics.ios.swiftui.inline-foreach-transform.ast', code: 'HEURISTICS_IOS_SWIFTUI_INLINE_FOREACH_TRANSFORM_AST', message: 'AST heuristic detected inline filter/map/sort work inside ForEach; prefiltered or cached collections remain the preferred baseline.' },
682
683
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUiForEachConditionalViewCountUsage, ruleId: 'heuristics.ios.swiftui.foreach-conditional-view-count.ast', code: 'HEURISTICS_IOS_SWIFTUI_FOREACH_CONDITIONAL_VIEW_COUNT_AST', message: 'AST heuristic detected conditional view count inside ForEach; keep a constant number of views per element by moving branching into row views or modifiers.' },
683
684
  { platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftContainsUserFilterUsage, ruleId: 'heuristics.ios.contains-user-filter.ast', code: 'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST', message: 'AST heuristic detected contains() in a user-facing filter where localizedStandardContains() may be preferred.' },
@@ -785,6 +785,25 @@ export const iosRules: RuleSet = [
785
785
  code: 'HEURISTICS_IOS_FOREACH_INDICES_AST',
786
786
  },
787
787
  },
788
+ {
789
+ id: 'heuristics.ios.swiftui.foreach-self-identity.ast',
790
+ description: 'Detects SwiftUI ForEach usage with id: \.self instead of stable domain identity.',
791
+ severity: 'WARN',
792
+ platform: 'ios',
793
+ locked: true,
794
+ when: {
795
+ kind: 'Heuristic',
796
+ where: {
797
+ ruleId: 'heuristics.ios.swiftui.foreach-self-identity.ast',
798
+ },
799
+ },
800
+ then: {
801
+ kind: 'Finding',
802
+ message:
803
+ 'AST heuristic detected ForEach(..., id: \.self) usage; prefer a stable domain identity such as id: \.id or Identifiable models.',
804
+ code: 'HEURISTICS_IOS_SWIFTUI_FOREACH_SELF_IDENTITY_AST',
805
+ },
806
+ },
788
807
  {
789
808
  id: 'heuristics.ios.swiftui.inline-foreach-transform.ast',
790
809
  description: 'Detects inline filter/map/sort transformations inside SwiftUI ForEach calls.',
@@ -233,6 +233,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
233
233
  'skills.ios.no-foreach-indices': heuristicDetector('ios.foreach-indices', [
234
234
  'heuristics.ios.foreach-indices.ast',
235
235
  ]),
236
+ 'skills.ios.guideline.ios-swiftui-expert.ensure-foreach-uses-stable-identity-see-references-list-patterns-md':
237
+ heuristicDetector('ios.swiftui.foreach-self-identity', [
238
+ 'heuristics.ios.swiftui.foreach-self-identity.ast',
239
+ ]),
240
+ 'skills.ios.guideline.ios-swiftui-expert.verify-list-patterns-use-stable-identity-see-references-list-patterns-':
241
+ heuristicDetector('ios.swiftui.foreach-self-identity', [
242
+ 'heuristics.ios.swiftui.foreach-self-identity.ast',
243
+ ]),
236
244
  'skills.ios.guideline.ios-swiftui-expert.avoid-inline-filtering-in-foreach-prefilter-and-cache':
237
245
  heuristicDetector('ios.swiftui.inline-foreach-transform', [
238
246
  'heuristics.ios.swiftui.inline-foreach-transform.ast',
@@ -316,6 +316,15 @@ const normalizeKnownRuleTarget = (
316
316
  ) {
317
317
  return 'skills.ios.no-foreach-indices';
318
318
  }
319
+ if (
320
+ (includes('foreach') && includes('stable identity')) ||
321
+ (includes('list patterns') && includes('stable identity'))
322
+ ) {
323
+ if (includes('verify list patterns')) {
324
+ return 'skills.ios.guideline.ios-swiftui-expert.verify-list-patterns-use-stable-identity-see-references-list-patterns-';
325
+ }
326
+ return 'skills.ios.guideline.ios-swiftui-expert.ensure-foreach-uses-stable-identity-see-references-list-patterns-md';
327
+ }
319
328
  if (
320
329
  (includes('inline filtering') && includes('foreach')) ||
321
330
  (includes('no inline filtering') && includes('foreach')) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.229",
3
+ "version": "6.3.230",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/skills.lock.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "1.0",
3
3
  "compilerVersion": "1.0.0",
4
- "generatedAt": "2026-05-13T16:44:08.385Z",
4
+ "generatedAt": "2026-05-13T16:49:25.074Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -8620,7 +8620,7 @@
8620
8620
  "name": "ios-swiftui-expert-guidelines",
8621
8621
  "version": "1.0.0",
8622
8622
  "source": "file:vendor/skills/swiftui-expert-skill/SKILL.md",
8623
- "hash": "92aa9bdb43b5641206e53aaba4647a5c88b88a7aca83dc00eea373d5e70b7080",
8623
+ "hash": "71169779d2e497e74027e271cc9ada00eb269509218964df2a2ff798b9a63f93",
8624
8624
  "rules": [
8625
8625
  {
8626
8626
  "id": "skills.ios.guideline.ios-swiftui-expert.action-handlers-should-reference-methods-not-contain-inline-logic",
@@ -8691,7 +8691,7 @@
8691
8691
  "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8692
8692
  "confidence": "MEDIUM",
8693
8693
  "locked": true,
8694
- "evaluationMode": "DECLARATIVE",
8694
+ "evaluationMode": "AUTO",
8695
8695
  "origin": "core"
8696
8696
  },
8697
8697
  {
@@ -8979,7 +8979,7 @@
8979
8979
  "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8980
8980
  "confidence": "MEDIUM",
8981
8981
  "locked": true,
8982
- "evaluationMode": "DECLARATIVE",
8982
+ "evaluationMode": "AUTO",
8983
8983
  "origin": "core"
8984
8984
  },
8985
8985
  {