@rigour-labs/core 2.22.0 → 3.0.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.
Files changed (103) hide show
  1. package/README.md +58 -0
  2. package/dist/context.test.js +2 -3
  3. package/dist/environment.test.js +2 -1
  4. package/dist/gates/agent-team.d.ts +2 -1
  5. package/dist/gates/agent-team.js +1 -0
  6. package/dist/gates/base.d.ts +3 -1
  7. package/dist/gates/base.js +3 -0
  8. package/dist/gates/checkpoint.d.ts +2 -1
  9. package/dist/gates/checkpoint.js +3 -2
  10. package/dist/gates/context-window-artifacts.d.ts +2 -1
  11. package/dist/gates/context-window-artifacts.js +6 -3
  12. package/dist/gates/context.d.ts +2 -1
  13. package/dist/gates/context.js +1 -0
  14. package/dist/gates/coverage.js +3 -1
  15. package/dist/gates/dependency.js +5 -5
  16. package/dist/gates/duplication-drift.d.ts +2 -1
  17. package/dist/gates/duplication-drift.js +4 -1
  18. package/dist/gates/environment.js +4 -4
  19. package/dist/gates/hallucinated-imports.d.ts +21 -2
  20. package/dist/gates/hallucinated-imports.js +116 -2
  21. package/dist/gates/inconsistent-error-handling.d.ts +2 -1
  22. package/dist/gates/inconsistent-error-handling.js +21 -7
  23. package/dist/gates/promise-safety.d.ts +68 -0
  24. package/dist/gates/promise-safety.js +509 -0
  25. package/dist/gates/retry-loop-breaker.d.ts +2 -1
  26. package/dist/gates/retry-loop-breaker.js +2 -1
  27. package/dist/gates/runner.js +34 -1
  28. package/dist/gates/safety.d.ts +2 -1
  29. package/dist/gates/safety.js +2 -1
  30. package/dist/gates/security-patterns.d.ts +2 -1
  31. package/dist/gates/security-patterns.js +1 -0
  32. package/dist/gates/structure.js +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/services/fix-packet-service.d.ts +0 -1
  36. package/dist/services/fix-packet-service.js +9 -14
  37. package/dist/services/score-history.d.ts +54 -0
  38. package/dist/services/score-history.js +122 -0
  39. package/dist/templates/index.js +169 -0
  40. package/dist/types/fix-packet.d.ts +5 -5
  41. package/dist/types/fix-packet.js +1 -1
  42. package/dist/types/index.d.ts +153 -0
  43. package/dist/types/index.js +19 -0
  44. package/package.json +21 -1
  45. package/src/context.test.ts +0 -256
  46. package/src/discovery.test.ts +0 -88
  47. package/src/discovery.ts +0 -112
  48. package/src/environment.test.ts +0 -115
  49. package/src/gates/agent-team.test.ts +0 -134
  50. package/src/gates/agent-team.ts +0 -210
  51. package/src/gates/ast-handlers/base.ts +0 -13
  52. package/src/gates/ast-handlers/python.ts +0 -145
  53. package/src/gates/ast-handlers/python_parser.py +0 -181
  54. package/src/gates/ast-handlers/typescript.ts +0 -264
  55. package/src/gates/ast-handlers/universal.ts +0 -184
  56. package/src/gates/ast.ts +0 -54
  57. package/src/gates/base.ts +0 -28
  58. package/src/gates/checkpoint.test.ts +0 -135
  59. package/src/gates/checkpoint.ts +0 -311
  60. package/src/gates/content.ts +0 -51
  61. package/src/gates/context-window-artifacts.ts +0 -277
  62. package/src/gates/context.ts +0 -270
  63. package/src/gates/coverage.ts +0 -74
  64. package/src/gates/dependency.ts +0 -108
  65. package/src/gates/duplication-drift.ts +0 -231
  66. package/src/gates/environment.ts +0 -94
  67. package/src/gates/file.ts +0 -46
  68. package/src/gates/hallucinated-imports.ts +0 -361
  69. package/src/gates/inconsistent-error-handling.ts +0 -254
  70. package/src/gates/retry-loop-breaker.ts +0 -151
  71. package/src/gates/runner.ts +0 -188
  72. package/src/gates/safety.ts +0 -56
  73. package/src/gates/security-patterns.test.ts +0 -162
  74. package/src/gates/security-patterns.ts +0 -306
  75. package/src/gates/structure.ts +0 -36
  76. package/src/index.ts +0 -13
  77. package/src/pattern-index/embeddings.ts +0 -84
  78. package/src/pattern-index/index.ts +0 -59
  79. package/src/pattern-index/indexer.test.ts +0 -276
  80. package/src/pattern-index/indexer.ts +0 -1023
  81. package/src/pattern-index/matcher.test.ts +0 -293
  82. package/src/pattern-index/matcher.ts +0 -493
  83. package/src/pattern-index/overrides.ts +0 -235
  84. package/src/pattern-index/security.ts +0 -151
  85. package/src/pattern-index/staleness.test.ts +0 -313
  86. package/src/pattern-index/staleness.ts +0 -568
  87. package/src/pattern-index/types.ts +0 -339
  88. package/src/safety.test.ts +0 -53
  89. package/src/services/adaptive-thresholds.test.ts +0 -189
  90. package/src/services/adaptive-thresholds.ts +0 -275
  91. package/src/services/context-engine.ts +0 -104
  92. package/src/services/fix-packet-service.ts +0 -42
  93. package/src/services/state-service.ts +0 -138
  94. package/src/smoke.test.ts +0 -18
  95. package/src/templates/index.ts +0 -338
  96. package/src/types/fix-packet.ts +0 -32
  97. package/src/types/index.ts +0 -200
  98. package/src/utils/logger.ts +0 -43
  99. package/src/utils/scanner.test.ts +0 -37
  100. package/src/utils/scanner.ts +0 -43
  101. package/tsconfig.json +0 -10
  102. package/vitest.config.ts +0 -7
  103. package/vitest.setup.ts +0 -30
@@ -1,339 +0,0 @@
1
- /**
2
- * Pattern Index - Types
3
- *
4
- * Core type definitions for the Pattern Index system.
5
- * Rigour's Pattern Index prevents AI from reinventing existing code.
6
- */
7
-
8
- /**
9
- * All supported pattern types that can be indexed.
10
- * Organized by category for clarity.
11
- */
12
- export type PatternType =
13
- // === CODE PATTERNS ===
14
- | 'function' // Standalone functions, utilities
15
- | 'class' // Classes
16
- | 'method' // Class methods (indexed separately for reuse)
17
- | 'component' // React/Vue/Svelte components
18
- | 'hook' // React hooks (useX)
19
- | 'decorator' // Python/TS decorators
20
- | 'middleware' // Express/FastAPI middleware
21
-
22
- // === DATA PATTERNS ===
23
- | 'type' // TypeScript types
24
- | 'interface' // TypeScript interfaces
25
- | 'schema' // Zod, Yup, JSON Schema validators
26
- | 'model' // Database models (Prisma, SQLAlchemy, etc.)
27
- | 'enum' // Enumerations
28
-
29
- // === CONFIGURATION ===
30
- | 'constant' // Constants, magic values
31
- | 'config' // Configuration objects
32
- | 'env' // Environment variable patterns
33
-
34
- // === API PATTERNS ===
35
- | 'route' // API routes/endpoints
36
- | 'handler' // Route handlers
37
- | 'resolver' // GraphQL resolvers
38
- | 'rpc' // tRPC procedures
39
-
40
- // === STATE PATTERNS ===
41
- | 'store' // State stores (Zustand, Redux)
42
- | 'reducer' // Redux reducers
43
- | 'action' // Actions/mutations
44
- | 'selector' // State selectors
45
-
46
- // === ERROR HANDLING ===
47
- | 'error' // Custom error classes
48
- | 'exception' // Exception types
49
-
50
- // === TESTING ===
51
- | 'mock' // Mock objects/functions
52
- | 'fixture' // Test fixtures
53
- | 'factory' // Test factories
54
-
55
- // === INFRASTRUCTURE ===
56
- | 'command' // CLI commands
57
- | 'task' // Background tasks/jobs
58
- | 'event' // Event types/handlers
59
- | 'protocol'; // Python protocols, abstract classes
60
-
61
- /**
62
- * A single indexed pattern entry.
63
- */
64
- export interface PatternEntry {
65
- /** Unique identifier (hash of file + name) */
66
- id: string;
67
-
68
- /** The type of pattern */
69
- type: PatternType;
70
-
71
- /** Name of the pattern (e.g., "formatDate") */
72
- name: string;
73
-
74
- /** Relative path to the file */
75
- file: string;
76
-
77
- /** Line number where the pattern is defined */
78
- line: number;
79
-
80
- /** End line number */
81
- endLine: number;
82
-
83
- /** Function/method signature if applicable */
84
- signature: string;
85
-
86
- /** Description from JSDoc/docstring */
87
- description: string;
88
-
89
- /** Extracted semantic keywords for matching */
90
- keywords: string[];
91
-
92
- /** Content hash for change detection */
93
- hash: string;
94
-
95
- /** Is this pattern exported? */
96
- exported: boolean;
97
-
98
- /** How many files import this pattern */
99
- usageCount: number; // How many files import this?
100
-
101
- /** User-defined category/grouping */
102
- category?: string; // User-defined grouping
103
- embedding?: number[]; // Vector embedding for semantic search
104
-
105
- /** Last indexed timestamp */
106
- indexedAt: string;
107
- }
108
-
109
- /**
110
- * The complete pattern index structure.
111
- */
112
- export interface PatternIndex {
113
- /** Index format version */
114
- version: string;
115
-
116
- /** When the index was last updated */
117
- lastUpdated: string;
118
-
119
- /** Root directory that was indexed */
120
- rootDir: string;
121
-
122
- /** All indexed patterns */
123
- patterns: PatternEntry[];
124
-
125
- /** Index statistics */
126
- stats: PatternIndexStats;
127
-
128
- /** Files that were indexed */
129
- files: IndexedFile[];
130
- }
131
-
132
- /**
133
- * Statistics about the pattern index.
134
- */
135
- export interface PatternIndexStats {
136
- totalPatterns: number;
137
- totalFiles: number;
138
- byType: Record<PatternType, number>;
139
- indexDurationMs: number;
140
- }
141
-
142
- /**
143
- * Information about an indexed file.
144
- */
145
- export interface IndexedFile {
146
- path: string;
147
- hash: string;
148
- patternCount: number;
149
- indexedAt: string;
150
- }
151
-
152
- /**
153
- * Configuration for the pattern indexer.
154
- */
155
- export interface PatternIndexConfig {
156
- /** Directories to index (defaults to src/) */
157
- include: string[];
158
-
159
- /** Directories to exclude */
160
- exclude: string[];
161
-
162
- /** File extensions to index */
163
- extensions: string[];
164
-
165
- /** Whether to index test files */
166
- indexTests: boolean;
167
-
168
- /** Whether to index node_modules */
169
- indexNodeModules: boolean;
170
-
171
- /** Minimum pattern name length to index */
172
- minNameLength: number;
173
-
174
- /** Custom categories for patterns */
175
- categories: Record<string, string[]>;
176
-
177
- /** Whether to generate semantic embeddings for patterns */
178
- useEmbeddings?: boolean;
179
- }
180
-
181
- /**
182
- * Result from matching against the pattern index.
183
- */
184
- export interface PatternMatchResult {
185
- /** The query that was matched */
186
- query: string;
187
-
188
- /** All matches found */
189
- matches: PatternMatch[];
190
-
191
- /** Suggestion for what to do */
192
- suggestion: string;
193
-
194
- /** Whether human override is available */
195
- canOverride: boolean;
196
-
197
- /** Overall status */
198
- status: 'FOUND_SIMILAR' | 'NO_MATCH' | 'OVERRIDE_ALLOWED';
199
-
200
- /** Recommended action */
201
- action: 'BLOCK' | 'WARN' | 'ALLOW';
202
- }
203
-
204
- /**
205
- * A single pattern match.
206
- */
207
- export interface PatternMatch {
208
- /** The matched pattern */
209
- pattern: PatternEntry;
210
-
211
- /** How the match was determined */
212
- matchType: 'exact' | 'fuzzy' | 'signature' | 'semantic';
213
-
214
- /** Confidence score 0-100 */
215
- confidence: number;
216
-
217
- /** Human-readable reason for the match */
218
- reason: string;
219
- }
220
-
221
- /**
222
- * Human override entry.
223
- */
224
- export interface PatternOverride {
225
- /** Pattern name or glob */
226
- pattern: string;
227
-
228
- /** Why the override was granted */
229
- reason: string;
230
-
231
- /** When the override expires */
232
- expiresAt?: string;
233
-
234
- /** Who approved the override */
235
- approvedBy?: string;
236
-
237
- /** When the override was created */
238
- createdAt: string;
239
- }
240
-
241
- /**
242
- * Staleness detection result.
243
- */
244
- export interface StalenessResult {
245
- /** Overall status */
246
- status: 'FRESH' | 'STALE' | 'DEPRECATED';
247
-
248
- /** All staleness issues found */
249
- issues: StalenessIssue[];
250
-
251
- /** Project context (versions) */
252
- projectContext: Record<string, string>;
253
- }
254
-
255
- /**
256
- * A single staleness issue.
257
- */
258
- export interface StalenessIssue {
259
- /** Line number */
260
- line: number;
261
-
262
- /** The stale pattern */
263
- pattern: string;
264
-
265
- /** Severity */
266
- severity: 'error' | 'warning' | 'info';
267
-
268
- /** Why it's stale */
269
- reason: string;
270
-
271
- /** What to use instead */
272
- replacement: string;
273
-
274
- /** Link to documentation */
275
- docs?: string;
276
- }
277
-
278
- /**
279
- * Security/CVE entry for a package.
280
- */
281
- export interface SecurityEntry {
282
- /** CVE Identifier (e.g., CVE-2021-1234) */
283
- cveId: string;
284
-
285
- /** Package name */
286
- packageName: string;
287
-
288
- /** Vulnerable version range (semver) */
289
- vulnerableRange: string;
290
-
291
- /** Severity level */
292
- severity: 'critical' | 'high' | 'moderate' | 'low';
293
-
294
- /** Brief description of the vulnerability */
295
- title: string;
296
-
297
- /** Link to advisory */
298
- url: string;
299
-
300
- /** The version of the package in the current project */
301
- currentVersion?: string;
302
- }
303
-
304
- /**
305
- * Result from security check.
306
- */
307
- export interface SecurityResult {
308
- /** Overall security status */
309
- status: 'SECURE' | 'VULNERABLE';
310
-
311
- /** All CVEs/vulnerabilities found */
312
- vulnerabilities: SecurityEntry[];
313
- }
314
-
315
- /**
316
- * Deprecation entry in the deprecation database.
317
- */
318
- export interface DeprecationEntry {
319
- /** Pattern to match (can be regex) */
320
- pattern: string;
321
-
322
- /** Library this belongs to */
323
- library?: string;
324
-
325
- /** Version when deprecated */
326
- deprecatedIn: string;
327
-
328
- /** Suggested replacement */
329
- replacement: string;
330
-
331
- /** Severity level */
332
- severity: 'error' | 'warning' | 'info';
333
-
334
- /** Additional context */
335
- reason?: string;
336
-
337
- /** Documentation link */
338
- docs?: string;
339
- }
@@ -1,53 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { FileGuardGate } from './gates/safety.js';
3
- import { Gates } from './types/index.js';
4
- import { execa } from 'execa';
5
-
6
- vi.mock('execa');
7
-
8
- describe('FileGuardGate', () => {
9
- const config: Gates = {
10
- safety: {
11
- protected_paths: ['docs/'],
12
- max_files_changed_per_cycle: 10
13
- }
14
- } as any;
15
-
16
- it('should flag modified (M) protected files', async () => {
17
- const gate = new FileGuardGate(config);
18
- vi.mocked(execa).mockResolvedValueOnce({ stdout: ' M docs/SPEC.md\n' } as any);
19
-
20
- const failures = await gate.run({ cwd: '/test', record: {} as any });
21
- expect(failures).toHaveLength(1);
22
- expect(failures[0].title).toContain("Protected file 'docs/SPEC.md' was modified.");
23
- });
24
-
25
- it('should flag added (A) protected files', async () => {
26
- const gate = new FileGuardGate(config);
27
- vi.mocked(execa).mockResolvedValueOnce({ stdout: 'A docs/NEW.md\n' } as any);
28
-
29
- const failures = await gate.run({ cwd: '/test', record: {} as any });
30
- expect(failures).toHaveLength(1);
31
- expect(failures[0].title).toContain("Protected file 'docs/NEW.md' was modified.");
32
- });
33
-
34
- it('should NOT flag untracked (??) protected files', async () => {
35
- const gate = new FileGuardGate(config);
36
- vi.mocked(execa).mockResolvedValueOnce({ stdout: '?? docs/UNTRAKED.md\n' } as any);
37
-
38
- const failures = await gate.run({ cwd: '/test', record: {} as any });
39
- expect(failures).toHaveLength(0);
40
- });
41
-
42
- it('should correctly handle multiple mixed statuses', async () => {
43
- const gate = new FileGuardGate(config);
44
- vi.mocked(execa).mockResolvedValueOnce({
45
- stdout: ' M docs/MODIFIED.md\n?? docs/NEW_UNTRACKED.md\n D docs/DELETED.md\n'
46
- } as any);
47
-
48
- const failures = await gate.run({ cwd: '/test', record: {} as any });
49
- expect(failures).toHaveLength(2);
50
- expect(failures.map(f => f.title)).toContain("Protected file 'docs/MODIFIED.md' was modified.");
51
- expect(failures.map(f => f.title)).toContain("Protected file 'docs/DELETED.md' was modified.");
52
- });
53
- });
@@ -1,189 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import {
3
- detectComplexityTier,
4
- calculateAdaptiveThresholds,
5
- recordGateRun,
6
- getQualityTrend,
7
- clearAdaptiveHistory,
8
- getAdaptiveSummary,
9
- } from './adaptive-thresholds.js';
10
- import * as fs from 'fs';
11
- import * as path from 'path';
12
- import * as os from 'os';
13
-
14
- describe('AdaptiveThresholds', () => {
15
- let testDir: string;
16
-
17
- beforeEach(() => {
18
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'adaptive-test-'));
19
- });
20
-
21
- afterEach(() => {
22
- clearAdaptiveHistory(testDir);
23
- fs.rmSync(testDir, { recursive: true, force: true });
24
- });
25
-
26
- describe('detectComplexityTier', () => {
27
- it('should detect hobby tier for small projects', () => {
28
- const tier = detectComplexityTier({ fileCount: 20 });
29
- expect(tier).toBe('hobby');
30
- });
31
-
32
- it('should detect startup tier for medium projects', () => {
33
- const tier = detectComplexityTier({ fileCount: 100 });
34
- expect(tier).toBe('startup');
35
- });
36
-
37
- it('should detect enterprise tier for large projects', () => {
38
- const tier = detectComplexityTier({ fileCount: 600 });
39
- expect(tier).toBe('enterprise');
40
- });
41
-
42
- it('should consider commit count for tier detection', () => {
43
- const tier = detectComplexityTier({ fileCount: 50, commitCount: 1500 });
44
- expect(tier).toBe('enterprise');
45
- });
46
- });
47
-
48
- describe('calculateAdaptiveThresholds', () => {
49
- it('should return lenient thresholds for hobby tier', () => {
50
- const adjustments = calculateAdaptiveThresholds(
51
- testDir,
52
- { fileCount: 20 }
53
- );
54
-
55
- expect(adjustments.tier).toBe('hobby');
56
- expect(adjustments.coverageThreshold).toBeLessThan(80);
57
- expect(adjustments.securityBlockLevel).toBe('critical');
58
- expect(adjustments.leniencyFactor).toBeGreaterThan(0.5);
59
- });
60
-
61
- it('should return strict thresholds for enterprise tier', () => {
62
- const adjustments = calculateAdaptiveThresholds(
63
- testDir,
64
- { fileCount: 600 }
65
- );
66
-
67
- expect(adjustments.tier).toBe('enterprise');
68
- expect(adjustments.coverageThreshold).toBe(80);
69
- expect(adjustments.securityBlockLevel).toBe('medium');
70
- expect(adjustments.leniencyFactor).toBeLessThan(0.5);
71
- });
72
-
73
- it('should respect forced_tier config', () => {
74
- const adjustments = calculateAdaptiveThresholds(
75
- testDir,
76
- { fileCount: 20 },
77
- { forced_tier: 'enterprise' }
78
- );
79
-
80
- expect(adjustments.tier).toBe('enterprise');
81
- });
82
-
83
- it('should include reasoning for adjustments', () => {
84
- const adjustments = calculateAdaptiveThresholds(
85
- testDir,
86
- { fileCount: 100 }
87
- );
88
-
89
- expect(adjustments.reasoning.length).toBeGreaterThan(0);
90
- expect(adjustments.reasoning.some(r => r.includes('tier'))).toBe(true);
91
- });
92
- });
93
-
94
- describe('historical tracking', () => {
95
- it('should record gate runs', () => {
96
- recordGateRun(testDir, 5, 2, 10);
97
- recordGateRun(testDir, 6, 1, 5);
98
-
99
- const historyPath = path.join(testDir, '.rigour', 'adaptive-history.json');
100
- expect(fs.existsSync(historyPath)).toBe(true);
101
-
102
- const history = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
103
- expect(history.runs).toHaveLength(2);
104
- });
105
-
106
- it('should return stable trend for new projects', () => {
107
- const trend = getQualityTrend(testDir);
108
- expect(trend).toBe('stable');
109
- });
110
-
111
- it('should detect improving trend', () => {
112
- // Record 20 runs: older ones with high failures, recent with low
113
- for (let i = 0; i < 10; i++) {
114
- recordGateRun(testDir, 3, 5, 20);
115
- }
116
- for (let i = 0; i < 10; i++) {
117
- recordGateRun(testDir, 7, 1, 5);
118
- }
119
-
120
- const trend = getQualityTrend(testDir);
121
- expect(trend).toBe('improving');
122
- });
123
-
124
- it('should detect degrading trend', () => {
125
- // Record 20 runs: older ones with low failures, recent with high
126
- for (let i = 0; i < 10; i++) {
127
- recordGateRun(testDir, 7, 1, 3);
128
- }
129
- for (let i = 0; i < 10; i++) {
130
- recordGateRun(testDir, 3, 5, 20);
131
- }
132
-
133
- const trend = getQualityTrend(testDir);
134
- expect(trend).toBe('degrading');
135
- });
136
- });
137
-
138
- describe('trend-based adjustments', () => {
139
- it('should relax thresholds for improving trend', () => {
140
- // Create improving history
141
- for (let i = 0; i < 10; i++) {
142
- recordGateRun(testDir, 3, 5, 20);
143
- }
144
- for (let i = 0; i < 10; i++) {
145
- recordGateRun(testDir, 7, 1, 5);
146
- }
147
-
148
- const adjustments = calculateAdaptiveThresholds(
149
- testDir,
150
- { fileCount: 100 }
151
- );
152
-
153
- expect(adjustments.trend).toBe('improving');
154
- expect(adjustments.reasoning.some(r => r.includes('bonus'))).toBe(true);
155
- });
156
-
157
- it('should tighten thresholds for degrading trend', () => {
158
- // Create degrading history
159
- for (let i = 0; i < 10; i++) {
160
- recordGateRun(testDir, 7, 1, 3);
161
- }
162
- for (let i = 0; i < 10; i++) {
163
- recordGateRun(testDir, 3, 5, 20);
164
- }
165
-
166
- const adjustments = calculateAdaptiveThresholds(
167
- testDir,
168
- { fileCount: 100 }
169
- );
170
-
171
- expect(adjustments.trend).toBe('degrading');
172
- expect(adjustments.reasoning.some(r => r.includes('tightened'))).toBe(true);
173
- });
174
- });
175
-
176
- describe('getAdaptiveSummary', () => {
177
- it('should return formatted summary string', () => {
178
- const adjustments = calculateAdaptiveThresholds(
179
- testDir,
180
- { fileCount: 100 }
181
- );
182
-
183
- const summary = getAdaptiveSummary(adjustments);
184
- expect(summary).toContain('STARTUP');
185
- expect(summary).toContain('Coverage:');
186
- expect(summary).toContain('Quality:');
187
- });
188
- });
189
- });