pumuki 6.3.294 → 6.3.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [6.3.295] - 2026-05-19
4
+
5
+ - `PUMUKI-INC-156`: staged PRE_WRITE/PRE_COMMIT no longer blocks an atomic iOS slice with broad file-level SwiftUI skill findings that expose dozens of lines without a precise AST node introduced by the diff. Precise AST/skills findings still block in every severity when they intersect changed lines; broad brownfield file debt is retained as advisory with remediation demanding line/node-level evidence or a dedicated remediation slice.
6
+
3
7
  ## [6.3.283] - 2026-05-19
4
8
 
5
9
  - `PUMUKI-INC-150`: brownfield XCTest specs that satisfy `makeSUT()` and `trackForMemoryLeaks()` are also exempt from Swift Testing migration assertion findings (`skills.ios.no-xctassert` / `skills.ios.no-xctunwrap`). These rules remain active for modern Swift Testing suites and XCTest specs without an explicit brownfield quality contract.
@@ -31,6 +31,10 @@ import {
31
31
  hasSwiftAdHocLoggingUsage,
32
32
  hasSwiftAlamofireUsage,
33
33
  hasSwiftEmptyCatchUsage,
34
+ collectSwiftNSErrorThrowLines,
35
+ hasSwiftNSErrorThrowUsage,
36
+ collectSwiftPackageBranchDependencyLines,
37
+ hasSwiftPackageBranchDependencyUsage,
34
38
  hasSwiftForEachIndicesUsage,
35
39
  hasSwiftForEachSelfIdentityUsage,
36
40
  collectSwiftForceCastLines,
@@ -384,6 +388,61 @@ do {
384
388
  assert.equal(hasSwiftEmptyCatchUsage(safe), false);
385
389
  });
386
390
 
391
+ test('hasSwiftNSErrorThrowUsage detecta NSError lanzado directamente y preserva errores tipados', () => {
392
+ const source = `
393
+ func map(response: HTTPURLResponse) throws {
394
+ if response.statusCode >= 500 {
395
+ throw NSError(domain: "Network", code: response.statusCode)
396
+ }
397
+ }
398
+ `;
399
+ const safe = `
400
+ enum NetworkError: Error {
401
+ case server(statusCode: Int)
402
+ }
403
+
404
+ func map(response: HTTPURLResponse) throws {
405
+ if response.statusCode >= 500 {
406
+ throw NetworkError.server(statusCode: response.statusCode)
407
+ }
408
+ }
409
+
410
+ let sample = "throw NSError(domain: code:)"
411
+ // throw NSError(domain: "Sample", code: 1)
412
+ `;
413
+
414
+ assert.equal(hasSwiftNSErrorThrowUsage(source), true);
415
+ assert.deepEqual(collectSwiftNSErrorThrowLines(source), [4]);
416
+ assert.equal(hasSwiftNSErrorThrowUsage(safe), false);
417
+ assert.deepEqual(collectSwiftNSErrorThrowLines(safe), []);
418
+ });
419
+
420
+ test('hasSwiftPackageBranchDependencyUsage detecta dependencias SwiftPM por branch y preserva versiones', () => {
421
+ const source = `
422
+ let package = Package(
423
+ dependencies: [
424
+ .package(url: "https://example.com/ui-kit.git", branch: "main"),
425
+ ]
426
+ )
427
+ `;
428
+ const safe = `
429
+ let package = Package(
430
+ dependencies: [
431
+ .package(url: "https://example.com/ui-kit.git", exact: "1.2.3"),
432
+ .package(url: "https://example.com/domain.git", from: "2.0.0"),
433
+ ]
434
+ )
435
+
436
+ let sample = ".package(url: branch:)"
437
+ // .package(url: "https://example.com/debug.git", branch: "develop")
438
+ `;
439
+
440
+ assert.equal(hasSwiftPackageBranchDependencyUsage(source), true);
441
+ assert.deepEqual(collectSwiftPackageBranchDependencyLines(source), [4]);
442
+ assert.equal(hasSwiftPackageBranchDependencyUsage(safe), false);
443
+ assert.deepEqual(collectSwiftPackageBranchDependencyLines(safe), []);
444
+ });
445
+
387
446
  test('hasSwiftNonLazyScrollForEachUsage detecta ScrollView con stack no lazy y preserva LazyVStack', () => {
388
447
  const source = `
389
448
  struct FeedView: View {
@@ -698,6 +698,31 @@ export const hasSwiftEmptyCatchUsage = (source: string): boolean => {
698
698
  return /\bcatch(?:\s+(?:let|var)\s+[A-Za-z_][A-Za-z0-9_]*)?\s*\{\s*\}/.test(sanitized);
699
699
  };
700
700
 
701
+ export const collectSwiftNSErrorThrowLines = (source: string): readonly number[] => {
702
+ return collectSwiftRegexLines(source, /\bthrow\s+NSError\s*\(/g);
703
+ };
704
+
705
+ export const hasSwiftNSErrorThrowUsage = (source: string): boolean => {
706
+ return collectSwiftNSErrorThrowLines(source).length > 0;
707
+ };
708
+
709
+ export const collectSwiftPackageBranchDependencyLines = (source: string): readonly number[] => {
710
+ const matches: number[] = [];
711
+ source.split(/\r?\n/).forEach((line, index) => {
712
+ const sanitized = line
713
+ .replace(/"(?:\\.|[^"\\])*"/g, '""')
714
+ .replace(/\/\/.*$/, '');
715
+ if (/\.\s*package\s*\([^)]*\bbranch\s*:/.test(sanitized)) {
716
+ matches.push(index + 1);
717
+ }
718
+ });
719
+ return matches;
720
+ };
721
+
722
+ export const hasSwiftPackageBranchDependencyUsage = (source: string): boolean => {
723
+ return collectSwiftPackageBranchDependencyLines(source).length > 0;
724
+ };
725
+
701
726
  export const hasSwiftOnAppearTaskUsage = (source: string): boolean => {
702
727
  return collectSwiftOnAppearTaskLines(source).length > 0;
703
728
  };