vimonkey 0.2.0 → 0.2.2
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/package.json +11 -10
- package/dist/chaos/index.d.ts +0 -81
- package/dist/chaos/index.d.ts.map +0 -1
- package/dist/chaos/index.js +0 -148
- package/dist/env.d.ts +0 -38
- package/dist/env.d.ts.map +0 -1
- package/dist/env.js +0 -52
- package/dist/fuzz/context.d.ts +0 -36
- package/dist/fuzz/context.d.ts.map +0 -1
- package/dist/fuzz/context.js +0 -43
- package/dist/fuzz/gen.d.ts +0 -70
- package/dist/fuzz/gen.d.ts.map +0 -1
- package/dist/fuzz/gen.js +0 -113
- package/dist/fuzz/index.d.ts +0 -57
- package/dist/fuzz/index.d.ts.map +0 -1
- package/dist/fuzz/index.js +0 -62
- package/dist/fuzz/regression.d.ts +0 -47
- package/dist/fuzz/regression.d.ts.map +0 -1
- package/dist/fuzz/regression.js +0 -91
- package/dist/fuzz/shrink.d.ts +0 -41
- package/dist/fuzz/shrink.d.ts.map +0 -1
- package/dist/fuzz/shrink.js +0 -80
- package/dist/fuzz/test-fuzz.d.ts +0 -55
- package/dist/fuzz/test-fuzz.d.ts.map +0 -1
- package/dist/fuzz/test-fuzz.js +0 -178
- package/dist/index.d.ts +0 -12
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -13
- package/dist/plugin.d.ts +0 -74
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -41
- package/dist/random.d.ts +0 -66
- package/dist/random.d.ts.map +0 -1
- package/dist/random.js +0 -137
package/dist/plugin.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @todo Planned vitest plugin — stub only. Implement config injection and
|
|
3
|
-
* custom CLI mode interception (vitest fuzz, vitest ai, vitest doc).
|
|
4
|
-
*
|
|
5
|
-
* Vitest plugin for vimonkey
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* // vitest.config.ts
|
|
10
|
-
* import { defineConfig } from 'vitest/config'
|
|
11
|
-
* import { viMonkey } from 'vimonkey/plugin'
|
|
12
|
-
*
|
|
13
|
-
* export default defineConfig({
|
|
14
|
-
* plugins: [
|
|
15
|
-
* viMonkey({
|
|
16
|
-
* fuzz: { iterations: 100 },
|
|
17
|
-
* ai: { model: 'claude-sonnet' },
|
|
18
|
-
* doc: { pattern: '**\/*.test.md' },
|
|
19
|
-
* })
|
|
20
|
-
* ]
|
|
21
|
-
* })
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
/** Minimal Vite plugin interface — avoids requiring vite as a dependency */
|
|
25
|
-
interface Plugin {
|
|
26
|
-
name: string;
|
|
27
|
-
config?(): void;
|
|
28
|
-
configureServer?(server: unknown): void;
|
|
29
|
-
}
|
|
30
|
-
export interface ViMonkeyFuzzOptions {
|
|
31
|
-
/** Number of actions per test run (default: 100) */
|
|
32
|
-
iterations?: number;
|
|
33
|
-
/** Seed source: 'env' reads FUZZ_SEED, 'random' generates new (default: 'env') */
|
|
34
|
-
seed?: "env" | "random";
|
|
35
|
-
/** Stop after first failure (default: true) */
|
|
36
|
-
failFast?: boolean;
|
|
37
|
-
/** Shrinking settings */
|
|
38
|
-
shrink?: {
|
|
39
|
-
enabled?: boolean;
|
|
40
|
-
maxAttempts?: number;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export interface ViMonkeyAiOptions {
|
|
44
|
-
/** Model identifier (Vercel AI SDK format) */
|
|
45
|
-
model?: string;
|
|
46
|
-
/** Temperature for LLM (0 = deterministic) */
|
|
47
|
-
temperature?: number;
|
|
48
|
-
/** Maximum actions per exploration */
|
|
49
|
-
maxSteps?: number;
|
|
50
|
-
/** Token budget per file */
|
|
51
|
-
maxTokens?: number;
|
|
52
|
-
/** Directory to save discovered tests */
|
|
53
|
-
saveDir?: string;
|
|
54
|
-
/** Use Claude Code provider */
|
|
55
|
-
provider?: "openai" | "anthropic" | "claude-code";
|
|
56
|
-
}
|
|
57
|
-
export interface ViMonkeyDocOptions {
|
|
58
|
-
/** Glob pattern for mdtest files */
|
|
59
|
-
pattern?: string;
|
|
60
|
-
}
|
|
61
|
-
export interface ViMonkeyOptions {
|
|
62
|
-
/** Fuzz mode configuration */
|
|
63
|
-
fuzz?: ViMonkeyFuzzOptions;
|
|
64
|
-
/** AI mode configuration */
|
|
65
|
-
ai?: ViMonkeyAiOptions;
|
|
66
|
-
/** Doc mode configuration (mdtest) */
|
|
67
|
-
doc?: ViMonkeyDocOptions;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Creates the vimonkey plugin for Vitest
|
|
71
|
-
*/
|
|
72
|
-
export declare function viMonkey(options?: ViMonkeyOptions): Plugin;
|
|
73
|
-
export default viMonkey;
|
|
74
|
-
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,4EAA4E;AAC5E,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,IAAI,IAAI,CAAA;IACf,eAAe,CAAC,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,yBAAyB;IACzB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAA;CAClD;AAED,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,IAAI,CAAC,EAAE,mBAAmB,CAAA;IAC1B,4BAA4B;IAC5B,EAAE,CAAC,EAAE,iBAAiB,CAAA;IACtB,sCAAsC;IACtC,GAAG,CAAC,EAAE,kBAAkB,CAAA;CACzB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAgB9D;AAED,eAAe,QAAQ,CAAA"}
|
package/dist/plugin.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @todo Planned vitest plugin — stub only. Implement config injection and
|
|
3
|
-
* custom CLI mode interception (vitest fuzz, vitest ai, vitest doc).
|
|
4
|
-
*
|
|
5
|
-
* Vitest plugin for vimonkey
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* // vitest.config.ts
|
|
10
|
-
* import { defineConfig } from 'vitest/config'
|
|
11
|
-
* import { viMonkey } from 'vimonkey/plugin'
|
|
12
|
-
*
|
|
13
|
-
* export default defineConfig({
|
|
14
|
-
* plugins: [
|
|
15
|
-
* viMonkey({
|
|
16
|
-
* fuzz: { iterations: 100 },
|
|
17
|
-
* ai: { model: 'claude-sonnet' },
|
|
18
|
-
* doc: { pattern: '**\/*.test.md' },
|
|
19
|
-
* })
|
|
20
|
-
* ]
|
|
21
|
-
* })
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* Creates the vimonkey plugin for Vitest
|
|
26
|
-
*/
|
|
27
|
-
export function viMonkey(options = {}) {
|
|
28
|
-
const { fuzz = {}, ai = {}, doc = {} } = options;
|
|
29
|
-
return {
|
|
30
|
-
name: "vimonkey",
|
|
31
|
-
config() {
|
|
32
|
-
// Configure vitest for custom modes
|
|
33
|
-
// Returns void for now - will be populated as modes are implemented
|
|
34
|
-
},
|
|
35
|
-
configureServer(server) {
|
|
36
|
-
// Handle custom CLI modes: vitest fuzz, vitest ai, vitest doc
|
|
37
|
-
// Implementation will intercept vitest CLI commands
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export default viMonkey;
|
package/dist/random.d.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Seeded random number generator
|
|
3
|
-
*
|
|
4
|
-
* Uses a Linear Congruential Generator (LCG) for reproducible random sequences.
|
|
5
|
-
* Ported from km's chaos testing infrastructure.
|
|
6
|
-
*/
|
|
7
|
-
export interface SeededRandom {
|
|
8
|
-
/** Get the current seed */
|
|
9
|
-
seed: number;
|
|
10
|
-
/** Generate a random integer in [min, max] inclusive */
|
|
11
|
-
int(min: number, max: number): number;
|
|
12
|
-
/** Generate a random float in [0, 1) */
|
|
13
|
-
float(): number;
|
|
14
|
-
/** Pick a random element from an array */
|
|
15
|
-
pick<T>(array: readonly T[]): T;
|
|
16
|
-
/** Pick a random element with weights */
|
|
17
|
-
weightedPick<T extends string>(items: readonly T[], weights: Partial<Record<T, number>>): T;
|
|
18
|
-
/** Shuffle an array (returns new array) */
|
|
19
|
-
shuffle<T>(array: readonly T[]): T[];
|
|
20
|
-
/** Generate a random array */
|
|
21
|
-
array<T>(length: number, generator: () => T): T[];
|
|
22
|
-
/** Generate a random boolean with given probability of true */
|
|
23
|
-
bool(probability?: number): boolean;
|
|
24
|
-
/** Fork the RNG (create independent stream) */
|
|
25
|
-
fork(): SeededRandom;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Pick a random element from weighted tuples using a pre-generated random float.
|
|
29
|
-
*
|
|
30
|
-
* @param tuples - Array of [weight, value] pairs
|
|
31
|
-
* @param rand - Random float in [0, 1)
|
|
32
|
-
* @returns The selected value
|
|
33
|
-
*/
|
|
34
|
-
export declare function weightedPickFromTuples<T>(tuples: readonly (readonly [number, T])[], rand: number): T;
|
|
35
|
-
/**
|
|
36
|
-
* Create a seeded random number generator
|
|
37
|
-
*
|
|
38
|
-
* @param seed - Initial seed (uses Date.now() if not provided)
|
|
39
|
-
* @returns Seeded random number generator
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* const random = createSeededRandom(12345)
|
|
44
|
-
*
|
|
45
|
-
* // Same seed = same sequence
|
|
46
|
-
* random.int(0, 100) // always 47 with seed 12345
|
|
47
|
-
* random.pick(['a', 'b', 'c']) // always 'b' with seed 12345
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
export declare function createSeededRandom(seed?: number): SeededRandom;
|
|
51
|
-
/**
|
|
52
|
-
* Parse seed from environment or generate random
|
|
53
|
-
*/
|
|
54
|
-
export declare function parseSeed(source?: "env" | "random"): number;
|
|
55
|
-
/**
|
|
56
|
-
* Parse FUZZ_REPEATS from environment (default: 1)
|
|
57
|
-
*
|
|
58
|
-
* Controls how many times each test.fuzz() test runs with different seeds.
|
|
59
|
-
* Use FUZZ_REPEATS=10000 for CI nightly runs.
|
|
60
|
-
*/
|
|
61
|
-
export declare function parseRepeats(): number;
|
|
62
|
-
/**
|
|
63
|
-
* Derive N unique seeds from a base seed using LCG
|
|
64
|
-
*/
|
|
65
|
-
export declare function deriveSeeds(baseSeed: number, count: number): number[];
|
|
66
|
-
//# sourceMappingURL=random.d.ts.map
|
package/dist/random.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../src/random.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IAEZ,wDAAwD;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IAErC,wCAAwC;IACxC,KAAK,IAAI,MAAM,CAAA;IAEf,0CAA0C;IAC1C,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAA;IAE/B,yCAAyC;IACzC,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAA;IAE3F,2CAA2C;IAC3C,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;IAEpC,8BAA8B;IAC9B,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAA;IAEjD,+DAA+D;IAC/D,IAAI,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAEnC,+CAA+C;IAC/C,IAAI,IAAI,YAAY,CAAA;CACrB;AAQD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CASpG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAyD9D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,GAAE,KAAK,GAAG,QAAgB,GAAG,MAAM,CAYlE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAUrC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE"}
|
package/dist/random.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Seeded random number generator
|
|
3
|
-
*
|
|
4
|
-
* Uses a Linear Congruential Generator (LCG) for reproducible random sequences.
|
|
5
|
-
* Ported from km's chaos testing infrastructure.
|
|
6
|
-
*/
|
|
7
|
-
// Linear Congruential Generator constants (same as glibc's rand()).
|
|
8
|
-
// Full period of 2^31. See Numerical Recipes §7.1 or Knuth TAOCP Vol 2 §3.2.1.
|
|
9
|
-
const LCG_A = 1103515245; // multiplier
|
|
10
|
-
const LCG_C = 12345; // increment
|
|
11
|
-
const LCG_M = 2 ** 31; // modulus (2^31)
|
|
12
|
-
/**
|
|
13
|
-
* Pick a random element from weighted tuples using a pre-generated random float.
|
|
14
|
-
*
|
|
15
|
-
* @param tuples - Array of [weight, value] pairs
|
|
16
|
-
* @param rand - Random float in [0, 1)
|
|
17
|
-
* @returns The selected value
|
|
18
|
-
*/
|
|
19
|
-
export function weightedPickFromTuples(tuples, rand) {
|
|
20
|
-
let total = 0;
|
|
21
|
-
for (const [w] of tuples)
|
|
22
|
-
total += w;
|
|
23
|
-
let r = rand * total;
|
|
24
|
-
for (const [weight, value] of tuples) {
|
|
25
|
-
r -= weight;
|
|
26
|
-
if (r <= 0)
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
return tuples[tuples.length - 1][1];
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Create a seeded random number generator
|
|
33
|
-
*
|
|
34
|
-
* @param seed - Initial seed (uses Date.now() if not provided)
|
|
35
|
-
* @returns Seeded random number generator
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* const random = createSeededRandom(12345)
|
|
40
|
-
*
|
|
41
|
-
* // Same seed = same sequence
|
|
42
|
-
* random.int(0, 100) // always 47 with seed 12345
|
|
43
|
-
* random.pick(['a', 'b', 'c']) // always 'b' with seed 12345
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export function createSeededRandom(seed) {
|
|
47
|
-
let state = seed ?? Date.now();
|
|
48
|
-
function next() {
|
|
49
|
-
state = (LCG_A * state + LCG_C) % LCG_M;
|
|
50
|
-
return state;
|
|
51
|
-
}
|
|
52
|
-
const random = {
|
|
53
|
-
get seed() {
|
|
54
|
-
return state;
|
|
55
|
-
},
|
|
56
|
-
float() {
|
|
57
|
-
return next() / LCG_M;
|
|
58
|
-
},
|
|
59
|
-
int(min, max) {
|
|
60
|
-
return Math.floor(random.float() * (max - min + 1)) + min;
|
|
61
|
-
},
|
|
62
|
-
pick(array) {
|
|
63
|
-
if (array.length === 0) {
|
|
64
|
-
throw new Error("Cannot pick from empty array");
|
|
65
|
-
}
|
|
66
|
-
return array[random.int(0, array.length - 1)];
|
|
67
|
-
},
|
|
68
|
-
weightedPick(items, weights) {
|
|
69
|
-
const tuples = items.map((item) => [weights[item] ?? 1, item]);
|
|
70
|
-
return weightedPickFromTuples(tuples, random.float());
|
|
71
|
-
},
|
|
72
|
-
shuffle(array) {
|
|
73
|
-
const result = [...array];
|
|
74
|
-
for (let i = result.length - 1; i > 0; i--) {
|
|
75
|
-
const j = random.int(0, i);
|
|
76
|
-
[result[i], result[j]] = [result[j], result[i]];
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
},
|
|
80
|
-
array(length, generator) {
|
|
81
|
-
return Array.from({ length }, generator);
|
|
82
|
-
},
|
|
83
|
-
bool(probability = 0.5) {
|
|
84
|
-
return random.float() < probability;
|
|
85
|
-
},
|
|
86
|
-
fork() {
|
|
87
|
-
// Use current state to seed new generator
|
|
88
|
-
return createSeededRandom(next());
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
return random;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Parse seed from environment or generate random
|
|
95
|
-
*/
|
|
96
|
-
export function parseSeed(source = "env") {
|
|
97
|
-
if (source === "env") {
|
|
98
|
-
const envSeed = process.env.FUZZ_SEED;
|
|
99
|
-
if (envSeed) {
|
|
100
|
-
const parsed = parseInt(envSeed, 10);
|
|
101
|
-
if (!isNaN(parsed)) {
|
|
102
|
-
return parsed;
|
|
103
|
-
}
|
|
104
|
-
console.warn(`Invalid FUZZ_SEED: "${envSeed}", using random seed`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return Date.now();
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Parse FUZZ_REPEATS from environment (default: 1)
|
|
111
|
-
*
|
|
112
|
-
* Controls how many times each test.fuzz() test runs with different seeds.
|
|
113
|
-
* Use FUZZ_REPEATS=10000 for CI nightly runs.
|
|
114
|
-
*/
|
|
115
|
-
export function parseRepeats() {
|
|
116
|
-
const env = process.env.FUZZ_REPEATS;
|
|
117
|
-
if (env) {
|
|
118
|
-
const parsed = parseInt(env, 10);
|
|
119
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
120
|
-
return parsed;
|
|
121
|
-
}
|
|
122
|
-
console.warn(`Invalid FUZZ_REPEATS: "${env}", using 1`);
|
|
123
|
-
}
|
|
124
|
-
return 1;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Derive N unique seeds from a base seed using LCG
|
|
128
|
-
*/
|
|
129
|
-
export function deriveSeeds(baseSeed, count) {
|
|
130
|
-
const seeds = [];
|
|
131
|
-
let state = baseSeed;
|
|
132
|
-
for (let i = 0; i < count; i++) {
|
|
133
|
-
state = (LCG_A * state + LCG_C) % LCG_M;
|
|
134
|
-
seeds.push(state);
|
|
135
|
-
}
|
|
136
|
-
return seeds;
|
|
137
|
-
}
|